diff --git a/browser/components/loop/test/xpcshell/test_loopservice_hawk_errors.js b/browser/components/loop/test/xpcshell/test_loopservice_hawk_errors.js index 2dd215e3afdf..1ad6cff88fd2 100644 --- a/browser/components/loop/test/xpcshell/test_loopservice_hawk_errors.js +++ b/browser/components/loop/test/xpcshell/test_loopservice_hawk_errors.js @@ -37,6 +37,7 @@ add_task(function* setup_server() { add_task(function* error_offline() { Services.io.offline = true; + Services.prefs.setBoolPref("network.dns.offline-localhost", false); yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/offline", "GET").then( () => Assert.ok(false, "Should have rejected"), (error) => { @@ -179,6 +180,7 @@ function run_test() { Services.prefs.clearUserPref("loop.hawk-session-token"); Services.prefs.clearUserPref("loop.hawk-session-token.fxa"); Services.prefs.clearUserPref("loop.urlsExpiryTimeSeconds"); + Services.prefs.clearUserPref("network.dns.offline-localhost"); MozLoopService.errors.clear(); }); diff --git a/dom/base/test/unit/test_error_codes.js b/dom/base/test/unit/test_error_codes.js index 737241a75174..c4f488f18920 100644 --- a/dom/base/test/unit/test_error_codes.js +++ b/dom/base/test/unit/test_error_codes.js @@ -6,6 +6,9 @@ var gExpectedStatus = null; var gNextTestFunc = null; +var prefs = Components.classes["@mozilla.org/preferences-service;1"]. + getService(Components.interfaces.nsIPrefBranch); + var asyncXHR = { load: function() { var request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] @@ -39,6 +42,7 @@ function run_test_pt1() { catch (e) { } ioService.offline = true; + prefs.setBoolPref("network.dns.offline-localhost", false); gExpectedStatus = Components.results.NS_ERROR_OFFLINE; gNextTestFunc = run_test_pt2; @@ -51,6 +55,7 @@ function run_test_pt2() { var ioService = Components.classes["@mozilla.org/network/io-service;1"] .getService(Components.interfaces.nsIIOService); ioService.offline = false; + prefs.clearUserPref("network.dns.offline-localhost"); gExpectedStatus = Components.results.NS_ERROR_CONNECTION_REFUSED; gNextTestFunc = end_test; diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index dac73b27515f..58addcd71036 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1589,6 +1589,9 @@ pref("network.dnsCacheExpirationGracePeriod", 60); // This preference can be used to turn off DNS prefetch. pref("network.dns.disablePrefetch", false); +// Contols whether or not "localhost" should resolve when offline +pref("network.dns.offline-localhost", true); + // This preference controls whether or not URLs with UTF-8 characters are // escaped. Set this preference to TRUE for strict RFC2396 conformance. pref("network.standard-url.escape-utf8", true); diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp index ce43a68eaec3..e51baa75983b 100644 --- a/netwerk/dns/nsDNSService2.cpp +++ b/netwerk/dns/nsDNSService2.cpp @@ -50,6 +50,7 @@ static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains"; static const char kPrefDisableIPv6[] = "network.dns.disableIPv6"; static const char kPrefDisablePrefetch[] = "network.dns.disablePrefetch"; static const char kPrefDnsLocalDomains[] = "network.dns.localDomains"; +static const char kPrefDnsOfflineLocalhost[] = "network.dns.offline-localhost"; static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution"; //----------------------------------------------------------------------------- @@ -535,12 +536,12 @@ nsDNSService::Init() if (mResolver) return NS_OK; NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED); - // prefs uint32_t maxCacheEntries = 400; uint32_t defaultCacheLifetime = 120; // seconds uint32_t defaultGracePeriod = 60; // seconds bool disableIPv6 = false; + bool offlineLocalhost = true; bool disablePrefetch = false; int proxyType = nsIProtocolProxyService::PROXYCONFIG_DIRECT; bool notifyResolution = false; @@ -563,6 +564,7 @@ nsDNSService::Init() prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6); prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains)); prefs->GetCharPref(kPrefDnsLocalDomains, getter_Copies(localDomains)); + prefs->GetBoolPref(kPrefDnsOfflineLocalhost, &offlineLocalhost); prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch); // If a manual proxy is in use, disable prefetch implicitly @@ -581,6 +583,7 @@ nsDNSService::Init() prefs->AddObserver(kPrefIPv4OnlyDomains, this, false); prefs->AddObserver(kPrefDnsLocalDomains, this, false); prefs->AddObserver(kPrefDisableIPv6, this, false); + prefs->AddObserver(kPrefDnsOfflineLocalhost, this, false); prefs->AddObserver(kPrefDisablePrefetch, this, false); prefs->AddObserver(kPrefDnsNotifyResolution, this, false); @@ -621,6 +624,7 @@ nsDNSService::Init() mResolver = res; mIDN = idn; mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership + mOfflineLocalhost = offlineLocalhost; mDisableIPv6 = disableIPv6; // Disable prefetching either by explicit preference or if a manual proxy is configured @@ -753,13 +757,15 @@ nsDNSService::AsyncResolveExtended(const nsACString &aHostname, if (!res) return NS_ERROR_OFFLINE; - if (mOffline) - flags |= RESOLVE_OFFLINE; - nsCString hostname; if (!PreprocessHostname(localDomain, aHostname, idn, hostname)) return NS_ERROR_FAILURE; + if (mOffline && + (!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) { + flags |= RESOLVE_OFFLINE; + } + // make sure JS callers get notification on the main thread nsCOMPtr wrappedListener = do_QueryInterface(listener); if (wrappedListener && !target) { @@ -867,13 +873,15 @@ nsDNSService::Resolve(const nsACString &aHostname, NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE); - if (mOffline) - flags |= RESOLVE_OFFLINE; - nsCString hostname; if (!PreprocessHostname(localDomain, aHostname, idn, hostname)) return NS_ERROR_FAILURE; + if (mOffline && + (!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) { + flags |= RESOLVE_OFFLINE; + } + // // sync resolve: since the host resolver only works asynchronously, we need // to use a mutex and a condvar to wait for the result. however, since the diff --git a/netwerk/dns/nsDNSService2.h b/netwerk/dns/nsDNSService2.h index 3db214d0e22f..1187ba147a4d 100644 --- a/netwerk/dns/nsDNSService2.h +++ b/netwerk/dns/nsDNSService2.h @@ -60,6 +60,7 @@ private: bool mFirstTime; bool mOffline; bool mNotifyResolution; + bool mOfflineLocalhost; nsMainThreadPtrHandle mObserverService; nsTHashtable mLocalDomains; }; diff --git a/netwerk/test/unit/test_dns_offline.js b/netwerk/test/unit/test_dns_offline.js new file mode 100644 index 000000000000..87a9ad8b1e30 --- /dev/null +++ b/netwerk/test/unit/test_dns_offline.js @@ -0,0 +1,74 @@ +var dns = Cc["@mozilla.org/network/dns-service;1"].getService(Ci.nsIDNSService); +var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); +var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); +var threadManager = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); +var mainThread = threadManager.currentThread; + +var listener1 = { + onLookupComplete: function(inRequest, inRecord, inStatus) { + do_check_eq(inStatus, Cr.NS_ERROR_OFFLINE); + test2(); + do_test_finished(); + } +}; + +var listener2 = { + onLookupComplete: function(inRequest, inRecord, inStatus) { + do_check_eq(inStatus, Cr.NS_OK); + var answer = inRecord.getNextAddrAsString(); + do_check_true(answer == "127.0.0.1" || answer == "::1"); + test3(); + do_test_finished(); + } +}; + +var listener3 = { + onLookupComplete: function(inRequest, inRecord, inStatus) { + do_check_eq(inStatus, Cr.NS_OK); + var answer = inRecord.getNextAddrAsString(); + do_check_true(answer == "127.0.0.1" || answer == "::1"); + cleanup(); + do_test_finished(); + } +}; + +function run_test() { + do_test_pending(); + prefs.setBoolPref("network.dns.offline-localhost", false); + ioService.offline = true; + try { + dns.asyncResolve("localhost", 0, listener1, mainThread); + } catch (e) { + do_check_eq(e.result, Cr.NS_ERROR_OFFLINE); + test2(); + do_test_finished(); + } +} + +function test2() { + do_test_pending(); + prefs.setBoolPref("network.dns.offline-localhost", true); + ioService.offline = false; + ioService.offline = true; + // we need to let the main thread run and apply the changes + do_timeout(0, test2Continued); +} + +function test2Continued() { + dns.asyncResolve("localhost", 0, listener2, mainThread); +} + +function test3() { + do_test_pending(); + ioService.offline = false; + // we need to let the main thread run and apply the changes + do_timeout(0, test3Continued); +} + +function test3Continued() { + dns.asyncResolve("localhost", 0, listener3, mainThread); +} + +function cleanup() { + prefs.clearUserPref("network.dns.offline-localhost"); +} diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 525a6b4a4834..40eea60a3603 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -176,6 +176,7 @@ skip-if = bits != 32 [test_dns_per_interface.js] [test_data_protocol.js] [test_dns_service.js] +[test_dns_offline.js] [test_dns_localredirect.js] [test_dns_proxy_bypass.js] [test_duplicate_headers.js] diff --git a/services/sync/tests/unit/test_errorhandler_sync_checkServerError.js b/services/sync/tests/unit/test_errorhandler_sync_checkServerError.js index 18cea2ccee99..6a3882b5cb74 100644 --- a/services/sync/tests/unit/test_errorhandler_sync_checkServerError.js +++ b/services/sync/tests/unit/test_errorhandler_sync_checkServerError.js @@ -200,6 +200,7 @@ add_identity_test(this, function test_service_offline() { let deferred = Promise.defer(); server.stop(() => { Services.io.offline = true; + Services.prefs.setBoolPref("network.dns.offline-localhost", false); try { do_check_eq(Status.sync, SYNC_SUCCEEDED); @@ -214,6 +215,7 @@ add_identity_test(this, function test_service_offline() { Service.startOver(); } Services.io.offline = false; + Services.prefs.clearUserPref("network.dns.offline-localhost"); deferred.resolve(); }); yield deferred.promise;