Bug 1220810 - Hardcode localhost to loopback, r=ckerschb,necko-reviewers,dragana

This patch make localhost addresses resolve to a loopback address,
thereby ensuring that we can safely treat http://localhost/ and
http://*.localhost/ as "Potentially Trustworthy". This addresses
various bug reports from developers and aligns with specifications.

See https://groups.google.com/g/mozilla.dev.platform/c/sZdEYTiEBdE

Differential Revision: https://phabricator.services.mozilla.com/D92716
This commit is contained in:
Frédéric Wang 2020-10-22 07:36:15 +00:00
Родитель 1508f89c46
Коммит 70d15af587
27 изменённых файлов: 441 добавлений и 283 удалений

Просмотреть файл

@ -9,6 +9,8 @@
const PREF_BAD_CERT_DOMAIN_FIX_ENABLED =
"security.bad_cert_domain_error.url_fix_enabled";
const PREF_ALLOW_HIJACKING_LOCALHOST =
"network.proxy.allow_hijacking_localhost";
const BAD_CERT_DOMAIN_ERROR_URL = "https://badcertdomain.example.com:443";
const FIXED_URL = "https://www.badcertdomain.example.com/";
@ -77,7 +79,9 @@ add_task(async function ignoreBadCertDomain() {
info("Certificate error was shown as expected");
// Test that urls with IP addresses are not fixed.
Services.prefs.setBoolPref(PREF_ALLOW_HIJACKING_LOCALHOST, true);
await verifyErrorPage(IPV4_ADDRESS);
Services.prefs.clearUserPref(PREF_ALLOW_HIJACKING_LOCALHOST);
info("Certificate error was shown as expected for an IP address");
// Test that urls with ports are not fixed.

Просмотреть файл

@ -38,6 +38,8 @@ function run_test_pt1() {
} catch (e) {}
Services.io.offline = true;
prefs.setBoolPref("network.dns.offline-localhost", false);
// We always resolve localhost as it's hardcoded without the following pref:
prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
gExpectedStatus = Cr.NS_ERROR_OFFLINE;
gNextTestFunc = run_test_pt2;
@ -49,6 +51,7 @@ function run_test_pt1() {
function run_test_pt2() {
Services.io.offline = false;
prefs.clearUserPref("network.dns.offline-localhost");
prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
gExpectedStatus = Cr.NS_ERROR_CONNECTION_REFUSED;
gNextTestFunc = end_test;

Просмотреть файл

@ -27,7 +27,9 @@ support-files =
!/dom/media/test/bug461281.ogg
!/dom/media/test/seek.webm
!/dom/media/test/gizmo.mp4
prefs = privacy.partition.network_state=false
prefs =
privacy.partition.network_state=false
network.proxy.allow_hijacking_localhost=true
[test_1488832.html]
[test_a_noOp.html]

Просмотреть файл

@ -229,8 +229,7 @@ nsMixedContentBlocker::ShouldLoad(nsIURI* aContentLocation,
bool nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost(
const nsACString& aAsciiHost) {
if (aAsciiHost.EqualsLiteral("::1") ||
aAsciiHost.EqualsLiteral("localhost")) {
if (mozilla::net::IsLoopbackHostname(aAsciiHost)) {
return true;
}
@ -249,9 +248,8 @@ bool nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost(
// https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy says
// we should only consider [::1]/128 as a potentially trustworthy IPv6
// address, whereas for IPv4 127.0.0.1/8 are considered as potentially
// trustworthy. We already handled "[::1]" above, so all that's remained to
// handle here are IPv4 loopback addresses.
return addr.IsIPAddrV4() && addr.IsLoopbackAddr();
// trustworthy.
return addr.IsLoopBackAddressWithoutIPv6Mapping();
}
bool nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(nsIURI* aURL) {

Просмотреть файл

@ -34,6 +34,11 @@ enum MixedContentTypes {
using mozilla::OriginAttributes;
class nsILoadInfo; // forward declaration
namespace mozilla {
namespace net {
class nsProtocolProxyService; // forward declaration
}
} // namespace mozilla
class nsMixedContentBlocker : public nsIContentPolicy,
public nsIChannelEventSink {

Просмотреть файл

@ -24,12 +24,29 @@ struct TestExpectations {
bool expectedResult;
};
class MOZ_RAII AutoRestoreBoolPref final {
public:
AutoRestoreBoolPref(const char* aPref, bool aValue) : mPref(aPref) {
Preferences::GetBool(mPref, &mOldValue);
Preferences::SetBool(mPref, aValue);
}
~AutoRestoreBoolPref() { Preferences::SetBool(mPref, mOldValue); }
private:
const char* mPref = nullptr;
bool mOldValue = false;
};
// ============================= TestDirectives ========================
TEST(SecureContext, IsOriginPotentiallyTrustworthyWithContentPrincipal)
{
// boolean isOriginPotentiallyTrustworthy(in nsIPrincipal aPrincipal);
AutoRestoreBoolPref savedPref("network.proxy.allow_hijacking_localhost",
false);
static const TestExpectations uris[] = {
{"http://example.com/", false},
{"https://example.com/", true},
@ -39,7 +56,9 @@ TEST(SecureContext, IsOriginPotentiallyTrustworthyWithContentPrincipal)
{"ftp://example.com", false},
{"about:config", false},
{"http://localhost", true},
{"http://xyzzy.localhost", false},
{"http://localhost.localhost", true},
{"http://a.b.c.d.e.localhost", true},
{"http://xyzzy.localhost", true},
{"http://127.0.0.1", true},
{"http://127.0.0.2", true},
{"http://127.1.0.1", true},
@ -71,7 +90,8 @@ TEST(SecureContext, IsOriginPotentiallyTrustworthyWithContentPrincipal)
->CreateContentPrincipalFromOrigin(uri, getter_AddRefs(prin));
ASSERT_EQ(rv, NS_OK);
bool isPotentiallyTrustworthy = prin->GetIsOriginPotentiallyTrustworthy();
ASSERT_EQ(isPotentiallyTrustworthy, uris[i].expectedResult);
ASSERT_EQ(isPotentiallyTrustworthy, uris[i].expectedResult)
<< uris[i].uri << uris[i].expectedResult;
}
}

Просмотреть файл

@ -38,6 +38,7 @@ add_task(async function test_isOriginPotentiallyTrustworthy() {
["http://example.com/", false],
["https://example.com/", true],
["http://localhost/", true],
["http://localhost.localhost/", true],
["http://127.0.0.1/", true],
["file:///", true],
["resource:///", true],

Просмотреть файл

@ -21,6 +21,7 @@ add_task(function test_setup() {
"security.webauth.webauthn_enable_usbtoken",
false
);
Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
});
registerCleanupFunction(async function() {
@ -31,6 +32,7 @@ registerCleanupFunction(async function() {
);
Services.prefs.clearUserPref("security.webauth.webauthn_enable_softtoken");
Services.prefs.clearUserPref("security.webauth.webauthn_enable_usbtoken");
Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
});
add_task(async function test_appid() {

Просмотреть файл

@ -8213,6 +8213,12 @@
value: true
mirror: always
# Set true to allow resolving proxy for localhost
- name: network.proxy.allow_hijacking_localhost
type: RelaxedAtomicBool
value: false
mirror: always
# Allow CookieJarSettings to be unblocked for channels without a document.
# This is for testing only.
- name: network.cookieJarSettings.unblocked_for_testing

Просмотреть файл

@ -1947,8 +1947,6 @@ pref("network.proxy.socks_port", 0);
pref("network.proxy.socks_version", 5);
pref("network.proxy.proxy_over_tls", true);
pref("network.proxy.no_proxies_on", "");
// Set true to allow resolving proxy for localhost
pref("network.proxy.allow_hijacking_localhost", false);
pref("network.proxy.failover_timeout", 1800); // 30 minutes
pref("network.online", true); //online/offline

Просмотреть файл

@ -36,7 +36,9 @@
#include "nsISystemProxySettings.h"
#include "nsINetworkLinkService.h"
#include "nsIHttpChannelInternal.h"
#include "mozilla/dom/nsMixedContentBlocker.h"
#include "mozilla/Logging.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/Tokenizer.h"
#include "mozilla/Unused.h"
@ -771,7 +773,6 @@ nsProtocolProxyService::nsProtocolProxyService()
mSOCKSProxyRemoteDNS(false),
mProxyOverTLS(true),
mWPADOverDHCPEnabled(false),
mAllowHijackingLocalhost(false),
mPACMan(nullptr),
mSessionStart(PR_Now()),
mFailedProxyTimeout(30 * 60) // 30 minute default
@ -1019,11 +1020,6 @@ void nsProtocolProxyService::PrefsChanged(nsIPrefBranch* prefBranch,
reloadPAC = reloadPAC || mProxyConfig == PROXYCONFIG_WPAD;
}
if (!pref || !strcmp(pref, PROXY_PREF("allow_hijacking_localhost"))) {
proxy_GetBoolPref(prefBranch, PROXY_PREF("allow_hijacking_localhost"),
mAllowHijackingLocalhost);
}
if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout")))
proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
mFailedProxyTimeout);
@ -1097,9 +1093,12 @@ bool nsProtocolProxyService::CanUseProxy(nsIURI* aURI, int32_t defaultPort) {
// Don't use proxy for local hosts (plain hostname, no dots)
if ((!is_ipaddr && mFilterLocalHosts && !host.Contains('.')) ||
(!mAllowHijackingLocalhost &&
(host.EqualsLiteral("127.0.0.1") || host.EqualsLiteral("::1") ||
host.EqualsLiteral("localhost")))) {
// This method detects if we have network.proxy.allow_hijacking_localhost
// pref enabled. If it's true then this method will always return false
// otherwise it returns true if the host matches an address that's
// hardcoded to the loopback address.
(!StaticPrefs::network_proxy_allow_hijacking_localhost() &&
nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost(host))) {
LOG(("Not using proxy for this local host [%s]!\n", host.get()));
return false; // don't allow proxying
}

Просмотреть файл

@ -388,7 +388,6 @@ class nsProtocolProxyService final : public nsIProtocolProxyService2,
bool mSOCKSProxyRemoteDNS;
bool mProxyOverTLS;
bool mWPADOverDHCPEnabled;
bool mAllowHijackingLocalhost;
RefPtr<nsPACMan> mPACMan; // non-null if we are using PAC
nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;

Просмотреть файл

@ -6,9 +6,11 @@
#include "mozilla/net/DNS.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/mozalloc.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/StaticPrefs_network.h"
#include "nsContentUtils.h"
#include "nsString.h"
#include <string.h>
@ -140,23 +142,41 @@ bool NetAddr::ToStringBuffer(char* buf, uint32_t bufSize) const {
}
bool NetAddr::IsLoopbackAddr() const {
if (IsLoopBackAddressWithoutIPv6Mapping()) {
return true;
}
const NetAddr* addr = this;
if (addr->raw.family != AF_INET6) {
return false;
}
return IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip) &&
IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip) == htonl(INADDR_LOOPBACK);
}
bool NetAddr::IsLoopBackAddressWithoutIPv6Mapping() const {
const NetAddr* addr = this;
if (addr->raw.family == AF_INET) {
// Consider 127.0.0.1/8 as loopback
uint32_t ipv4Addr = ntohl(addr->inet.ip);
return (ipv4Addr >> 24) == 127;
}
if (addr->raw.family == AF_INET6) {
if (IPv6ADDR_IS_LOOPBACK(&addr->inet6.ip)) {
return true;
}
if (IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip) &&
IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip) ==
htonl(INADDR_LOOPBACK)) {
return true;
}
return addr->raw.family == AF_INET6 && IPv6ADDR_IS_LOOPBACK(&addr->inet6.ip);
}
bool IsLoopbackHostname(const nsACString& aAsciiHost) {
// If the user has configured to proxy localhost addresses don't consider them
// to be secure
if (StaticPrefs::network_proxy_allow_hijacking_localhost()) {
return false;
}
return false;
nsAutoCString host;
nsContentUtils::ASCIIToLower(aAsciiHost, host);
return host.EqualsLiteral("localhost") ||
StringEndsWith(host, ".localhost"_ns);
}
bool NetAddr::IsIPAddrAny() const {

Просмотреть файл

@ -137,6 +137,7 @@ union NetAddr {
bool IsIPAddrAny() const;
bool IsLoopbackAddr() const;
bool IsLoopBackAddressWithoutIPv6Mapping() const;
bool IsIPAddrV4() const;
bool IsIPAddrV4Mapped() const;
bool IsIPAddrLocal() const;
@ -233,6 +234,8 @@ void PRNetAddrToNetAddr(const PRNetAddr* prAddr, NetAddr* addr);
// Does not do a ptr safety check!
void NetAddrToPRNetAddr(const NetAddr* addr, PRNetAddr* prAddr);
bool IsLoopbackHostname(const nsACString& aAsciiHost);
} // namespace net
} // namespace mozilla

Просмотреть файл

@ -916,11 +916,7 @@ nsresult nsHostResolver::GetHostRecord(const nsACString& host,
RefPtr<nsHostRecord>& entry = mRecordDB.GetOrInsert(key);
if (!entry) {
if (IS_ADDR_TYPE(type)) {
entry = new AddrHostRecord(key);
} else {
entry = new TypeHostRecord(key);
}
entry = InitRecord(key);
}
RefPtr<nsHostRecord> rec = entry;
@ -939,6 +935,48 @@ nsresult nsHostResolver::GetHostRecord(const nsACString& host,
return NS_OK;
}
nsHostRecord* nsHostResolver::InitRecord(const nsHostKey& key) {
if (IS_ADDR_TYPE(key.type)) {
return new AddrHostRecord(key);
}
return new TypeHostRecord(key);
}
already_AddRefed<nsHostRecord> nsHostResolver::InitLoopbackRecord(
const nsHostKey& key, nsresult* aRv) {
MOZ_ASSERT(aRv);
MOZ_ASSERT(IS_ADDR_TYPE(key.type));
*aRv = NS_ERROR_FAILURE;
RefPtr<nsHostRecord> rec = InitRecord(key);
RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
MutexAutoLock lock(addrRec->addr_info_lock);
PRNetAddr prAddr;
if (key.af == PR_AF_INET) {
MOZ_RELEASE_ASSERT(PR_StringToNetAddr("127.0.0.1", &prAddr) == PR_SUCCESS);
} else {
MOZ_RELEASE_ASSERT(PR_StringToNetAddr("::1", &prAddr) == PR_SUCCESS);
}
RefPtr<AddrInfo> ai;
*aRv = GetAddrInfo(rec->host, rec->af, addrRec->flags, getter_AddRefs(ai),
addrRec->mGetTtl);
if (NS_WARN_IF(NS_FAILED(*aRv))) {
return nullptr;
}
addrRec->addr_info = ai;
addrRec->SetExpiration(TimeStamp::NowLoRes(), mDefaultCacheLifetime,
mDefaultGracePeriod);
addrRec->negative = false;
*aRv = NS_OK;
return rec.forget();
}
nsresult nsHostResolver::ResolveHost(const nsACString& aHost,
const nsACString& aTrrServer,
uint16_t type,
@ -988,272 +1026,282 @@ nsresult nsHostResolver::ResolveHost(const nsACString& aHost,
MutexAutoLock lock(mLock);
if (mShutdown) {
rv = NS_ERROR_NOT_INITIALIZED;
} else {
// check to see if there is already an entry for this |host|
// in the hash table. if so, then check to see if we can't
// just reuse the lookup result. otherwise, if there are
// any pending callbacks, then add to pending callbacks queue,
// and return. otherwise, add ourselves as first pending
// callback, and proceed to do the lookup.
return NS_ERROR_NOT_INITIALIZED;
}
bool excludedFromTRR = false;
if (gTRRService && gTRRService->IsExcludedFromTRR(host)) {
flags |= RES_DISABLE_TRR;
excludedFromTRR = true;
// check to see if there is already an entry for this |host|
// in the hash table. if so, then check to see if we can't
// just reuse the lookup result. otherwise, if there are
// any pending callbacks, then add to pending callbacks queue,
// and return. otherwise, add ourselves as first pending
// callback, and proceed to do the lookup.
if (!aTrrServer.IsEmpty()) {
return NS_ERROR_UNKNOWN_HOST;
}
bool excludedFromTRR = false;
if (gTRRService && gTRRService->IsExcludedFromTRR(host)) {
flags |= RES_DISABLE_TRR;
excludedFromTRR = true;
if (!aTrrServer.IsEmpty()) {
return NS_ERROR_UNKNOWN_HOST;
}
}
nsHostKey key(host, aTrrServer, type, flags, af,
(aOriginAttributes.mPrivateBrowsingId > 0), originSuffix);
// Check if we have a localhost domain, if so hardcode to loopback
if (IS_ADDR_TYPE(type) && IsLoopbackHostname(host)) {
nsresult rv;
RefPtr<nsHostRecord> result = InitLoopbackRecord(key, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(result);
aCallback->OnResolveHostComplete(this, result, NS_OK);
return NS_OK;
}
RefPtr<nsHostRecord>& entry = mRecordDB.GetOrInsert(key);
if (!entry) {
entry = InitRecord(key);
}
RefPtr<nsHostRecord> rec = entry;
RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
MOZ_ASSERT(rec, "Record should not be null");
MOZ_ASSERT((IS_ADDR_TYPE(type) && rec->IsAddrRecord() && addrRec) ||
(IS_OTHER_TYPE(type) && !rec->IsAddrRecord()));
if (excludedFromTRR) {
rec->RecordReason(nsHostRecord::TRR_EXCLUDED);
}
if (!(flags & RES_BYPASS_CACHE) &&
rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) {
LOG((" Using cached record for host [%s].\n", host.get()));
// put reference to host record on stack...
result = rec;
if (IS_ADDR_TYPE(type)) {
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
}
nsHostKey key(host, aTrrServer, type, flags, af,
(aOriginAttributes.mPrivateBrowsingId > 0), originSuffix);
RefPtr<nsHostRecord>& entry = mRecordDB.GetOrInsert(key);
if (!entry) {
// For entries that are in the grace period
// or all cached negative entries, use the cache but start a new
// lookup in the background
ConditionallyRefreshRecord(rec, host);
if (rec->negative) {
LOG((" Negative cache entry for host [%s].\n", host.get()));
if (IS_ADDR_TYPE(type)) {
entry = new AddrHostRecord(key);
} else {
entry = new TypeHostRecord(key);
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
METHOD_NEGATIVE_HIT);
}
status = NS_ERROR_UNKNOWN_HOST;
}
RefPtr<nsHostRecord> rec = entry;
RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec);
MOZ_ASSERT(rec, "Record should not be null");
MOZ_ASSERT((IS_ADDR_TYPE(type) && rec->IsAddrRecord() && addrRec) ||
(IS_OTHER_TYPE(type) && !rec->IsAddrRecord()));
// Check whether host is a IP address for A/AAAA queries.
// For by-type records we have already checked at the beginning of
// this function.
} else if (addrRec && addrRec->addr) {
// if the host name is an IP address literal and has been
// parsed, go ahead and use it.
LOG((" Using cached address for IP Literal [%s].\n", host.get()));
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
result = rec;
} else if (addrRec &&
PR_StringToNetAddr(host.get(), &tempAddr) == PR_SUCCESS) {
// try parsing the host name as an IP address literal to short
// circuit full host resolution. (this is necessary on some
// platforms like Win9x. see bug 219376 for more details.)
LOG((" Host is IP Literal [%s].\n", host.get()));
if (excludedFromTRR) {
rec->RecordReason(nsHostRecord::TRR_EXCLUDED);
// ok, just copy the result into the host record, and be
// done with it! ;-)
addrRec->addr = MakeUnique<NetAddr>();
PRNetAddrToNetAddr(&tempAddr, addrRec->addr.get());
// put reference to host record on stack...
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
result = rec;
// Check if we have received too many requests.
} else if (mPendingCount >= MAX_NON_PRIORITY_REQUESTS &&
!IsHighPriority(flags) && !rec->mResolving) {
LOG(
(" Lookup queue full: dropping %s priority request for "
"host [%s].\n",
IsMediumPriority(flags) ? "medium" : "low", host.get()));
if (IS_ADDR_TYPE(type)) {
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_OVERFLOW);
}
// This is a lower priority request and we are swamped, so refuse it.
rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
// Check if the entry is vaild.
if (!(flags & RES_BYPASS_CACHE) &&
rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) {
LOG((" Using cached record for host [%s].\n", host.get()));
// put reference to host record on stack...
result = rec;
if (IS_ADDR_TYPE(type)) {
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
}
// Check if the offline flag is set.
} else if (flags & RES_OFFLINE) {
LOG((" Offline request for host [%s]; ignoring.\n", host.get()));
rv = NS_ERROR_OFFLINE;
// For entries that are in the grace period
// or all cached negative entries, use the cache but start a new
// lookup in the background
ConditionallyRefreshRecord(rec, host);
// We do not have a valid result till here.
// A/AAAA request can check for an alternative entry like AF_UNSPEC.
// Otherwise we need to start a new query.
} else if (!rec->mResolving) {
// If this is an IPV4 or IPV6 specific request, check if there is
// an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
if (addrRec && !(flags & RES_BYPASS_CACHE) &&
((af == PR_AF_INET) || (af == PR_AF_INET6))) {
// Check for an AF_UNSPEC entry.
if (rec->negative) {
LOG((" Negative cache entry for host [%s].\n", host.get()));
if (IS_ADDR_TYPE(type)) {
const nsHostKey unspecKey(
host, aTrrServer, nsIDNSService::RESOLVE_TYPE_DEFAULT, flags,
PR_AF_UNSPEC, (aOriginAttributes.mPrivateBrowsingId > 0),
originSuffix);
RefPtr<nsHostRecord> unspecRec = mRecordDB.Get(unspecKey);
TimeStamp now = TimeStamp::NowLoRes();
if (unspecRec && unspecRec->HasUsableResult(now, flags)) {
MOZ_ASSERT(unspecRec->IsAddrRecord());
RefPtr<AddrHostRecord> addrUnspecRec = do_QueryObject(unspecRec);
MOZ_ASSERT(addrUnspecRec);
MOZ_ASSERT(addrUnspecRec->addr_info || addrUnspecRec->negative,
"Entry should be resolved or negative.");
LOG((" Trying AF_UNSPEC entry for host [%s] af: %s.\n", host.get(),
(af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
// We need to lock in case any other thread is reading
// addr_info.
MutexAutoLock lock(addrRec->addr_info_lock);
addrRec->addr_info = nullptr;
addrRec->addr_info_gencnt++;
if (unspecRec->negative) {
rec->negative = unspecRec->negative;
rec->CopyExpirationTimesAndFlagsFrom(unspecRec);
} else if (addrUnspecRec->addr_info) {
MutexAutoLock lock(addrUnspecRec->addr_info_lock);
if (addrUnspecRec->addr_info) {
// Search for any valid address in the AF_UNSPEC entry
// in the cache (not blocklisted and from the right
// family).
nsTArray<NetAddr> addresses;
for (const auto& addr : addrUnspecRec->addr_info->Addresses()) {
if ((af == addr.inet.family) &&
!addrUnspecRec->Blocklisted(&addr)) {
addresses.AppendElement(addr);
}
}
if (!addresses.IsEmpty()) {
addrRec->addr_info = new AddrInfo(
addrUnspecRec->addr_info->Hostname(),
addrUnspecRec->addr_info->CanonicalHostname(),
addrUnspecRec->addr_info->IsTRR(), std::move(addresses));
addrRec->addr_info_gencnt++;
rec->CopyExpirationTimesAndFlagsFrom(unspecRec);
}
}
}
// Now check if we have a new record.
if (rec->HasUsableResult(now, flags)) {
result = rec;
if (rec->negative) {
status = NS_ERROR_UNKNOWN_HOST;
}
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
ConditionallyRefreshRecord(rec, host);
} else if (af == PR_AF_INET6) {
// For AF_INET6, a new lookup means another AF_UNSPEC
// lookup. We have already iterated through the
// AF_UNSPEC addresses, so we mark this record as
// negative.
LOG(
(" No AF_INET6 in AF_UNSPEC entry: "
"host [%s] unknown host.",
host.get()));
result = rec;
rec->negative = true;
status = NS_ERROR_UNKNOWN_HOST;
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
METHOD_NEGATIVE_HIT);
}
status = NS_ERROR_UNKNOWN_HOST;
}
}
// If this is a by-type request or if no valid record was found
// in the cache or this is an AF_UNSPEC request, then start a
// new lookup.
if (!result) {
LOG((" No usable record in cache for host [%s] type %d.", host.get(),
type));
if (flags & RES_REFRESH_CACHE) {
rec->Invalidate();
}
// Check whether host is a IP address for A/AAAA queries.
// For by-type records we have already checked at the beginning of
// this function.
} else if (addrRec && addrRec->addr) {
// if the host name is an IP address literal and has been
// parsed, go ahead and use it.
LOG((" Using cached address for IP Literal [%s].\n", host.get()));
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
result = rec;
} else if (addrRec &&
PR_StringToNetAddr(host.get(), &tempAddr) == PR_SUCCESS) {
// try parsing the host name as an IP address literal to short
// circuit full host resolution. (this is necessary on some
// platforms like Win9x. see bug 219376 for more details.)
LOG((" Host is IP Literal [%s].\n", host.get()));
// ok, just copy the result into the host record, and be
// done with it! ;-)
addrRec->addr = MakeUnique<NetAddr>(&tempAddr);
// put reference to host record on stack...
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL);
result = rec;
// Check if we have received too many requests.
} else if (mPendingCount >= MAX_NON_PRIORITY_REQUESTS &&
!IsHighPriority(flags) && !rec->mResolving) {
LOG(
(" Lookup queue full: dropping %s priority request for "
"host [%s].\n",
IsMediumPriority(flags) ? "medium" : "low", host.get()));
if (IS_ADDR_TYPE(type)) {
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_OVERFLOW);
}
// This is a lower priority request and we are swamped, so refuse it.
rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
// Check if the offline flag is set.
} else if (flags & RES_OFFLINE) {
LOG((" Offline request for host [%s]; ignoring.\n", host.get()));
rv = NS_ERROR_OFFLINE;
// We do not have a valid result till here.
// A/AAAA request can check for an alternative entry like AF_UNSPEC.
// Otherwise we need to start a new query.
} else if (!rec->mResolving) {
// If this is an IPV4 or IPV6 specific request, check if there is
// an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
if (addrRec && !(flags & RES_BYPASS_CACHE) &&
((af == PR_AF_INET) || (af == PR_AF_INET6))) {
// Check for an AF_UNSPEC entry.
const nsHostKey unspecKey(
host, aTrrServer, nsIDNSService::RESOLVE_TYPE_DEFAULT, flags,
PR_AF_UNSPEC, (aOriginAttributes.mPrivateBrowsingId > 0),
originSuffix);
RefPtr<nsHostRecord> unspecRec = mRecordDB.Get(unspecKey);
TimeStamp now = TimeStamp::NowLoRes();
if (unspecRec && unspecRec->HasUsableResult(now, flags)) {
MOZ_ASSERT(unspecRec->IsAddrRecord());
RefPtr<AddrHostRecord> addrUnspecRec = do_QueryObject(unspecRec);
MOZ_ASSERT(addrUnspecRec);
MOZ_ASSERT(addrUnspecRec->addr_info || addrUnspecRec->negative,
"Entry should be resolved or negative.");
LOG((" Trying AF_UNSPEC entry for host [%s] af: %s.\n", host.get(),
(af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
// We need to lock in case any other thread is reading
// addr_info.
MutexAutoLock lock(addrRec->addr_info_lock);
addrRec->addr_info = nullptr;
addrRec->addr_info_gencnt++;
if (unspecRec->negative) {
rec->negative = unspecRec->negative;
rec->CopyExpirationTimesAndFlagsFrom(unspecRec);
} else {
MutexAutoLock lock(addrUnspecRec->addr_info_lock);
if (addrUnspecRec->addr_info) {
// Search for any valid address in the AF_UNSPEC entry
// in the cache (not blocklisted and from the right
// family).
nsTArray<NetAddr> addresses;
for (const auto& addr : addrUnspecRec->addr_info->Addresses()) {
if ((af == addr.inet.family) &&
!addrUnspecRec->Blocklisted(&addr)) {
addresses.AppendElement(addr);
}
}
if (!addresses.IsEmpty()) {
addrRec->addr_info = new AddrInfo(
addrUnspecRec->addr_info->Hostname(),
addrUnspecRec->addr_info->CanonicalHostname(),
addrUnspecRec->addr_info->IsTRR(), std::move(addresses));
addrRec->addr_info_gencnt++;
rec->CopyExpirationTimesAndFlagsFrom(unspecRec);
}
}
}
// Now check if we have a new record.
if (rec->HasUsableResult(now, flags)) {
result = rec;
if (rec->negative) {
status = NS_ERROR_UNKNOWN_HOST;
}
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
ConditionallyRefreshRecord(rec, host);
} else if (af == PR_AF_INET6) {
// For AF_INET6, a new lookup means another AF_UNSPEC
// lookup. We have already iterated through the
// AF_UNSPEC addresses, so we mark this record as
// negative.
LOG(
(" No AF_INET6 in AF_UNSPEC entry: "
"host [%s] unknown host.",
host.get()));
result = rec;
rec->negative = true;
status = NS_ERROR_UNKNOWN_HOST;
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
METHOD_NEGATIVE_HIT);
}
}
}
// If this is a by-type request or if no valid record was found
// in the cache or this is an AF_UNSPEC request, then start a
// new lookup.
if (!result) {
LOG((" No usable record in cache for host [%s] type %d.", host.get(),
type));
if (flags & RES_REFRESH_CACHE) {
rec->Invalidate();
}
// Add callback to the list of pending callbacks.
rec->mCallbacks.insertBack(callback);
rec->flags = flags;
rv = NameLookup(rec);
if (IS_ADDR_TYPE(type)) {
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
METHOD_NETWORK_FIRST);
}
if (NS_FAILED(rv) && callback->isInList()) {
callback->remove();
} else {
LOG(
(" DNS lookup for host [%s] blocking "
"pending 'getaddrinfo' or trr query: "
"callback [%p]",
host.get(), callback.get()));
}
}
} else if (addrRec && addrRec->mDidCallbacks) {
// This is only for A/AAAA query.
// record is still pending more (TRR) data; make the callback
// at once
result = rec;
// make it count as a hit
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
LOG((" Host [%s] re-using early TRR resolve data\n", host.get()));
} else {
LOG(
(" Host [%s] is being resolved. Appending callback "
"[%p].",
host.get(), callback.get()));
// Add callback to the list of pending callbacks.
rec->mCallbacks.insertBack(callback);
// Only A/AAAA records are place in a queue. The queues are for
// the native resolver, therefore by-type request are never put
// into a queue.
if (addrRec && addrRec->onQueue) {
rec->flags = flags;
rv = NameLookup(rec);
if (IS_ADDR_TYPE(type)) {
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
METHOD_NETWORK_SHARED);
METHOD_NETWORK_FIRST);
}
if (NS_FAILED(rv) && callback->isInList()) {
callback->remove();
} else {
LOG(
(" DNS lookup for host [%s] blocking "
"pending 'getaddrinfo' or trr query: "
"callback [%p]",
host.get(), callback.get()));
}
}
// Consider the case where we are on a pending queue of
// lower priority than the request is being made at.
// In that case we should upgrade to the higher queue.
} else if (addrRec && addrRec->mDidCallbacks) {
// This is only for A/AAAA query.
// record is still pending more (TRR) data; make the callback
// at once
result = rec;
// make it count as a hit
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
if (IsHighPriority(flags) && !IsHighPriority(rec->flags)) {
// Move from (low|med) to high.
NS_ASSERTION(addrRec->onQueue,
"Moving Host Record Not Currently Queued");
rec->remove();
mHighQ.insertBack(rec);
rec->flags = flags;
ConditionallyCreateThread(rec);
} else if (IsMediumPriority(flags) && IsLowPriority(rec->flags)) {
// Move from low to med.
NS_ASSERTION(addrRec->onQueue,
"Moving Host Record Not Currently Queued");
rec->remove();
mMediumQ.insertBack(rec);
rec->flags = flags;
mIdleTaskCV.Notify();
}
LOG((" Host [%s] re-using early TRR resolve data\n", host.get()));
} else {
LOG(
(" Host [%s] is being resolved. Appending callback "
"[%p].",
host.get(), callback.get()));
rec->mCallbacks.insertBack(callback);
// Only A/AAAA records are place in a queue. The queues are for
// the native resolver, therefore by-type request are never put
// into a queue.
if (addrRec && addrRec->onQueue) {
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
METHOD_NETWORK_SHARED);
// Consider the case where we are on a pending queue of
// lower priority than the request is being made at.
// In that case we should upgrade to the higher queue.
if (IsHighPriority(flags) && !IsHighPriority(rec->flags)) {
// Move from (low|med) to high.
NS_ASSERTION(addrRec->onQueue,
"Moving Host Record Not Currently Queued");
rec->remove();
mHighQ.insertBack(rec);
rec->flags = flags;
ConditionallyCreateThread(rec);
} else if (IsMediumPriority(flags) && IsLowPriority(rec->flags)) {
// Move from low to med.
NS_ASSERTION(addrRec->onQueue,
"Moving Host Record Not Currently Queued");
rec->remove();
mMediumQ.insertBack(rec);
rec->flags = flags;
mIdleTaskCV.Notify();
}
}
}

Просмотреть файл

@ -494,6 +494,14 @@ class nsHostResolver : public nsISupports, public AHostResolver {
uint16_t flags, uint16_t af,
nsResolveHostCallback* callback);
nsHostRecord* InitRecord(const nsHostKey& key);
/**
* return a resolved hard coded loopback dns record for the specified key
*/
already_AddRefed<nsHostRecord> InitLoopbackRecord(const nsHostKey& key,
nsresult* aRv);
/**
* removes the specified callback from the nsHostRecord for the given
* hostname, originAttributes, flags, and address family. these parameters

Просмотреть файл

@ -15,6 +15,7 @@ XPCOMUtils.defineLazyGetter(this, "PORT", function() {
var srv;
function run_test() {
Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
srv = createServer();
srv.registerPathHandler("/raw-data", handleRawData);
@ -25,7 +26,10 @@ function run_test() {
srv.start(-1);
runRawTests(tests, testComplete(srv));
runRawTests(tests, function() {
Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
testComplete(srv)();
});
}
function checkException(fun, err, msg) {

Просмотреть файл

@ -98,6 +98,9 @@ function run_test() {
true
);
// We always resolve localhost as it's hardcoded without the following pref:
Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
let ioService = Cc["@mozilla.org/network/io-service;1"].getService(
Ci.nsIIOService
);
@ -112,6 +115,7 @@ function run_test() {
channel.open();
gServerSocket.init(-1, true, -1);
Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
run_next_test();
}

Просмотреть файл

@ -47,6 +47,8 @@ const defaultOriginAttributes = {};
function run_test() {
do_test_pending();
prefs.setBoolPref("network.dns.offline-localhost", false);
// We always resolve localhost as it's hardcoded without the following pref:
prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
ioService.offline = true;
try {
dns.asyncResolve(
@ -107,4 +109,5 @@ function test3Continued() {
function cleanup() {
prefs.clearUserPref("network.dns.offline-localhost");
prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
}

Просмотреть файл

@ -4,6 +4,9 @@ var dns = Cc["@mozilla.org/network/dns-service;1"].getService(Ci.nsIDNSService);
var threadManager = Cc["@mozilla.org/thread-manager;1"].getService(
Ci.nsIThreadManager
);
var prefs = Cc["@mozilla.org/preferences-service;1"].getService(
Ci.nsIPrefBranch
);
var mainThread = threadManager.currentThread;
var listener1 = {
@ -31,6 +34,7 @@ var listener2 = {
var listener3 = {
onLookupComplete(inRequest, inRecord, inStatus) {
Assert.equal(inStatus, Cr.NS_ERROR_OFFLINE);
cleanup();
do_test_finished();
},
};
@ -41,6 +45,7 @@ const secondOriginAttributes = { userContextId: 2 };
// First, we resolve the address normally for first originAttributes.
function run_test() {
do_test_pending();
prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
dns.asyncResolve(
"localhost",
Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT,
@ -84,6 +89,11 @@ function test3() {
);
} catch (e) {
Assert.equal(e.result, Cr.NS_ERROR_OFFLINE);
cleanup();
do_test_finished();
}
}
function cleanup() {
prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
}

Просмотреть файл

@ -71,6 +71,9 @@ function run_test() {
prefs.setBoolPref("network.http.http3.enabled", true);
prefs.setCharPref("network.dns.localDomains", "foo.example.com");
// We always resolve elements of localDomains as it's hardcoded without the
// following pref:
prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
// The certificate for the http3server server is for foo.example.com and
// is signed by http2-ca.pem so add that cert to the trust list as a
@ -553,6 +556,7 @@ function test_version_fallback() {
function testsDone() {
prefs.clearUserPref("network.http.http3.enabled");
prefs.clearUserPref("network.dns.localDomains");
prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
dump("testDone\n");
do_test_pending();
h1Server.stop(do_test_finished);

Просмотреть файл

@ -34,6 +34,9 @@ function run_test() {
prefs.setBoolPref("network.http.http3.enabled", true);
prefs.setCharPref("network.dns.localDomains", "foo.example.com");
// We always resolve elements of localDomains as it's hardcoded without the
// following pref:
prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
// The certificate for the http3server server is for foo.example.com and
// is signed by http2-ca.pem so add that cert to the trust list as a
@ -165,6 +168,7 @@ function test_response_421() {
function testsDone() {
prefs.clearUserPref("network.http.http3.enabled");
prefs.clearUserPref("network.dns.localDomains");
prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
dump("testDone\n");
do_test_pending();
do_test_finished();

Просмотреть файл

@ -36,6 +36,9 @@ function run_test() {
prefs.setBoolPref("network.http.http3.enabled", true);
prefs.setCharPref("network.dns.localDomains", "foo.example.com");
// We always resolve elements of localDomains as it's hardcoded without the
// following pref:
prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
// The certificate for the http3server server is for foo.example.com and
// is signed by http2-ca.pem so add that cert to the trust list as a
@ -121,5 +124,6 @@ function test_https_alt_svc() {
function testsDone() {
prefs.clearUserPref("network.http.http3.enabled");
prefs.clearUserPref("network.dns.localDomains");
prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
dump("testDone\n");
}

Просмотреть файл

@ -76,6 +76,9 @@ function run_test() {
prefs.setBoolPref("network.http.http3.enabled", true);
prefs.setCharPref("network.dns.localDomains", "foo.example.com");
// We always resolve elements of localDomains as it's hardcoded without the
// following pref:
prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
// The certificate for the http3server server is for foo.example.com and
// is signed by http2-ca.pem so add that cert to the trust list as a
@ -252,6 +255,7 @@ function test_download() {
function testsDone() {
prefs.clearUserPref("network.http.http3.enabled");
prefs.clearUserPref("network.dns.localDomains");
prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
dump("testDone\n");
do_test_pending();
do_test_finished();

Просмотреть файл

@ -58,9 +58,12 @@ function run_test() {
// disable network changed events to avoid the the risk of having the dns
// cache getting flushed behind our back
ps.setBoolPref("network.notify.changed", false);
// Localhost is hardcoded to loopback and isn't cached, disable that with this pref
ps.setBoolPref("network.proxy.allow_hijacking_localhost", true);
registerCleanupFunction(function() {
ps.clearUserPref("network.notify.changed");
ps.clearUserPref("network.proxy.allow_hijacking_localhost");
});
let serverSocket = Cc["@mozilla.org/network/server-socket;1"].createInstance(

Просмотреть файл

@ -201,8 +201,10 @@ skip-if = true # Bug 863738
[test_data_protocol.js]
[test_dns_service.js]
[test_dns_offline.js]
skip-if = socketprocess_networking # Bug 1640105
[test_dns_onion.js]
[test_dns_originAttributes.js]
skip-if = socketprocess_networking # Bug 1640105
[test_dns_localredirect.js]
[test_dns_proxy_bypass.js]
[test_dns_disabled.js]

Просмотреть файл

@ -3,7 +3,7 @@
if (os == "win") and ccov: [OK, TIMEOUT]
if (os == "win") and debug and not webrender and (processor == "x86"): ["OK", "TIMEOUT"]
if (os == "win") and debug and not webrender and (processor == "x86_64"): ["OK", "TIMEOUT"]
if (os == "win") and not debug and webrender and not fission: ["OK", "TIMEOUT"]
if (os == "win") and not debug and webrender: ["OK", "TIMEOUT"]
if (os == "win") and debug and webrender: ["TIMEOUT", "OK"]
[Initialize lineGapOverride with 'normal' should succeed]
expected: FAIL
@ -70,7 +70,7 @@
if (os == "win") and ccov: [PASS, NOTRUN]
if (os == "win") and debug and not webrender and (processor == "x86_64"): ["FAIL", "NOTRUN"]
if (os == "win") and debug and not webrender and (processor == "x86"): ["FAIL", "NOTRUN"]
if (os == "win") and not debug and webrender and not fission: ["FAIL", "TIMEOUT"]
if (os == "win") and not debug and webrender: ["FAIL", "TIMEOUT"]
if (os == "win") and debug and webrender: ["NOTRUN", "FAIL"]
FAIL