omegafox/patches/upstream-dns-leak-fix.patch
2024-09-16 00:49:28 -05:00

875 lines
31 KiB
Diff

# HG changeset patch
# User Kershaw Chang <kershaw@mozilla.com>
# Date 1725998669 0
# Node ID eb748cbf195dc2195a5a26910efb0f35a0967e03
# Parent f2a1d0b442ab14b7d00c4229444d4845d14cbb68
Bug 1910593 - Don't prefetch HTTPS RR if proxyDNS is enabled, r=necko-reviewers,valentin
Differential Revision: https://phabricator.services.mozilla.com/D219528
diff --git a/dom/chrome-webidl/NetDashboard.webidl b/dom/chrome-webidl/NetDashboard.webidl
--- a/dom/chrome-webidl/NetDashboard.webidl
+++ b/dom/chrome-webidl/NetDashboard.webidl
@@ -63,16 +63,17 @@ dictionary WebSocketDict {
dictionary DnsCacheEntry {
DOMString hostname = "";
sequence<DOMString> hostaddr;
DOMString family = "";
double expiration = 0;
boolean trr = false;
DOMString originAttributesSuffix = "";
DOMString flags = "";
+ unsigned short type = 0;
};
[GenerateConversionToJS]
dictionary DNSCacheDict {
sequence<DnsCacheEntry> entries;
};
[GenerateConversionToJS]
diff --git a/netwerk/base/Dashboard.cpp b/netwerk/base/Dashboard.cpp
--- a/netwerk/base/Dashboard.cpp
+++ b/netwerk/base/Dashboard.cpp
@@ -907,20 +907,23 @@ nsresult Dashboard::GetDNSCacheEntries(D
nsString* addr = addrs.AppendElement(fallible);
if (!addr) {
JS_ReportOutOfMemory(cx);
return NS_ERROR_OUT_OF_MEMORY;
}
CopyASCIItoUTF16(dnsData->mData[i].hostaddr[j], *addr);
}
- if (dnsData->mData[i].family == PR_AF_INET6) {
- entry.mFamily.AssignLiteral(u"ipv6");
- } else {
- entry.mFamily.AssignLiteral(u"ipv4");
+ entry.mType = dnsData->mData[i].resolveType;
+ if (entry.mType == nsIDNSService::RESOLVE_TYPE_DEFAULT) {
+ if (dnsData->mData[i].family == PR_AF_INET6) {
+ entry.mFamily.AssignLiteral(u"ipv6");
+ } else {
+ entry.mFamily.AssignLiteral(u"ipv4");
+ }
}
entry.mOriginAttributesSuffix =
NS_ConvertUTF8toUTF16(dnsData->mData[i].originAttributesSuffix);
entry.mFlags = NS_ConvertUTF8toUTF16(dnsData->mData[i].flags);
}
JS::Rooted<JS::Value> val(cx);
diff --git a/netwerk/base/DashboardTypes.h b/netwerk/base/DashboardTypes.h
--- a/netwerk/base/DashboardTypes.h
+++ b/netwerk/base/DashboardTypes.h
@@ -30,22 +30,22 @@ inline bool operator==(const SocketInfo&
struct DnsAndConnectSockets {
bool speculative;
};
struct DNSCacheEntries {
nsCString hostname;
nsTArray<nsCString> hostaddr;
- uint16_t family;
- int64_t expiration;
- nsCString netInterface;
- bool TRR;
+ uint16_t family{0};
+ int64_t expiration{0};
+ bool TRR{false};
nsCString originAttributesSuffix;
nsCString flags;
+ uint16_t resolveType{0};
};
struct HttpConnInfo {
uint32_t ttl;
uint32_t rtt;
nsString protocolVersion;
void SetHTTPProtocolVersion(HttpVersion pv);
@@ -94,27 +94,31 @@ template <>
struct ParamTraits<mozilla::net::DNSCacheEntries> {
typedef mozilla::net::DNSCacheEntries paramType;
static void Write(MessageWriter* aWriter, const paramType& aParam) {
WriteParam(aWriter, aParam.hostname);
WriteParam(aWriter, aParam.hostaddr);
WriteParam(aWriter, aParam.family);
WriteParam(aWriter, aParam.expiration);
- WriteParam(aWriter, aParam.netInterface);
WriteParam(aWriter, aParam.TRR);
+ WriteParam(aWriter, aParam.originAttributesSuffix);
+ WriteParam(aWriter, aParam.flags);
+ WriteParam(aWriter, aParam.resolveType);
}
static bool Read(MessageReader* aReader, paramType* aResult) {
return ReadParam(aReader, &aResult->hostname) &&
ReadParam(aReader, &aResult->hostaddr) &&
ReadParam(aReader, &aResult->family) &&
ReadParam(aReader, &aResult->expiration) &&
- ReadParam(aReader, &aResult->netInterface) &&
- ReadParam(aReader, &aResult->TRR);
+ ReadParam(aReader, &aResult->TRR) &&
+ ReadParam(aReader, &aResult->originAttributesSuffix) &&
+ ReadParam(aReader, &aResult->flags) &&
+ ReadParam(aReader, &aResult->resolveType);
}
};
template <>
struct ParamTraits<mozilla::net::DnsAndConnectSockets> {
typedef mozilla::net::DnsAndConnectSockets paramType;
static void Write(MessageWriter* aWriter, const paramType& aParam) {
diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -2030,50 +2030,44 @@ void nsHostResolver::GetDNSCacheEntries(
// Also require a host.
nsHostRecord* rec = recordEntry.GetWeak();
MOZ_ASSERT(rec, "rec should never be null here!");
if (!rec) {
continue;
}
- // For now we only show A/AAAA records.
- if (!rec->IsAddrRecord()) {
+ DNSCacheEntries info;
+ info.resolveType = rec->type;
+ info.hostname = rec->host;
+ info.family = rec->af;
+ if (rec->mValidEnd.IsNull()) {
continue;
}
-
- RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
- MOZ_ASSERT(addrRec);
- if (!addrRec || !addrRec->addr_info) {
- continue;
- }
-
- DNSCacheEntries info;
- info.hostname = rec->host;
- info.family = rec->af;
info.expiration =
(int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds();
if (info.expiration <= 0) {
// We only need valid DNS cache entries
continue;
}
- {
+ info.originAttributesSuffix = recordEntry.GetKey().originSuffix;
+ info.flags = nsPrintfCString("%u|0x%x|%u|%d|%s", rec->type, rec->flags,
+ rec->af, rec->pb, rec->mTrrServer.get());
+
+ RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
+ if (addrRec && addrRec->addr_info) {
MutexAutoLock lock(addrRec->addr_info_lock);
for (const auto& addr : addrRec->addr_info->Addresses()) {
char buf[kIPv6CStrBufSize];
if (addr.ToStringBuffer(buf, sizeof(buf))) {
info.hostaddr.AppendElement(buf);
}
}
info.TRR = addrRec->addr_info->IsTRR();
}
- info.originAttributesSuffix = recordEntry.GetKey().originSuffix;
- info.flags = nsPrintfCString("%u|0x%x|%u|%d|%s", rec->type, rec->flags,
- rec->af, rec->pb, rec->mTrrServer.get());
-
args->AppendElement(std::move(info));
}
}
#undef LOG
#undef LOG_ENABLED
diff --git a/netwerk/protocol/http/nsHttp.cpp b/netwerk/protocol/http/nsHttp.cpp
--- a/netwerk/protocol/http/nsHttp.cpp
+++ b/netwerk/protocol/http/nsHttp.cpp
@@ -30,16 +30,18 @@
#include <errno.h>
#include <functional>
#include "nsLiteralString.h"
#include <string.h>
namespace mozilla {
namespace net {
+extern const char kProxyType_SOCKS[];
+
const uint32_t kHttp3VersionCount = 5;
const nsCString kHttp3Versions[] = {"h3-29"_ns, "h3-30"_ns, "h3-31"_ns,
"h3-32"_ns, "h3"_ns};
// https://datatracker.ietf.org/doc/html/draft-ietf-webtrans-http3/#section-4.3
constexpr uint64_t kWebTransportErrorCodeStart = 0x52e4a40fa8db;
constexpr uint64_t kWebTransportErrorCodeEnd = 0x52e4a40fa9e2;
@@ -1173,10 +1175,24 @@ nsLiteralCString HttpVersionToTelemetryL
case HttpVersion::v3_0:
return "http_3"_ns;
default:
break;
}
return "unknown"_ns;
}
+ProxyDNSStrategy GetProxyDNSStrategyHelper(const char* aType, uint32_t aFlag) {
+ if (!aType) {
+ return ProxyDNSStrategy::ORIGIN;
+ }
+
+ if (!(aFlag & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST)) {
+ if (aType == kProxyType_SOCKS) {
+ return ProxyDNSStrategy::ORIGIN;
+ }
+ }
+
+ return ProxyDNSStrategy::PROXY;
+}
+
} // namespace net
} // namespace mozilla
diff --git a/netwerk/protocol/http/nsHttp.h b/netwerk/protocol/http/nsHttp.h
--- a/netwerk/protocol/http/nsHttp.h
+++ b/netwerk/protocol/http/nsHttp.h
@@ -524,12 +524,22 @@ uint64_t WebTransportErrorToHttp3Error(u
uint8_t Http3ErrorToWebTransportError(uint64_t aErrorCode);
bool PossibleZeroRTTRetryError(nsresult aReason);
void DisallowHTTPSRR(uint32_t& aCaps);
nsLiteralCString HttpVersionToTelemetryLabel(HttpVersion version);
+enum class ProxyDNSStrategy : uint8_t {
+ // To resolve the origin of the end server we are connecting
+ // to.
+ ORIGIN = 1 << 0,
+ // To resolve the host name of the proxy.
+ PROXY = 1 << 1
+};
+
+ProxyDNSStrategy GetProxyDNSStrategyHelper(const char* aType, uint32_t aFlag);
+
} // namespace net
} // namespace mozilla
#endif // nsHttp_h__
diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -773,16 +773,20 @@ nsresult nsHttpChannel::MaybeUseHTTPSRRF
return aStatus;
}
if (mURI->SchemeIs("https") || aShouldUpgrade || !LoadUseHTTPSSVC()) {
return ContinueOnBeforeConnect(aShouldUpgrade, aStatus);
}
auto shouldSkipUpgradeWithHTTPSRR = [&]() -> bool {
+ if (mCaps & NS_HTTP_DISALLOW_HTTPS_RR) {
+ return true;
+ }
+
// Skip using HTTPS RR to upgrade when this is not a top-level load and the
// loading principal is http.
if ((mLoadInfo->GetExternalContentPolicyType() !=
ExtContentPolicy::TYPE_DOCUMENT) &&
(mLoadInfo->GetLoadingPrincipal() &&
mLoadInfo->GetLoadingPrincipal()->SchemeIs("http"))) {
return true;
}
@@ -795,16 +799,21 @@ nsresult nsHttpChannel::MaybeUseHTTPSRRF
return true;
}
// Don't block the channel when TRR is not used.
if (!trrEnabled) {
return true;
}
+ auto dnsStrategy = GetProxyDNSStrategy();
+ if (dnsStrategy != ProxyDNSStrategy::ORIGIN) {
+ return true;
+ }
+
nsAutoCString uriHost;
mURI->GetAsciiHost(uriHost);
return gHttpHandler->IsHostExcludedForHTTPSRR(uriHost);
};
if (shouldSkipUpgradeWithHTTPSRR()) {
StoreUseHTTPSSVC(false);
@@ -819,21 +828,16 @@ nsresult nsHttpChannel::MaybeUseHTTPSRRF
LOG((
"nsHttpChannel::MaybeUseHTTPSRRForUpgrade [%p] mHTTPSSVCRecord is some",
this));
StoreWaitHTTPSSVCRecord(false);
bool hasHTTPSRR = (mHTTPSSVCRecord.ref() != nullptr);
return ContinueOnBeforeConnect(hasHTTPSRR, aStatus, hasHTTPSRR);
}
- auto dnsStrategy = GetProxyDNSStrategy();
- if (!(dnsStrategy & DNS_PREFETCH_ORIGIN)) {
- return ContinueOnBeforeConnect(aShouldUpgrade, aStatus);
- }
-
LOG(("nsHttpChannel::MaybeUseHTTPSRRForUpgrade [%p] wait for HTTPS RR",
this));
OriginAttributes originAttributes;
StoragePrincipalHelper::GetOriginAttributesForHTTPSRR(this, originAttributes);
RefPtr<nsDNSPrefetch> resolver =
new nsDNSPrefetch(mURI, originAttributes, nsIRequest::GetTRRMode());
@@ -1346,23 +1350,23 @@ void nsHttpChannel::SpeculativeConnect()
if (LoadAllowStaleCacheContent()) {
return;
}
nsCOMPtr<nsIInterfaceRequestor> callbacks;
NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
getter_AddRefs(callbacks));
if (!callbacks) return;
-
- Unused << gHttpHandler->SpeculativeConnect(
+ bool httpsRRAllowed = !(mCaps & NS_HTTP_DISALLOW_HTTPS_RR);
+ Unused << gHttpHandler->MaybeSpeculativeConnectWithHTTPSRR(
mConnectionInfo, callbacks,
mCaps & (NS_HTTP_DISALLOW_SPDY | NS_HTTP_TRR_MODE_MASK |
NS_HTTP_DISABLE_IPV4 | NS_HTTP_DISABLE_IPV6 |
NS_HTTP_DISALLOW_HTTP3 | NS_HTTP_REFRESH_DNS),
- gHttpHandler->EchConfigEnabled());
+ gHttpHandler->EchConfigEnabled() && httpsRRAllowed);
}
void nsHttpChannel::DoNotifyListenerCleanup() {
// We don't need this info anymore
CleanRedirectCacheChainIfNecessary();
}
void nsHttpChannel::ReleaseListeners() {
@@ -6755,39 +6759,26 @@ nsHttpChannel::GetOrCreateChannelClassif
LOG(("nsHttpChannel [%p] created nsChannelClassifier [%p]\n", this,
mChannelClassifier.get()));
}
RefPtr<nsChannelClassifier> classifier = mChannelClassifier;
return classifier.forget();
}
-uint16_t nsHttpChannel::GetProxyDNSStrategy() {
- // This function currently only supports returning DNS_PREFETCH_ORIGIN.
- // Support for the rest of the DNS_* flags will be added later.
-
+ProxyDNSStrategy nsHttpChannel::GetProxyDNSStrategy() {
// When network_dns_force_use_https_rr is true, return DNS_PREFETCH_ORIGIN.
// This ensures that we always perform HTTPS RR query.
- if (!mProxyInfo || StaticPrefs::network_dns_force_use_https_rr()) {
- return DNS_PREFETCH_ORIGIN;
- }
-
- uint32_t flags = 0;
- nsAutoCString type;
- mProxyInfo->GetFlags(&flags);
- mProxyInfo->GetType(type);
+ nsCOMPtr<nsProxyInfo> proxyInfo(static_cast<nsProxyInfo*>(mProxyInfo.get()));
+ if (!proxyInfo || StaticPrefs::network_dns_force_use_https_rr()) {
+ return ProxyDNSStrategy::ORIGIN;
+ }
// If the proxy is not to perform name resolution itself.
- if (!(flags & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST)) {
- if (type.EqualsLiteral("socks")) {
- return DNS_PREFETCH_ORIGIN;
- }
- }
-
- return 0;
+ return GetProxyDNSStrategyHelper(proxyInfo->Type(), proxyInfo->Flags());
}
// BeginConnect() SHOULD NOT call AsyncAbort(). AsyncAbort will be called by
// functions that called BeginConnect if needed. Only
// MaybeResolveProxyAndBeginConnect and OnProxyAvailable ever call
// BeginConnect.
nsresult nsHttpChannel::BeginConnect() {
LOG(("nsHttpChannel::BeginConnect [this=%p]\n", this));
@@ -6962,21 +6953,23 @@ nsresult nsHttpChannel::BeginConnect() {
} else {
LOG(("nsHttpChannel %p Using default connection info", this));
mConnectionInfo = connInfo;
Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, false);
}
bool trrEnabled = false;
+ auto dnsStrategy = GetProxyDNSStrategy();
bool httpsRRAllowed =
!LoadBeConservative() && !(mCaps & NS_HTTP_BE_CONSERVATIVE) &&
!(mLoadInfo->TriggeringPrincipal()->IsSystemPrincipal() &&
mLoadInfo->GetExternalContentPolicyType() !=
ExtContentPolicy::TYPE_DOCUMENT) &&
+ dnsStrategy == ProxyDNSStrategy::ORIGIN &&
!mConnectionInfo->UsingConnect() && canUseHTTPSRRonNetwork(&trrEnabled) &&
StaticPrefs::network_dns_use_https_rr_as_altsvc();
if (!httpsRRAllowed) {
DisallowHTTPSRR(mCaps);
} else if (trrEnabled) {
if (nsIRequest::GetTRRMode() != nsIRequest::TRR_DISABLED_MODE) {
mCaps |= NS_HTTP_FORCE_WAIT_HTTP_RR;
}
@@ -7077,26 +7070,17 @@ nsresult nsHttpChannel::BeginConnect() {
"[this=%p]\n",
this));
return NS_OK;
}
ReEvaluateReferrerAfterTrackingStatusIsKnown();
}
- rv = MaybeStartDNSPrefetch();
- if (NS_FAILED(rv)) {
- auto dnsStrategy = GetProxyDNSStrategy();
- if (dnsStrategy & DNS_BLOCK_ON_ORIGIN_RESOLVE) {
- // TODO: Should this be fatal?
- return rv;
- }
- // Otherwise this shouldn't be fatal.
- return NS_OK;
- }
+ MaybeStartDNSPrefetch();
rv = CallOrWaitForResume(
[](nsHttpChannel* self) { return self->PrepareToConnect(); });
if (NS_FAILED(rv)) {
return rv;
}
if (shouldBeClassified) {
@@ -7106,69 +7090,57 @@ nsresult nsHttpChannel::BeginConnect() {
LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]",
channelClassifier.get(), this));
channelClassifier->Start();
}
return NS_OK;
}
-nsresult nsHttpChannel::MaybeStartDNSPrefetch() {
+void nsHttpChannel::MaybeStartDNSPrefetch() {
// Start a DNS lookup very early in case the real open is queued the DNS can
// happen in parallel. Do not do so in the presence of an HTTP proxy as
// all lookups other than for the proxy itself are done by the proxy.
// Also we don't do a lookup if the LOAD_NO_NETWORK_IO or
// LOAD_ONLY_FROM_CACHE flags are set.
//
// We keep the DNS prefetch object around so that we can retrieve
// timing information from it. There is no guarantee that we actually
// use the DNS prefetch data for the real connection, but as we keep
// this data around for 3 minutes by default, this should almost always
// be correct, and even when it isn't, the timing still represents _a_
// valid DNS lookup timing for the site, even if it is not _the_
// timing we used.
if ((mLoadFlags & (LOAD_NO_NETWORK_IO | LOAD_ONLY_FROM_CACHE)) ||
LoadAuthRedirectedChannel()) {
- return NS_OK;
+ return;
}
auto dnsStrategy = GetProxyDNSStrategy();
LOG(
("nsHttpChannel::MaybeStartDNSPrefetch [this=%p, strategy=%u] "
"prefetching%s\n",
- this, dnsStrategy,
+ this, static_cast<uint32_t>(dnsStrategy),
mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : ""));
- if (dnsStrategy & DNS_PREFETCH_ORIGIN) {
+ if (dnsStrategy == ProxyDNSStrategy::ORIGIN) {
OriginAttributes originAttributes;
StoragePrincipalHelper::GetOriginAttributesForNetworkState(
this, originAttributes);
mDNSPrefetch =
new nsDNSPrefetch(mURI, originAttributes, nsIRequest::GetTRRMode(),
this, LoadTimingEnabled());
nsIDNSService::DNSFlags dnsFlags = nsIDNSService::RESOLVE_DEFAULT_FLAGS;
if (mCaps & NS_HTTP_REFRESH_DNS) {
dnsFlags |= nsIDNSService::RESOLVE_BYPASS_CACHE;
}
- nsresult rv = mDNSPrefetch->PrefetchHigh(dnsFlags);
-
- if (dnsStrategy & DNS_BLOCK_ON_ORIGIN_RESOLVE) {
- LOG((" blocking on prefetching origin"));
-
- if (NS_WARN_IF(NS_FAILED(rv))) {
- LOG((" lookup failed with 0x%08" PRIx32 ", aborting request",
- static_cast<uint32_t>(rv)));
- return rv;
- }
-
- // Resolved in OnLookupComplete.
- mDNSBlockingThenable = mDNSBlockingPromise.Ensure(__func__);
- }
+
+ Unused << mDNSPrefetch->PrefetchHigh(dnsFlags);
if (StaticPrefs::network_dns_use_https_rr_as_altsvc() && !mHTTPSSVCRecord &&
!(mCaps & NS_HTTP_DISALLOW_HTTPS_RR) && canUseHTTPSRRonNetwork()) {
MOZ_ASSERT(!mHTTPSSVCRecord);
OriginAttributes originAttributes;
StoragePrincipalHelper::GetOriginAttributesForHTTPSRR(this,
originAttributes);
@@ -7176,18 +7148,16 @@ nsresult nsHttpChannel::MaybeStartDNSPre
RefPtr<nsDNSPrefetch> resolver =
new nsDNSPrefetch(mURI, originAttributes, nsIRequest::GetTRRMode());
Unused << resolver->FetchHTTPSSVC(mCaps & NS_HTTP_REFRESH_DNS, true,
[](nsIDNSHTTPSSVCRecord*) {
// Do nothing. This is a DNS prefetch.
});
}
}
-
- return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::GetEncodedBodySize(uint64_t* aEncodedBodySize) {
if (mCacheEntry && !LoadCacheEntryIsWriteOnly()) {
int64_t dataSize = 0;
mCacheEntry->GetDataSize(&dataSize);
*aEncodedBodySize = dataSize;
diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -304,33 +304,21 @@ class nsHttpChannel final : public HttpB
bool RequestIsConditional();
void HandleContinueCancellingByURLClassifier(nsresult aErrorCode);
nsresult CancelInternal(nsresult status);
void ContinueCancellingByURLClassifier(nsresult aErrorCode);
// Connections will only be established in this function.
// (including DNS prefetch and speculative connection.)
void MaybeResolveProxyAndBeginConnect();
- nsresult MaybeStartDNSPrefetch();
-
- // Tells the channel to resolve the origin of the end server we are connecting
- // to.
- static uint16_t const DNS_PREFETCH_ORIGIN = 1 << 0;
- // Tells the channel to resolve the host name of the proxy.
- static uint16_t const DNS_PREFETCH_PROXY = 1 << 1;
- // Will be set if the current channel uses an HTTP/HTTPS proxy.
- static uint16_t const DNS_PROXY_IS_HTTP = 1 << 2;
- // Tells the channel to wait for the result of the origin server resolution
- // before any connection attempts are made.
- static uint16_t const DNS_BLOCK_ON_ORIGIN_RESOLVE = 1 << 3;
+ void MaybeStartDNSPrefetch();
// Based on the proxy configuration determine the strategy for resolving the
// end server host name.
- // Returns a combination of the above flags.
- uint16_t GetProxyDNSStrategy();
+ ProxyDNSStrategy GetProxyDNSStrategy();
// We might synchronously or asynchronously call BeginConnect,
// which includes DNS prefetch and speculative connection, according to
// whether an async tracker lookup is required. If the tracker lookup
// is required, this funciton will just return NS_OK and BeginConnect()
// will be called when callback. See Bug 1325054 for more information.
nsresult BeginConnect();
[[nodiscard]] nsresult PrepareToConnect();
diff --git a/netwerk/protocol/http/nsHttpConnectionInfo.h b/netwerk/protocol/http/nsHttpConnectionInfo.h
--- a/netwerk/protocol/http/nsHttpConnectionInfo.h
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.h
@@ -122,16 +122,23 @@ class nsHttpConnectionInfo final : publi
return mProxyInfo ? mProxyInfo->Type() : nullptr;
}
const char* ProxyUsername() const {
return mProxyInfo ? mProxyInfo->Username().get() : nullptr;
}
const char* ProxyPassword() const {
return mProxyInfo ? mProxyInfo->Password().get() : nullptr;
}
+ uint32_t ProxyFlag() const {
+ uint32_t flags = 0;
+ if (mProxyInfo) {
+ mProxyInfo->GetFlags(&flags);
+ }
+ return flags;
+ }
const nsCString& ProxyAuthorizationHeader() const {
return mProxyInfo ? mProxyInfo->ProxyAuthorizationHeader() : EmptyCString();
}
const nsCString& ConnectionIsolationKey() const {
return mProxyInfo ? mProxyInfo->ConnectionIsolationKey() : EmptyCString();
}
diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -3570,19 +3570,25 @@ void nsHttpConnectionMgr::DoSpeculativeC
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
MOZ_ASSERT(aTrans);
MOZ_ASSERT(aEnt);
if (!gHttpHandler->Active()) {
// Do nothing if we are shutting down.
return;
}
- if (aFetchHTTPSRR && NS_SUCCEEDED(aTrans->FetchHTTPSRR())) {
- // nsHttpConnectionMgr::DoSpeculativeConnection will be called again when
- // HTTPS RR is available.
+ ProxyDNSStrategy strategy = GetProxyDNSStrategyHelper(
+ aEnt->mConnInfo->ProxyType(), aEnt->mConnInfo->ProxyFlag());
+ // Speculative connections can be triggered by non-Necko consumers,
+ // so add an extra check to ensure HTTPS RR isn't fetched when a proxy is
+ // used.
+ if (aFetchHTTPSRR && strategy == ProxyDNSStrategy::ORIGIN &&
+ NS_SUCCEEDED(aTrans->FetchHTTPSRR())) {
+ // nsHttpConnectionMgr::DoSpeculativeConnection will be called again
+ // when HTTPS RR is available.
return;
}
uint32_t parallelSpeculativeConnectLimit =
aTrans->ParallelSpeculativeConnectLimit()
? *aTrans->ParallelSpeculativeConnectLimit()
: gHttpHandler->ParallelSpeculativeConnectLimit();
bool ignoreIdle = aTrans->IgnoreIdle() ? *aTrans->IgnoreIdle() : false;
diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -2403,17 +2403,19 @@ nsresult nsHttpHandler::SpeculativeConne
if (!neckoParent) {
continue;
}
Unused << neckoParent->SendSpeculativeConnectRequest();
}
}
}
- return SpeculativeConnect(ci, aCallbacks);
+ // When ech is enabled, always do speculative connect with HTTPS RR.
+ return MaybeSpeculativeConnectWithHTTPSRR(ci, aCallbacks, 0,
+ EchConfigEnabled());
}
NS_IMETHODIMP
nsHttpHandler::SpeculativeConnect(nsIURI* aURI, nsIPrincipal* aPrincipal,
nsIInterfaceRequestor* aCallbacks,
bool aAnonymous) {
return SpeculativeConnectInternal(aURI, aPrincipal, Nothing(), aCallbacks,
aAnonymous);
diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -291,24 +291,23 @@ class nsHttpHandler final : public nsIHt
[[nodiscard]] nsresult ProcessPendingQ() {
return mConnMgr->ProcessPendingQ();
}
[[nodiscard]] nsresult GetSocketThreadTarget(nsIEventTarget** target) {
return mConnMgr->GetSocketThreadTarget(target);
}
- [[nodiscard]] nsresult SpeculativeConnect(nsHttpConnectionInfo* ci,
- nsIInterfaceRequestor* callbacks,
- uint32_t caps = 0,
- bool aFetchHTTPSRR = false) {
+ [[nodiscard]] nsresult MaybeSpeculativeConnectWithHTTPSRR(
+ nsHttpConnectionInfo* ci, nsIInterfaceRequestor* callbacks, uint32_t caps,
+ bool aFetchHTTPSRR) {
TickleWifi(callbacks);
RefPtr<nsHttpConnectionInfo> clone = ci->Clone();
return mConnMgr->SpeculativeConnect(clone, callbacks, caps, nullptr,
- aFetchHTTPSRR | EchConfigEnabled());
+ aFetchHTTPSRR);
}
[[nodiscard]] nsresult SpeculativeConnect(nsHttpConnectionInfo* ci,
nsIInterfaceRequestor* callbacks,
uint32_t caps,
SpeculativeTransaction* aTrans) {
RefPtr<nsHttpConnectionInfo> clone = ci->Clone();
return mConnMgr->SpeculativeConnect(clone, callbacks, caps, aTrans);
diff --git a/netwerk/test/unit/test_proxyDNS_leak.js b/netwerk/test/unit/test_proxyDNS_leak.js
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_proxyDNS_leak.js
@@ -0,0 +1,111 @@
+/* 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/. */
+
+// Test when socks proxy is registered, we don't try to resolve HTTPS record.
+// Steps:
+// 1. Use addHTTPSRecordOverride to add an override for service.com.
+// 2. Add a proxy filter to use socks proxy.
+// 3. Create a request to load service.com.
+// 4. See if the HTTPS record is in DNS cache entries.
+
+"use strict";
+
+const gDashboard = Cc["@mozilla.org/network/dashboard;1"].getService(
+ Ci.nsIDashboard
+);
+const pps = Cc["@mozilla.org/network/protocol-proxy-service;1"].getService();
+
+add_task(async function setup() {
+ Services.prefs.setBoolPref("network.dns.native_https_query", true);
+ Services.prefs.setBoolPref("network.dns.native_https_query_win10", true);
+ const override = Cc["@mozilla.org/network/native-dns-override;1"].getService(
+ Ci.nsINativeDNSResolverOverride
+ );
+
+ let rawBuffer = [
+ 0, 0, 128, 0, 0, 0, 0, 1, 0, 0, 0, 0, 7, 115, 101, 114, 118, 105, 99, 101,
+ 3, 99, 111, 109, 0, 0, 65, 0, 1, 0, 0, 0, 55, 0, 13, 0, 1, 0, 0, 1, 0, 6, 2,
+ 104, 50, 2, 104, 51,
+ ];
+ override.addHTTPSRecordOverride("service.com", rawBuffer, rawBuffer.length);
+ override.addIPOverride("service.com", "127.0.0.1");
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("network.dns.native_https_query");
+ Services.prefs.clearUserPref("network.dns.native_https_query_win10");
+ Services.prefs.clearUserPref("network.dns.localDomains");
+ override.clearOverrides();
+ });
+});
+
+function makeChan(uri) {
+ let chan = NetUtil.newChannel({
+ uri,
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ }).QueryInterface(Ci.nsIHttpChannel);
+ chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
+ return chan;
+}
+
+function channelOpenPromise(chan, flags) {
+ return new Promise(resolve => {
+ function finish(req, buffer) {
+ resolve([req, buffer]);
+ }
+ chan.asyncOpen(new ChannelListener(finish, null, flags));
+ });
+}
+
+async function isRecordFound(hostname) {
+ return new Promise(resolve => {
+ gDashboard.requestDNSInfo(function (data) {
+ let found = false;
+ for (let i = 0; i < data.entries.length; i++) {
+ if (
+ data.entries[i].hostname == hostname &&
+ data.entries[i].type == Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC
+ ) {
+ found = true;
+ break;
+ }
+ }
+ resolve(found);
+ });
+ });
+}
+
+async function do_test_with_proxy_filter(filter) {
+ pps.registerFilter(filter, 10);
+
+ let chan = makeChan(`https://service.com/`);
+ await channelOpenPromise(chan, CL_EXPECT_LATE_FAILURE | CL_ALLOW_UNKNOWN_CL);
+
+ let found = await isRecordFound("service.com");
+ pps.unregisterFilter(filter);
+
+ return found;
+}
+
+add_task(async function test_proxyDNS_do_leak() {
+ let filter = new NodeProxyFilter("socks", "localhost", 443, 0);
+
+ let res = await do_test_with_proxy_filter(filter);
+
+ Assert.ok(res, "Should find a DNS entry");
+});
+
+add_task(async function test_proxyDNS_dont_leak() {
+ Services.dns.clearCache(false);
+
+ let filter = new NodeProxyFilter(
+ "socks",
+ "localhost",
+ 443,
+ Ci.nsIProxyInfo.TRANSPARENT_PROXY_RESOLVES_HOST
+ );
+
+ let res = await do_test_with_proxy_filter(filter);
+
+ Assert.ok(!res, "Should not find a DNS entry");
+});
diff --git a/netwerk/test/unit/xpcshell.toml b/netwerk/test/unit/xpcshell.toml
--- a/netwerk/test/unit/xpcshell.toml
+++ b/netwerk/test/unit/xpcshell.toml
@@ -974,16 +974,22 @@ skip-if = [
["test_proxy-slow-upload.js"]
["test_proxy_cancel.js"]
run-sequentially = "node server exceptions dont replay well"
["test_proxy_pac.js"]
+["test_proxyDNS_leak.js"]
+skip-if = [
+ "os == 'android'",
+ "socketprocess_networking",
+]
+
["test_proxyconnect.js"]
skip-if = [
"tsan",
"socketprocess_networking", # Bug 1614708
]
["test_proxyconnect_https.js"]
skip-if = [
diff --git a/toolkit/content/aboutNetworking.js b/toolkit/content/aboutNetworking.js
--- a/toolkit/content/aboutNetworking.js
+++ b/toolkit/content/aboutNetworking.js
@@ -111,16 +111,21 @@ function displayDns(data) {
prevURL.parentNode.replaceChild(trr_url_tbody, prevURL);
let cont = document.getElementById("dns_content");
let parent = cont.parentNode;
let new_cont = document.createElement("tbody");
new_cont.setAttribute("id", "dns_content");
for (let i = 0; i < data.entries.length; i++) {
+ // TODO: Will be supported in bug 1889387.
+ if (data.entries[i].type != Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT) {
+ continue;
+ }
+
let row = document.createElement("tr");
row.appendChild(col(data.entries[i].hostname));
row.appendChild(col(data.entries[i].family));
row.appendChild(col(data.entries[i].trr));
let column = document.createElement("td");
for (let j = 0; j < data.entries[i].hostaddr.length; j++) {
column.appendChild(document.createTextNode(data.entries[i].hostaddr[j]));