☠☠ backed out by b34436389d46 ☠ ☠ | |
author | Andrea Marchesini <amarchesini@mozilla.com> |
Fri, 10 Jul 2020 12:23:46 +0000 | |
changeset 539864 | 1b44f13206d03a1f4113a8d1472f87b7d1afd488 |
parent 539863 | f05d3259ce178f9042c3e26478f3a2bf2b2693a4 |
child 539865 | 57fd9fa7bf4984ec8d01407c2fcbbc2c3aecf421 |
push id | 37586 |
push user | ccoroiu@mozilla.com |
push date | Fri, 10 Jul 2020 16:06:24 +0000 |
treeherder | mozilla-central@4e9d6619c9d5 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | ckerschb, dragana |
bugs | 1220810 |
milestone | 80.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/base/test/unit/test_error_codes.js +++ b/dom/base/test/unit/test_error_codes.js @@ -33,27 +33,30 @@ function run_test() { // network offline function run_test_pt1() { try { Services.io.manageOfflineStatus = false; } 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; dump("Testing error returned by async XHR when the network is offline\n"); asyncXHR.load(); } // connection refused 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; dump("Testing error returned by aync XHR when the connection is refused\n"); asyncXHR.load(); } function end_test() {
--- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -1,12 +1,14 @@ [DEFAULT] tags = mtg webrtc subsuite = media scheme = https +prefs = + network.proxy.allow_hijacking_localhost=true support-files = head.js dataChannel.js mediaStreamPlayback.js network.js nonTrickleIce.js pc.js templates.js
--- a/dom/security/nsMixedContentBlocker.cpp +++ b/dom/security/nsMixedContentBlocker.cpp @@ -222,18 +222,17 @@ nsMixedContentBlocker::ShouldLoad(nsIURI nsILoadInfo::BLOCKING_REASON_MIXED_BLOCKED); } return rv; } bool nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost( const nsACString& aAsciiHost) { - if (aAsciiHost.EqualsLiteral("::1") || - aAsciiHost.EqualsLiteral("localhost")) { + if (mozilla::net::IsLoopbackHostname(aAsciiHost)) { return true; } PRNetAddr tempAddr; memset(&tempAddr, 0, sizeof(PRNetAddr)); if (PR_StringToNetAddr(PromiseFlatCString(aAsciiHost).get(), &tempAddr) != PR_SUCCESS) { @@ -243,19 +242,18 @@ bool nsMixedContentBlocker::IsPotentiall using namespace mozilla::net; NetAddr addr; PRNetAddrToNetAddr(&tempAddr, &addr); // Step 4 of // 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 IsIPAddrV4(&addr) && IsLoopBackAddress(&addr); + // trustworthy. + return IsLoopBackAddressWithoutIPv6Mapping(&addr); } bool nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(nsIURI* aURL) { nsAutoCString asciiHost; nsresult rv = aURL->GetAsciiHost(asciiHost); NS_ENSURE_SUCCESS(rv, false); return IsPotentiallyTrustworthyLoopbackHost(asciiHost); }
--- a/dom/security/nsMixedContentBlocker.h +++ b/dom/security/nsMixedContentBlocker.h @@ -29,16 +29,21 @@ enum MixedContentTypes { #include "nsIContentPolicy.h" #include "nsIChannel.h" #include "nsIChannelEventSink.h" #include "imgRequest.h" using mozilla::OriginAttributes; class nsILoadInfo; // forward declaration +namespace mozilla { +namespace net { +class nsProtocolProxyService; // forward declaration +} +} // namespace mozilla class nsMixedContentBlocker : public nsIContentPolicy, public nsIChannelEventSink { private: virtual ~nsMixedContentBlocker(); public: NS_DECL_ISUPPORTS
--- a/dom/security/test/gtest/TestSecureContext.cpp +++ b/dom/security/test/gtest/TestSecureContext.cpp @@ -19,32 +19,51 @@ using namespace mozilla; static const uint32_t kURIMaxLength = 64; struct TestExpectations { char uri[kURIMaxLength]; 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}, {"ws://example.com/", false}, {"wss://example.com/", true}, {"file:///xyzzy", true}, {"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}, {"http://128.0.0.1", false}, {"http://[::1]", true}, {"http://[::ffff:127.0.0.1]", false}, {"http://[::ffff:127.0.0.2]", false}, {"http://[::ffff:7f00:1]", false}, @@ -66,17 +85,18 @@ TEST(SecureContext, IsOriginPotentiallyT nsresult rv; for (uint32_t i = 0; i < numExpectations; i++) { nsCOMPtr<nsIPrincipal> prin; nsAutoCString uri(uris[i].uri); rv = nsScriptSecurityManager::GetScriptSecurityManager() ->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; } } TEST(SecureContext, IsOriginPotentiallyTrustworthyWithSystemPrincipal) { RefPtr<nsScriptSecurityManager> ssManager = nsScriptSecurityManager::GetScriptSecurityManager(); ASSERT_TRUE(!!ssManager);
--- a/dom/security/test/unit/test_isOriginPotentiallyTrustworthy.js +++ b/dom/security/test/unit/test_isOriginPotentiallyTrustworthy.js @@ -33,16 +33,17 @@ Services.prefs.setCharPref( Services.prefs.setBoolPref("dom.securecontext.whitelist_onions", false); add_task(async function test_isOriginPotentiallyTrustworthy() { for (let [uriSpec, expectedResult] of [ ["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], ["moz-extension://", true], ["wss://example.com/", true], ["about:config", false], ["http://example.net/", true], ["ws://example.org/", true],
--- a/dom/webauthn/tests/browser/browser_webauthn_ipaddress.js +++ b/dom/webauthn/tests/browser/browser_webauthn_ipaddress.js @@ -16,16 +16,17 @@ add_task(function test_setup() { Services.prefs.setBoolPref( "security.webauth.webauthn_enable_softtoken", true ); Services.prefs.setBoolPref( "security.webauth.webauthn_enable_usbtoken", false ); + Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true); }); registerCleanupFunction(async function() { Services.prefs.clearUserPref("security.webauth.u2f"); Services.prefs.clearUserPref("security.webauth.webauthn"); Services.prefs.clearUserPref( "security.webauth.webauthn_enable_android_fido2" );
--- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -7890,16 +7890,22 @@ mirror: always # Enables the predictive service. - name: network.predictor.enabled type: bool 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 type: bool value: false mirror: always - name: network.predictor.enable-hover-on-ssl
--- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1907,18 +1907,16 @@ pref("network.proxy.http", pref("network.proxy.http_port", 0); pref("network.proxy.ssl", ""); pref("network.proxy.ssl_port", 0); pref("network.proxy.socks", ""); 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 // The interval in seconds to move the cookies in the child process. // Set to 0 to disable moving the cookies. pref("network.cookie.move.interval_sec", 10); // This pref contains the list of hostnames (such as
--- a/netwerk/base/nsProtocolProxyService.cpp +++ b/netwerk/base/nsProtocolProxyService.cpp @@ -31,17 +31,19 @@ #include "prnetdb.h" #include "nsPACMan.h" #include "nsProxyRelease.h" #include "mozilla/Mutex.h" #include "mozilla/CondVar.h" #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" //---------------------------------------------------------------------------- namespace mozilla { namespace net { @@ -766,17 +768,16 @@ nsProtocolProxyService::nsProtocolProxyS mHTTPProxyPort(-1), mFTPProxyPort(-1), mHTTPSProxyPort(-1), mSOCKSProxyPort(-1), mSOCKSProxyVersion(4), mSOCKSProxyRemoteDNS(false), mProxyOverTLS(true), mWPADOverDHCPEnabled(false), - mAllowHijackingLocalhost(false), mPACMan(nullptr), mSessionStart(PR_Now()), mFailedProxyTimeout(30 * 60) // 30 minute default , mIsShutdown(false) {} nsProtocolProxyService::~nsProtocolProxyService() { // These should have been cleaned up in our Observe method. @@ -1014,21 +1015,16 @@ void nsProtocolProxyService::PrefsChange } if (!pref || !strcmp(pref, PROXY_PREF("enable_wpad_over_dhcp"))) { proxy_GetBoolPref(prefBranch, PROXY_PREF("enable_wpad_over_dhcp"), mWPADOverDHCPEnabled); 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); if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) { rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"), tempString); if (NS_SUCCEEDED(rv)) LoadHostFilters(tempString); } @@ -1092,19 +1088,22 @@ bool nsProtocolProxyService::CanUseProxy } else { NS_WARNING("unknown address family"); return true; // allow proxying } } // 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 } int32_t index = -1; while (++index < int32_t(mHostFiltersArray.Length())) { const auto& hinfo = mHostFiltersArray[index];
--- a/netwerk/base/nsProtocolProxyService.h +++ b/netwerk/base/nsProtocolProxyService.h @@ -383,17 +383,16 @@ class nsProtocolProxyService final : pub // mSOCKSProxyTarget could be a host, a domain socket path, // or a named-pipe name. nsCString mSOCKSProxyTarget; int32_t mSOCKSProxyPort; int32_t mSOCKSProxyVersion; bool mSOCKSProxyRemoteDNS; bool mProxyOverTLS; bool mWPADOverDHCPEnabled; - bool mAllowHijackingLocalhost; RefPtr<nsPACMan> mPACMan; // non-null if we are using PAC nsCOMPtr<nsISystemProxySettings> mSystemProxySettings; PRTime mSessionStart; nsFailedProxyTable mFailedProxies; int32_t mFailedProxyTimeout;
--- a/netwerk/dns/DNS.cpp +++ b/netwerk/dns/DNS.cpp @@ -1,19 +1,21 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=4 sw=2 sts=2 et cin: */ /* 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/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> #ifdef XP_WIN # include "ws2tcpip.h" #endif namespace mozilla { @@ -134,31 +136,56 @@ bool NetAddrToString(const NetAddr* addr memcpy(buf, addr->local.path, sizeof(addr->local.path)); return true; } #endif return false; } bool IsLoopBackAddress(const NetAddr* addr) { + if (IsLoopBackAddressWithoutIPv6Mapping(addr)) { + return true; + } + 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 IsLoopBackAddressWithoutIPv6Mapping(const NetAddr* addr) { 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; - } + + if (addr->raw.family == AF_INET6 && IPv6ADDR_IS_LOOPBACK(&addr->inet6.ip)) { + return true; } + + return false; +} + +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; + } + + nsAutoCString host; + nsContentUtils::ASCIIToLower(aAsciiHost, host); + + if (host.EqualsLiteral("localhost") || + StringEndsWith(host, NS_LITERAL_CSTRING(".localhost"))) { + return true; + } + return false; } bool IsIPAddrAny(const NetAddr* addr) { if (addr->raw.family == AF_INET) { if (addr->inet.ip == htonl(INADDR_ANY)) { return true; }
--- a/netwerk/dns/DNS.h +++ b/netwerk/dns/DNS.h @@ -177,16 +177,20 @@ void PRNetAddrToNetAddr(const PRNetAddr* // Copies the contents of a NetAddr to a PRNetAddr. // Does not do a ptr safety check! void NetAddrToPRNetAddr(const NetAddr* addr, PRNetAddr* prAddr); bool NetAddrToString(const NetAddr* addr, char* buf, uint32_t bufSize); bool IsLoopBackAddress(const NetAddr* addr); +bool IsLoopBackAddressWithoutIPv6Mapping(const NetAddr* addr); + +bool IsLoopbackHostname(const nsACString& aAsciiHost); + bool IsIPAddrAny(const NetAddr* addr); bool IsIPAddrV4(const NetAddr* addr); bool IsIPAddrV4Mapped(const NetAddr* addr); bool IsIPAddrLocal(const NetAddr* addr);
--- a/netwerk/dns/nsHostResolver.cpp +++ b/netwerk/dns/nsHostResolver.cpp @@ -840,21 +840,17 @@ nsresult nsHostResolver::GetHostRecord(c uint16_t af, bool pb, const nsCString& originSuffix, nsHostRecord** result) { MutexAutoLock lock(mLock); nsHostKey key(host, aTrrServer, type, flags, af, pb, originSuffix); 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; if (rec->IsAddrRecord()) { RefPtr<AddrHostRecord> addrRec = do_QueryObject(rec); if (addrRec->addr) { return NS_ERROR_FAILURE; } @@ -863,16 +859,58 @@ nsresult nsHostResolver::GetHostRecord(c if (rec->mResolving) { return NS_ERROR_FAILURE; } *result = rec.forget().take(); 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, const OriginAttributes& aOriginAttributes, uint16_t flags, uint16_t af, nsResolveHostCallback* aCallback) { nsAutoCString host(aHost); NS_ENSURE_TRUE(!host.IsEmpty(), NS_ERROR_UNEXPECTED); @@ -910,276 +948,284 @@ nsresult nsHostResolver::ResolveHost(con // if result is set inside the lock, then we need to issue the // callback before returning. RefPtr<nsHostRecord> result; nsresult status = NS_OK, rv = NS_OK; { 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; + } + + // 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 (gTRRService && gTRRService->IsExcludedFromTRR(host)) { + flags |= RES_DISABLE_TRR; + + 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; + } - if (gTRRService && gTRRService->IsExcludedFromTRR(host)) { - flags |= RES_DISABLE_TRR; + 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 (!aTrrServer.IsEmpty()) { - return NS_ERROR_UNKNOWN_HOST; + 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); + } + + // 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)) { + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, + METHOD_NEGATIVE_HIT); } + status = NS_ERROR_UNKNOWN_HOST; } - nsHostKey key(host, aTrrServer, type, flags, af, - (aOriginAttributes.mPrivateBrowsingId > 0), originSuffix); - RefPtr<nsHostRecord>& entry = mRecordDB.GetOrInsert(key); - if (!entry) { - if (IS_ADDR_TYPE(type)) { - entry = new AddrHostRecord(key); - } else { - entry = new TypeHostRecord(key); - } + // 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>(); + 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; - 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 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); - // 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); - } + 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); - // 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)) { + addrRec->addr_info = nullptr; + addrRec->addr_info_gencnt++; + if (unspecRec->negative) { + rec->negative = unspecRec->negative; + rec->CopyExpirationTimesAndFlagsFrom(unspecRec); + } else if (addrUnspecRec->addr_info) { + // Search for any valid address in the AF_UNSPEC entry + // in the cache (not blacklisted and from the right + // family). + NetAddrElement* addrIter = + addrUnspecRec->addr_info->mAddresses.getFirst(); + while (addrIter) { + if ((af == addrIter->mAddress.inet.family) && + !addrUnspecRec->Blacklisted(&addrIter->mAddress)) { + if (!addrRec->addr_info) { + addrRec->addr_info = + new AddrInfo(addrUnspecRec->addr_info->mHostName, + addrUnspecRec->addr_info->mCanonicalName, + addrUnspecRec->addr_info->IsTRR()); + addrRec->addr_info_gencnt++; + rec->CopyExpirationTimesAndFlagsFrom(unspecRec); + } + addrRec->addr_info->AddAddress(new NetAddrElement(*addrIter)); + } + addrIter = addrIter->getNext(); + } + } + // 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>(); - 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())); + // 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_OVERFLOW); + Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, + METHOD_NETWORK_FIRST); } - // This is a lower priority request and we are swamped, so refuse it. - rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + 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())); + } + } - // 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); + } 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); - addrRec->addr_info = nullptr; - addrRec->addr_info_gencnt++; - if (unspecRec->negative) { - rec->negative = unspecRec->negative; - rec->CopyExpirationTimesAndFlagsFrom(unspecRec); - } else if (addrUnspecRec->addr_info) { - // Search for any valid address in the AF_UNSPEC entry - // in the cache (not blacklisted and from the right - // family). - NetAddrElement* addrIter = - addrUnspecRec->addr_info->mAddresses.getFirst(); - while (addrIter) { - if ((af == addrIter->mAddress.inet.family) && - !addrUnspecRec->Blacklisted(&addrIter->mAddress)) { - if (!addrRec->addr_info) { - addrRec->addr_info = - new AddrInfo(addrUnspecRec->addr_info->mHostName, - addrUnspecRec->addr_info->mCanonicalName, - addrUnspecRec->addr_info->IsTRR()); - addrRec->addr_info_gencnt++; - rec->CopyExpirationTimesAndFlagsFrom(unspecRec); - } - addrRec->addr_info->AddAddress(new NetAddrElement(*addrIter)); - } - addrIter = addrIter->getNext(); - } - } - // 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); - } - } - } + 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())); - // 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)); + rec->mCallbacks.insertBack(callback); - if (flags & RES_REFRESH_CACHE) { - rec->Invalidate(); - } + // 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); - // 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); + // 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. - 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(); - } + 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(); } } } if (result && callback->isInList()) { callback->remove(); } } // lock
--- a/netwerk/dns/nsHostResolver.h +++ b/netwerk/dns/nsHostResolver.h @@ -440,16 +440,24 @@ class nsHostResolver : public nsISupport * having the callback implementation return without doing anything). */ nsresult ResolveHost(const nsACString& hostname, const nsACString& trrServer, uint16_t type, const mozilla::OriginAttributes& aOriginAttributes, 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 * should correspond to the parameters passed to ResolveHost. this function * executes the callback if the callback is still pending with the given * status. */ void DetachCallback(const nsACString& hostname, const nsACString& trrServer,
--- a/netwerk/test/unit/test_about_networking.js +++ b/netwerk/test/unit/test_about_networking.js @@ -93,25 +93,29 @@ add_test(function test_sockets() { }); function run_test() { Services.prefs.setBoolPref( "network.cookieJarSettings.unblocked_for_testing", 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 ); gHttpServer.start(-1); let uri = ioService.newURI( "http://localhost:" + gHttpServer.identity.primaryPort ); let channel = NetUtil.newChannel({ uri, loadUsingSystemPrincipal: true }); channel.open(); gServerSocket.init(-1, true, -1); + Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost"); run_next_test(); }
--- a/netwerk/test/unit/test_dns_offline.js +++ b/netwerk/test/unit/test_dns_offline.js @@ -40,16 +40,18 @@ var listener3 = { }, }; 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( "localhost", 0, listener1, mainThread, defaultOriginAttributes @@ -94,9 +96,10 @@ function test3Continued() { listener3, mainThread, defaultOriginAttributes ); } function cleanup() { prefs.clearUserPref("network.dns.offline-localhost"); + prefs.clearUserPref("network.proxy.allow_hijacking_localhost"); }
--- a/netwerk/test/unit/test_dns_originAttributes.js +++ b/netwerk/test/unit/test_dns_originAttributes.js @@ -1,14 +1,17 @@ "use strict"; 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 = { onLookupComplete(inRequest, inRecord, inStatus) { Assert.equal(inStatus, Cr.NS_OK); var answer = inRecord.getNextAddrAsString(); Assert.ok(answer == "127.0.0.1" || answer == "::1"); test2(); @@ -61,21 +64,23 @@ function test2() { ); } // Third, we resolve the same address offline again with different originAttributes. // This resolving should fail since the DNS cache of the given address is not exist // for this originAttributes. function test3() { do_test_pending(); + prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true); try { dns.asyncResolve( "localhost", Ci.nsIDNSService.RESOLVE_OFFLINE, listener3, mainThread, secondOriginAttributes ); } catch (e) { Assert.equal(e.result, Cr.NS_ERROR_OFFLINE); + prefs.clearUserPref("network.proxy.allow_hijacking_localhost"); do_test_finished(); } }
--- a/netwerk/test/unit/test_ping_aboutnetworking.js +++ b/netwerk/test/unit/test_ping_aboutnetworking.js @@ -53,16 +53,18 @@ function test_sockets(serverSocket) { function run_test() { var ps = Cc["@mozilla.org/preferences-service;1"].getService( Ci.nsIPrefBranch ); // 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"); }); let serverSocket = Cc["@mozilla.org/network/server-socket;1"].createInstance( Ci.nsIServerSocket );
--- a/netwerk/test/unit/test_trr.js +++ b/netwerk/test/unit/test_trr.js @@ -1005,25 +1005,27 @@ add_task(async function test24k() { ); await new DNSListener("bar.example.com", "127.0.0.1"); }); // TRR-only that resolving localhost with TRR-only mode will use the remote // resolver if it's not in the excluded domains add_task(async function test25() { dns.clearCache(true); + Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true); // Disable localhost hardcoding Services.prefs.setIntPref("network.trr.mode", 3); // TRR-only Services.prefs.setCharPref("network.trr.excluded-domains", ""); Services.prefs.setCharPref("network.trr.builtin-excluded-domains", ""); Services.prefs.setCharPref( "network.trr.uri", `https://foo.example.com:${h2Port}/doh?responseIP=192.192.192.192` ); await new DNSListener("localhost", "192.192.192.192", true); + Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost"); }); // TRR-only check that localhost goes directly to native lookup when in the excluded-domains add_task(async function test25b() { dns.clearCache(true); Services.prefs.setIntPref("network.trr.mode", 3); // TRR-only Services.prefs.setCharPref("network.trr.excluded-domains", "localhost"); Services.prefs.setCharPref(