From c4ff495718e7b190229e863e3387c4e5f99475a9 Mon Sep 17 00:00:00 2001 From: "pkasting@chromium.org" Date: Fri, 8 Jan 2010 19:12:47 +0000 Subject: Add autodetection of "intranet" redirection, for ISPs etc. that send typos and nonexistent addresses to custom pages, and plumb it to the code that puts up infobars when users type in a search that appears to be an intranet address, so we don't show these for erroneous cases. BUG=31556 TEST=none Review URL: http://codereview.chromium.org/525079 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@35807 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/alternate_nav_url_fetcher.cc | 76 +++++---- chrome/browser/alternate_nav_url_fetcher.h | 8 +- chrome/browser/browser_main.cc | 20 +-- chrome/browser/browser_prefs.cc | 4 +- chrome/browser/browser_process.h | 4 +- chrome/browser/browser_process_impl.cc | 20 ++- chrome/browser/browser_process_impl.h | 11 +- .../browser/extensions/cross_origin_xhr_apitest.cc | 3 +- .../browser/extensions/execute_script_apitest.cc | 3 +- .../extensions/extension_history_apitest.cc | 3 +- .../extensions/extension_javascript_url_apitest.cc | 3 +- .../extensions/incognito_noscript_apitest.cc | 7 +- chrome/browser/first_run.h | 10 +- chrome/browser/intranet_redirect_detector.cc | 171 +++++++++++++++++++++ chrome/browser/intranet_redirect_detector.h | 106 +++++++++++++ chrome/browser/net/cookie_policy_browsertest.cc | 3 +- .../blacklist_manager_browsertest.cc | 3 +- chrome/chrome_browser.gypi | 2 + chrome/common/pref_names.cc | 6 +- chrome/common/pref_names.h | 3 +- chrome/test/in_process_browser_test.cc | 10 +- chrome/test/in_process_browser_test.h | 4 +- chrome/test/live_sync/live_bookmarks_sync_test.cc | 9 +- chrome/test/live_sync/live_bookmarks_sync_test.h | 9 +- chrome/test/testing_browser_process.h | 6 +- net/base/host_resolver_proc.cc | 28 +++- net/base/host_resolver_proc.h | 18 ++- net/base/mock_host_resolver.cc | 11 +- net/base/mock_host_resolver.h | 12 +- 29 files changed, 479 insertions(+), 94 deletions(-) create mode 100644 chrome/browser/intranet_redirect_detector.cc create mode 100644 chrome/browser/intranet_redirect_detector.h diff --git a/chrome/browser/alternate_nav_url_fetcher.cc b/chrome/browser/alternate_nav_url_fetcher.cc index 7523f66..ee8c7b6 100644 --- a/chrome/browser/alternate_nav_url_fetcher.cc +++ b/chrome/browser/alternate_nav_url_fetcher.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,6 +6,7 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" +#include "chrome/browser/intranet_redirect_detector.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/navigation_controller.h" #include "chrome/browser/tab_contents/navigation_entry.h" @@ -82,37 +83,7 @@ void AlternateNavURLFetcher::OnURLFetchComplete(const URLFetcher* source, const ResponseCookies& cookies, const std::string& data) { DCHECK(fetcher_.get() == source); - if (status.is_success() && - // HTTP 2xx, 401, and 407 all indicate that the target address exists. - (((response_code / 100) == 2) || - (response_code == 401) || (response_code == 407))) { - state_ = SUCCEEDED; - - // The following TLD+1s are used as destinations by ISPs/DNS providers/etc. - // who return provider-controlled pages to arbitrary user navigation - // attempts. Because this can result in infobars on large fractions of user - // searches, we don't show automatic infobars for these. Note that users - // can still choose to explicitly navigate to or search for pages in these - // domains, and can still get infobars for cases that wind up on other - // domains (e.g. legit intranet sites), we're just trying to avoid - // erroneously harassing the user with our own UI prompts. - const char* kBlacklistedSites[] = { - // NOTE: Use complete URLs, because GURL() doesn't do fixup! - "http://comcast.com/", - "http://opendns.com/", - "http://verizon.net/", - }; - for (size_t i = 0; i < arraysize(kBlacklistedSites); ++i) { - if (net::RegistryControlledDomainService::SameDomainOrHost( - url, GURL(kBlacklistedSites[i]))) { - state_ = FAILED; - break; - } - } - } else { - state_ = FAILED; - } - + SetStatusFromURLFetch(url, status, response_code); ShowInfobarIfPossible(); } @@ -150,6 +121,47 @@ void AlternateNavURLFetcher::InfoBarClosed() { delete this; } +void AlternateNavURLFetcher::SetStatusFromURLFetch( + const GURL& url, + const URLRequestStatus& status, + int response_code) { + if (!status.is_success() || + // HTTP 2xx, 401, and 407 all indicate that the target address exists. + (((response_code / 100) != 2) && + (response_code != 401) && (response_code != 407)) || + // Fail if we're redirected to a common location. This is the "automatic + // heuristic" version of the explicit blacklist below; see comments there. + net::RegistryControlledDomainService::SameDomainOrHost(url, + IntranetRedirectDetector::RedirectOrigin())) { + state_ = FAILED; + return; + } + + // The following TLD+1s are used as destinations by ISPs/DNS providers/etc. + // who return provider-controlled pages to arbitrary user navigation attempts. + // Because this can result in infobars on large fractions of user searches, we + // don't show automatic infobars for these. Note that users can still choose + // to explicitly navigate to or search for pages in these domains, and can + // still get infobars for cases that wind up on other domains (e.g. legit + // intranet sites), we're just trying to avoid erroneously harassing the user + // with our own UI prompts. + const char* kBlacklistedSites[] = { + // NOTE: Use complete URLs, because GURL() doesn't do fixup! + "http://comcast.com/", + "http://opendns.com/", + "http://verizon.net/", + }; + for (size_t i = 0; i < arraysize(kBlacklistedSites); ++i) { + if (net::RegistryControlledDomainService::SameDomainOrHost(url, + GURL(kBlacklistedSites[i]))) { + state_ = FAILED; + return; + } + } + + state_ = SUCCEEDED; +} + void AlternateNavURLFetcher::ShowInfobarIfPossible() { if (!navigated_to_entry_ || state_ != SUCCEEDED) { if (state_ == FAILED) diff --git a/chrome/browser/alternate_nav_url_fetcher.h b/chrome/browser/alternate_nav_url_fetcher.h index 40a92f2..943f23e 100644 --- a/chrome/browser/alternate_nav_url_fetcher.h +++ b/chrome/browser/alternate_nav_url_fetcher.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -62,6 +62,12 @@ class AlternateNavURLFetcher : public NotificationObserver, virtual void InfoBarClosed(); private: + // Sets |state_| to either SUCCEEDED or FAILED depending on the result of the + // fetch. + void SetStatusFromURLFetch(const GURL& url, + const URLRequestStatus& status, + int response_code); + // Displays the infobar if all conditions are met (the page has loaded and // the fetch of the alternate URL succeeded). void ShowInfobarIfPossible(); diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index c5e4484..995f625 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -797,19 +797,19 @@ int BrowserMain(const MainFunctionParams& parameters) { RegisterMetadataURLRequestHandler(); // In unittest mode, this will do nothing. In normal mode, this will create - // the global GoogleURLTracker instance, which will promptly go to sleep for - // five seconds (to avoid slowing startup), and wake up afterwards to see if - // it should do anything else. If we don't cause this creation now, it won't - // happen until someone else asks for the tracker, at which point we may no - // longer want to sleep for five seconds. + // the global GoogleURLTracker and IntranetRedirectDetector instances, which + // will promptly go to sleep for five and seven seconds, respectively (to + // avoid slowing startup), and wake up afterwards to see if they should do + // anything else. // // A simpler way of doing all this would be to have some function which could - // give the time elapsed since startup, and simply have the tracker check that - // when asked to initialize itself, but this doesn't seem to exist. + // give the time elapsed since startup, and simply have these objects check + // that when asked to initialize themselves, but this doesn't seem to exist. // - // This can't be created in the BrowserProcessImpl constructor because it - // needs to read prefs that get set after that runs. + // These can't be created in the BrowserProcessImpl constructor because they + // need to read prefs that get set after that runs. browser_process->google_url_tracker(); + browser_process->intranet_redirect_detector(); // Have Chrome plugins write their data to the profile directory. PluginService::GetInstance()->SetChromePluginDataDir(profile->GetPath()); diff --git a/chrome/browser/browser_prefs.cc b/chrome/browser/browser_prefs.cc index ae3abf9..300f762 100644 --- a/chrome/browser/browser_prefs.cc +++ b/chrome/browser/browser_prefs.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -17,6 +17,7 @@ #include "chrome/browser/form_field_history_manager.h" #include "chrome/browser/google_url_tracker.h" #include "chrome/browser/host_zoom_map.h" +#include "chrome/browser/intranet_redirect_detector.h" #include "chrome/browser/metrics/metrics_service.h" #include "chrome/browser/net/dns_global.h" #include "chrome/browser/page_info_model.h" @@ -63,6 +64,7 @@ void RegisterLocalState(PrefService* local_state) { WebCacheManager::RegisterPrefs(local_state); ExternalProtocolHandler::RegisterPrefs(local_state); GoogleURLTracker::RegisterPrefs(local_state); + IntranetRedirectDetector::RegisterPrefs(local_state); KeywordEditorController::RegisterPrefs(local_state); MetricsLog::RegisterPrefs(local_state); MetricsService::RegisterPrefs(local_state); diff --git a/chrome/browser/browser_process.h b/chrome/browser/browser_process.h index d826321..d1ba1a3 100644 --- a/chrome/browser/browser_process.h +++ b/chrome/browser/browser_process.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -21,6 +21,7 @@ class Clipboard; class DevToolsManager; class DownloadRequestManager; class GoogleURLTracker; +class IntranetRedirectDetector; class IconManager; class MetricsService; class NotificationUIManager; @@ -122,6 +123,7 @@ class BrowserProcess { virtual printing::PrintJobManager* print_job_manager() = 0; virtual GoogleURLTracker* google_url_tracker() = 0; + virtual IntranetRedirectDetector* intranet_redirect_detector() = 0; // Returns the locale used by the application. virtual const std::string& GetApplicationLocale() = 0; diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc index f19e6a6..481d210 100644 --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -20,6 +20,7 @@ #include "chrome/browser/download/save_file_manager.h" #include "chrome/browser/google_url_tracker.h" #include "chrome/browser/icon_manager.h" +#include "chrome/browser/intranet_redirect_detector.h" #include "chrome/browser/metrics/metrics_service.h" #include "chrome/browser/net/dns_global.h" #include "chrome/browser/net/sdch_dictionary_fetcher.h" @@ -182,12 +183,14 @@ BrowserProcessImpl::~BrowserProcessImpl() { // any pending URLFetchers, and avoid creating any more. SdchDictionaryFetcher::Shutdown(); - // We need to destroy the MetricsService and GoogleURLTracker before the - // io_thread_ gets destroyed, since both destructors can call the URLFetcher - // destructor, which does an PostDelayedTask operation on the IO thread. (The - // IO thread will handle that URLFetcher operation before going away.) + // We need to destroy the MetricsService, GoogleURLTracker, and + // IntranetRedirectDetector before the io_thread_ gets destroyed, since their + // destructors can call the URLFetcher destructor, which does a + // PostDelayedTask operation on the IO thread. (The IO thread will handle + // that URLFetcher operation before going away.) metrics_service_.reset(); google_url_tracker_.reset(); + intranet_redirect_detector_.reset(); // Need to clear profiles (download managers) before the io_thread_. profile_manager_.reset(); @@ -461,6 +464,13 @@ void BrowserProcessImpl::CreateGoogleURLTracker() { google_url_tracker_.swap(google_url_tracker); } +void BrowserProcessImpl::CreateIntranetRedirectDetector() { + DCHECK(intranet_redirect_detector_.get() == NULL); + scoped_ptr intranet_redirect_detector( + new IntranetRedirectDetector); + intranet_redirect_detector_.swap(intranet_redirect_detector); +} + void BrowserProcessImpl::CreateNotificationUIManager() { DCHECK(notification_ui_manager_.get() == NULL); notification_ui_manager_.reset(NotificationUIManager::Create()); diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h index 51d0cf5..2c815ee 100644 --- a/chrome/browser/browser_process_impl.h +++ b/chrome/browser/browser_process_impl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -182,6 +182,13 @@ class BrowserProcessImpl : public BrowserProcess, public NonThreadSafe { return google_url_tracker_.get(); } + virtual IntranetRedirectDetector* intranet_redirect_detector() { + DCHECK(CalledOnValidThread()); + if (!intranet_redirect_detector_.get()) + CreateIntranetRedirectDetector(); + return intranet_redirect_detector_.get(); + } + virtual const std::string& GetApplicationLocale() { DCHECK(!locale_.empty()); return locale_; @@ -225,6 +232,7 @@ class BrowserProcessImpl : public BrowserProcess, public NonThreadSafe { void CreateDebuggerWrapper(int port); void CreateDevToolsManager(); void CreateGoogleURLTracker(); + void CreateIntranetRedirectDetector(); void CreateNotificationUIManager(); #if defined(IPC_MESSAGE_LOG_ENABLED) @@ -282,6 +290,7 @@ class BrowserProcessImpl : public BrowserProcess, public NonThreadSafe { scoped_ptr automation_provider_list_; scoped_ptr google_url_tracker_; + scoped_ptr intranet_redirect_detector_; scoped_ptr main_notification_service_; diff --git a/chrome/browser/extensions/cross_origin_xhr_apitest.cc b/chrome/browser/extensions/cross_origin_xhr_apitest.cc index 5a98322..c43c612 100644 --- a/chrome/browser/extensions/cross_origin_xhr_apitest.cc +++ b/chrome/browser/extensions/cross_origin_xhr_apitest.cc @@ -1,8 +1,9 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/extensions/extension_apitest.h" +#include "net/base/mock_host_resolver.h" IN_PROC_BROWSER_TEST_F(ExtensionApiTest, CrossOriginXHR) { host_resolver()->AddRule("*.com", "127.0.0.1"); diff --git a/chrome/browser/extensions/execute_script_apitest.cc b/chrome/browser/extensions/execute_script_apitest.cc index 07662b6c..4c7aa87 100644 --- a/chrome/browser/extensions/execute_script_apitest.cc +++ b/chrome/browser/extensions/execute_script_apitest.cc @@ -1,8 +1,9 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 20109 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/extensions/extension_apitest.h" +#include "net/base/mock_host_resolver.h" // This test failed at times on the Vista dbg builder and has been marked as // flaky for now. Bug http://code.google.com/p/chromium/issues/detail?id=28630 diff --git a/chrome/browser/extensions/extension_history_apitest.cc b/chrome/browser/extensions/extension_history_apitest.cc index 910012f..94873b3 100644 --- a/chrome/browser/extensions/extension_history_apitest.cc +++ b/chrome/browser/extensions/extension_history_apitest.cc @@ -1,10 +1,11 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/command_line.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/common/chrome_switches.h" +#include "net/base/mock_host_resolver.h" // Flaky, http://crbug.com/26296. IN_PROC_BROWSER_TEST_F(ExtensionApiTest, DISABLED_History) { diff --git a/chrome/browser/extensions/extension_javascript_url_apitest.cc b/chrome/browser/extensions/extension_javascript_url_apitest.cc index aaa7b30..657f82d 100644 --- a/chrome/browser/extensions/extension_javascript_url_apitest.cc +++ b/chrome/browser/extensions/extension_javascript_url_apitest.cc @@ -1,8 +1,9 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/extensions/extension_apitest.h" +#include "net/base/mock_host_resolver.h" IN_PROC_BROWSER_TEST_F(ExtensionApiTest, JavaScriptURLPermissions) { host_resolver()->AddRule("a.com", "127.0.0.1"); diff --git a/chrome/browser/extensions/incognito_noscript_apitest.cc b/chrome/browser/extensions/incognito_noscript_apitest.cc index 69f3876..fa78e67 100755 --- a/chrome/browser/extensions/incognito_noscript_apitest.cc +++ b/chrome/browser/extensions/incognito_noscript_apitest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,6 +10,7 @@ #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/url_constants.h" #include "chrome/test/ui_test_utils.h" +#include "net/base/mock_host_resolver.h" IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, IncognitoNoScript) { host_resolver()->AddRule("*", "127.0.0.1"); @@ -37,8 +38,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, IncognitoNoScript) { NULL); otr_browser->window()->Show(); ui_test_utils::WaitForNavigationInCurrentTab(otr_browser); - - string16 title; + + string16 title; ui_test_utils::GetCurrentTabTitle(otr_browser, &title); ASSERT_EQ("Unmodified", UTF16ToASCII(title)); } diff --git a/chrome/browser/first_run.h b/chrome/browser/first_run.h index 6b8a9d39..3b2d996 100644 --- a/chrome/browser/first_run.h +++ b/chrome/browser/first_run.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -136,8 +136,9 @@ class Upgrade { }; #endif -// A subclass of BrowserProcessImpl that does not have a GoogleURLTracker -// so we don't fetch as we have no IO thread (see bug #1292702). +// A subclass of BrowserProcessImpl that does not have a GoogleURLTracker or +// IntranetRedirectDetector so we don't do any URL fetches (as we have no IO +// thread to fetch on). class FirstRunBrowserProcess : public BrowserProcessImpl { public: explicit FirstRunBrowserProcess(const CommandLine& command_line) @@ -146,6 +147,9 @@ class FirstRunBrowserProcess : public BrowserProcessImpl { virtual ~FirstRunBrowserProcess() { } virtual GoogleURLTracker* google_url_tracker() { return NULL; } + virtual IntranetRedirectDetector* intranet_redirect_detector() { + return NULL; + } private: DISALLOW_COPY_AND_ASSIGN(FirstRunBrowserProcess); diff --git a/chrome/browser/intranet_redirect_detector.cc b/chrome/browser/intranet_redirect_detector.cc new file mode 100644 index 0000000..04b0a5a --- /dev/null +++ b/chrome/browser/intranet_redirect_detector.cc @@ -0,0 +1,171 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/intranet_redirect_detector.h" + +#include "base/rand_util.h" +#include "base/stl_util-inl.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profile.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/pref_service.h" +#include "net/base/load_flags.h" +#include "net/base/net_errors.h" +#include "net/base/registry_controlled_domain.h" +#include "net/url_request/url_request_status.h" + +const size_t IntranetRedirectDetector::kNumCharsInHostnames = 10; + +IntranetRedirectDetector::IntranetRedirectDetector() + : redirect_origin_(WideToUTF8(g_browser_process->local_state()->GetString( + prefs::kLastKnownIntranetRedirectOrigin))), + ALLOW_THIS_IN_INITIALIZER_LIST(fetcher_factory_(this)), + in_startup_sleep_(true), + request_context_available_(!!Profile::GetDefaultRequestContext()) { + registrar_.Add(this, NotificationType::DEFAULT_REQUEST_CONTEXT_AVAILABLE, + NotificationService::AllSources()); + + // Because this function can be called during startup, when kicking off a URL + // fetch can eat up 20 ms of time, we delay seven seconds, which is hopefully + // long enough to be after startup, but still get results back quickly. + // Ideally, instead of this timer, we'd do something like "check if the + // browser is starting up, and if so, come back later", but there is currently + // no function to do this. + static const int kStartFetchDelayMS = 7000; + MessageLoop::current()->PostDelayedTask(FROM_HERE, + fetcher_factory_.NewRunnableMethod( + &IntranetRedirectDetector::FinishSleep), + kStartFetchDelayMS); +} + +IntranetRedirectDetector::~IntranetRedirectDetector() { + STLDeleteElements(&fetchers_); +} + +// static +GURL IntranetRedirectDetector::RedirectOrigin() { + const IntranetRedirectDetector* const detector = + g_browser_process->intranet_redirect_detector(); + return detector ? detector->redirect_origin_ : GURL(); +} + +// static +void IntranetRedirectDetector::RegisterPrefs(PrefService* prefs) { + prefs->RegisterStringPref(prefs::kLastKnownIntranetRedirectOrigin, + std::wstring()); +} + +void IntranetRedirectDetector::FinishSleep() { + in_startup_sleep_ = false; + StartFetchesIfPossible(); +} + +void IntranetRedirectDetector::StartFetchesIfPossible() { + // Bail if a fetch isn't appropriate right now. This function will be called + // again each time one of the preconditions changes, so we'll fetch + // immediately once all of them are met. + if (in_startup_sleep_ || !request_context_available_) + return; + + // We shouldn't somehow run twice. + DCHECK(fetchers_.empty() && resulting_origins_.empty()); + + // Start three fetchers on random hostnames. + for (size_t i = 0; i < 3; ++i) { + std::string url_string("http://"); + for (size_t j = 0; j < kNumCharsInHostnames; ++j) + url_string += ('a' + base::RandInt(0, 'z' - 'a')); + GURL random_url(url_string + '/'); + URLFetcher* fetcher = new URLFetcher(random_url, URLFetcher::HEAD, this); + // We don't want these fetches to affect existing state in the profile. + fetcher->set_load_flags(net::LOAD_DISABLE_CACHE | + net::LOAD_DO_NOT_SAVE_COOKIES); + fetcher->set_request_context(Profile::GetDefaultRequestContext()); + fetcher->Start(); + fetchers_.insert(fetcher); + } +} + +void IntranetRedirectDetector::OnURLFetchComplete( + const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data) { + // Delete the fetcher on this function's exit. + Fetchers::iterator fetcher = fetchers_.find(const_cast(source)); + DCHECK(fetcher != fetchers_.end()); + scoped_ptr clean_up_fetcher(*fetcher); + fetchers_.erase(fetcher); + + // If any two fetches result in the same domain/host, we set the redirect + // origin to that; otherwise we set it to nothing. + if (!status.is_success() || (response_code != 200)) { + if ((resulting_origins_.empty()) || + ((resulting_origins_.size() == 1) && + resulting_origins_.front().is_valid())) { + resulting_origins_.push_back(GURL()); + return; + } + redirect_origin_ = GURL(); + } else { + DCHECK(url.is_valid()); + GURL origin(url.GetOrigin()); + if (resulting_origins_.empty()) { + resulting_origins_.push_back(origin); + return; + } + if (net::RegistryControlledDomainService::SameDomainOrHost( + resulting_origins_.front(), origin)) { + redirect_origin_ = origin; + if (!fetchers_.empty()) { + // Cancel remaining fetch, we don't need it. + DCHECK(fetchers_.size() == 1); + delete (*fetchers_.begin()); + fetchers_.clear(); + } + } + if (resulting_origins_.size() == 1) { + resulting_origins_.push_back(origin); + return; + } + DCHECK(resulting_origins_.size() == 2); + redirect_origin_ = net::RegistryControlledDomainService::SameDomainOrHost( + resulting_origins_.back(), origin) ? origin : GURL(); + } + + g_browser_process->local_state()->SetString( + prefs::kLastKnownIntranetRedirectOrigin, redirect_origin_.is_valid() ? + UTF8ToWide(redirect_origin_.spec()) : std::wstring()); +} + +void IntranetRedirectDetector::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK_EQ(NotificationType::DEFAULT_REQUEST_CONTEXT_AVAILABLE, type.value); + request_context_available_ = true; + StartFetchesIfPossible(); +} + +IntranetRedirectHostResolverProc::IntranetRedirectHostResolverProc( + net::HostResolverProc* previous) + : net::HostResolverProc(previous) { +} + +int IntranetRedirectHostResolverProc::Resolve(const std::string& host, + net::AddressFamily address_family, + net::AddressList* addrlist) { + // We'd love to just ask the IntranetRedirectDetector, but we may not be on + // the same thread. So just use the heuristic that any all-lowercase a-z + // hostname with the right number of characters is likely from the detector + // (and thus should be blocked). + return ((host.length() == IntranetRedirectDetector::kNumCharsInHostnames) && + (host.find_first_not_of("abcdefghijklmnopqrstuvwxyz") == + std::string::npos)) ? + net::ERR_NAME_NOT_RESOLVED : + ResolveUsingPrevious(host, address_family, addrlist); +} diff --git a/chrome/browser/intranet_redirect_detector.h b/chrome/browser/intranet_redirect_detector.h new file mode 100644 index 0000000..f1e38ae --- /dev/null +++ b/chrome/browser/intranet_redirect_detector.h @@ -0,0 +1,106 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_INTRANET_REDIRECT_DETECTOR_H_ +#define CHROME_BROWSER_INTRANET_REDIRECT_DETECTOR_H_ + +#include +#include +#include + +#include "chrome/browser/net/url_fetcher.h" +#include "chrome/common/notification_registrar.h" +#include "googleurl/src/gurl.h" +#include "net/base/host_resolver_proc.h" + +class PrefService; + +// This object is responsible for determining whether the user is on a network +// that redirects requests for intranet hostnames to another site, and if so, +// tracking what that site is (including across restarts via a pref). For +// example, the user's ISP might convert a request for "http://query/" into a +// 302 redirect to "http://isp.domain.com/search?q=query" in order to display +// custom pages on typos, nonexistent sites, etc. +// +// We use this information in the AlternateNavURLFetcher to avoid displaying +// infobars for these cases. Our infobars are designed to allow users to get at +// intranet sites when they were erroneously taken to a search result page. In +// these cases, however, users would be shown a confusing and useless infobar +// when they really did mean to do a search. +// +// Consumers should call RedirectOrigin(), which is guaranteed to synchronously +// return a value at all times (even during startup or in unittest mode). If no +// redirection is in place, the returned GURL will be empty. +class IntranetRedirectDetector : public URLFetcher::Delegate, + public NotificationObserver { + public: + // Only the main browser process loop should call this, when setting up + // g_browser_process->intranet_redirect_detector_. No code other than the + // IntranetRedirectDetector itself should actually use + // g_browser_process->intranet_redirect_detector() (which shouldn't be hard, + // since there aren't useful public functions on this object for consumers to + // access anyway). + IntranetRedirectDetector(); + ~IntranetRedirectDetector(); + + // Returns the current redirect origin. This will be empty if no redirection + // is in place. + static GURL RedirectOrigin(); + + static void RegisterPrefs(PrefService* prefs); + + // The number of characters the fetcher will use for its randomly-generated + // hostnames. + static const size_t kNumCharsInHostnames; + + private: + typedef std::set Fetchers; + + // Called when the five second startup sleep has finished. Runs any pending + // fetch. + void FinishSleep(); + + // Starts the fetches to determine the redirect URL if we can currently do so. + void StartFetchesIfPossible(); + + // URLFetcher::Delegate + virtual void OnURLFetchComplete(const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data); + + // NotificationObserver + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + NotificationRegistrar registrar_; + GURL redirect_origin_; + ScopedRunnableMethodFactory fetcher_factory_; + Fetchers fetchers_; + std::vector resulting_origins_; + bool in_startup_sleep_; // True if we're in the seven-second "no fetching" + // period that begins at browser start. + bool request_context_available_; + // True when the profile has been loaded and the + // default request context created, so we can + // actually do the fetch with the right data. + + DISALLOW_COPY_AND_ASSIGN(IntranetRedirectDetector); +}; + +// This is for use in testing, where we don't want our fetches to actually go +// over the network. It captures the requests and causes them to fail. +class IntranetRedirectHostResolverProc : public net::HostResolverProc { + public: + explicit IntranetRedirectHostResolverProc(net::HostResolverProc* previous); + + virtual int Resolve(const std::string& host, + net::AddressFamily address_family, + net::AddressList* addrlist); +}; + +#endif // CHROME_BROWSER_INTRANET_REDIRECT_DETECTOR_H_ diff --git a/chrome/browser/net/cookie_policy_browsertest.cc b/chrome/browser/net/cookie_policy_browsertest.cc index f218575..7dbecf9 100644 --- a/chrome/browser/net/cookie_policy_browsertest.cc +++ b/chrome/browser/net/cookie_policy_browsertest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -9,6 +9,7 @@ #include "chrome/common/pref_service.h" #include "chrome/test/in_process_browser_test.h" #include "chrome/test/ui_test_utils.h" +#include "net/base/mock_host_resolver.h" class CookiePolicyBrowserTest : public InProcessBrowserTest { public: diff --git a/chrome/browser/privacy_blacklist/blacklist_manager_browsertest.cc b/chrome/browser/privacy_blacklist/blacklist_manager_browsertest.cc index 8bbd8ac..b228af0 100644 --- a/chrome/browser/privacy_blacklist/blacklist_manager_browsertest.cc +++ b/chrome/browser/privacy_blacklist/blacklist_manager_browsertest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -18,6 +18,7 @@ #include "chrome/test/ui_test_utils.h" #include "grit/browser_resources.h" #include "grit/generated_resources.h" +#include "net/base/mock_host_resolver.h" #include "testing/gtest/include/gtest/gtest.h" namespace { diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index f1a2420..b18c7b9 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1097,6 +1097,8 @@ 'browser/input_window_dialog.h', 'browser/input_window_dialog_gtk.cc', 'browser/input_window_dialog_win.cc', + 'browser/intranet_redirect_detector.cc', + 'browser/intranet_redirect_detector.h', 'browser/jankometer.cc', 'browser/jankometer.h', 'browser/jumplist.cc', diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 6c1edf9..2f95793 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -519,6 +519,10 @@ const wchar_t kShouldShowWelcomePage[] = L"show-welcome-page"; // correct Google domain/country code for whatever location the user is in. const wchar_t kLastKnownGoogleURL[] = L"browser.last_known_google_url"; +// String containing the last known intranet redirect URL, if any. See +// intranet_redirect_detector.h for more information. +const wchar_t kLastKnownIntranetRedirectOrigin[] = L""; + // Integer containing the system Country ID the first time we checked the // template URL prepopulate data. This is used to avoid adding a whole bunch of // new search engine choices if prepopulation runs when the user's Country ID diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 8229a6f..4dab8c9 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -193,6 +193,7 @@ extern const wchar_t kShouldUseOEMFirstRunBubble[]; extern const wchar_t kShouldShowWelcomePage[]; extern const wchar_t kLastKnownGoogleURL[]; +extern const wchar_t kLastKnownIntranetRedirectOrigin[]; extern const wchar_t kCountryIDAtInstall[]; extern const wchar_t kGeoIDAtInstall[]; // OBSOLETE diff --git a/chrome/test/in_process_browser_test.cc b/chrome/test/in_process_browser_test.cc index 6c85760..4e206e8 100644 --- a/chrome/test/in_process_browser_test.cc +++ b/chrome/test/in_process_browser_test.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -15,6 +15,7 @@ #include "chrome/browser/browser_shutdown.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/chrome_thread.h" +#include "chrome/browser/intranet_redirect_detector.h" #include "chrome/browser/net/url_request_mock_util.h" #include "chrome/browser/profile.h" #include "chrome/browser/profile_manager.h" @@ -32,6 +33,7 @@ #include "chrome/common/url_constants.h" #include "chrome/test/testing_browser_process.h" #include "chrome/test/ui_test_utils.h" +#include "net/base/mock_host_resolver.h" #include "sandbox/src/dep.h" #if defined(OS_LINUX) @@ -76,6 +78,9 @@ InProcessBrowserTest::InProcessBrowserTest() initial_timeout_(kInitialTimeoutInMS) { } +InProcessBrowserTest::~InProcessBrowserTest() { +} + void InProcessBrowserTest::SetUp() { // Cleanup the user data dir. FilePath user_data_dir; @@ -152,7 +157,8 @@ void InProcessBrowserTest::SetUp() { params.ui_task = NewRunnableMethod(this, &InProcessBrowserTest::RunTestOnMainThreadLoop); - host_resolver_ = new net::RuleBasedHostResolverProc(NULL); + host_resolver_ = new net::RuleBasedHostResolverProc( + new IntranetRedirectHostResolverProc(NULL)); // Something inside the browser does this lookup implicitly. Make it fail // to avoid external dependency. It won't break the tests. diff --git a/chrome/test/in_process_browser_test.h b/chrome/test/in_process_browser_test.h index ace6e07..a142626 100644 --- a/chrome/test/in_process_browser_test.h +++ b/chrome/test/in_process_browser_test.h @@ -1,11 +1,10 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CHROME_TEST_IN_PROCESS_BROWSER_TEST_H_ #define CHROME_TEST_IN_PROCESS_BROWSER_TEST_H_ -#include "net/base/mock_host_resolver.h" #include "net/url_request/url_request_unittest.h" #include "testing/gtest/include/gtest/gtest.h" @@ -41,6 +40,7 @@ class RuleBasedHostResolverProc; class InProcessBrowserTest : public testing::Test { public: InProcessBrowserTest(); + virtual ~InProcessBrowserTest(); // We do this so we can be used in a Task. void AddRef() {} diff --git a/chrome/test/live_sync/live_bookmarks_sync_test.cc b/chrome/test/live_sync/live_bookmarks_sync_test.cc index 8d16edc..b8836c7 100644 --- a/chrome/test/live_sync/live_bookmarks_sync_test.cc +++ b/chrome/test/live_sync/live_bookmarks_sync_test.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,6 +13,7 @@ #include "chrome/browser/profile.h" #include "chrome/browser/profile_manager.h" #include "chrome/test/ui_test_utils.h" +#include "net/base/mock_host_resolver.h" namespace switches { const wchar_t kSyncUserForTest[] = L"sync-user-for-test"; @@ -52,6 +53,12 @@ class BookmarkLoadObserver : public BookmarkModelObserver { DISALLOW_COPY_AND_ASSIGN(BookmarkLoadObserver); }; +LiveBookmarksSyncTest::LiveBookmarksSyncTest() { +} + +LiveBookmarksSyncTest::~LiveBookmarksSyncTest() { +} + // static void LiveBookmarksSyncTest::BlockUntilLoaded(BookmarkModel* m) { if (m->IsLoaded()) diff --git a/chrome/test/live_sync/live_bookmarks_sync_test.h b/chrome/test/live_sync/live_bookmarks_sync_test.h index 1ff0251..134e82f 100644 --- a/chrome/test/live_sync/live_bookmarks_sync_test.h +++ b/chrome/test/live_sync/live_bookmarks_sync_test.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -16,6 +16,9 @@ class BookmarkModel; class BookmarkNode; class Profile; +namespace net { +class ScopedDefaultHostResolverProc; +} namespace switches { extern const wchar_t kSyncUserForTest[]; @@ -27,8 +30,8 @@ extern const wchar_t kSyncPasswordForTest[]; // without a valid sync server set up. class LiveBookmarksSyncTest : public InProcessBrowserTest { public: - LiveBookmarksSyncTest() { } - ~LiveBookmarksSyncTest() { } + LiveBookmarksSyncTest(); + ~LiveBookmarksSyncTest(); virtual void SetUp() { // At this point, the browser hasn't been launched, and no services are diff --git a/chrome/test/testing_browser_process.h b/chrome/test/testing_browser_process.h index 2131ed7..2bbcdcb 100644 --- a/chrome/test/testing_browser_process.h +++ b/chrome/test/testing_browser_process.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -111,6 +111,10 @@ class TestingBrowserProcess : public BrowserProcess { return NULL; } + virtual IntranetRedirectDetector* intranet_redirect_detector() { + return NULL; + } + virtual AutomationProviderList* InitAutomationProviderList() { return NULL; } diff --git a/net/base/host_resolver_proc.cc b/net/base/host_resolver_proc.cc index 87478eb..bd0ba2b 100644 --- a/net/base/host_resolver_proc.cc +++ b/net/base/host_resolver_proc.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -26,11 +26,33 @@ namespace net { HostResolverProc* HostResolverProc::default_proc_ = NULL; HostResolverProc::HostResolverProc(HostResolverProc* previous) { - set_previous_proc(previous); + SetPreviousProc(previous); // Implicitly fall-back to the global default procedure. if (!previous) - set_previous_proc(default_proc_); + SetPreviousProc(default_proc_); +} + +void HostResolverProc::SetPreviousProc(HostResolverProc* proc) { + HostResolverProc* current_previous = previous_proc_; + previous_proc_ = NULL; + // Now that we've guaranteed |this| is the last proc in a chain, we can + // detect potential cycles using GetLastProc(). + previous_proc_ = (GetLastProc(proc) == this) ? current_previous : proc; +} + +void HostResolverProc::SetLastProc(HostResolverProc* proc) { + GetLastProc(this)->SetPreviousProc(proc); +} + +// static +HostResolverProc* HostResolverProc::GetLastProc(HostResolverProc* proc) { + if (proc == NULL) + return NULL; + HostResolverProc* last_proc = proc; + while (last_proc->previous_proc_ != NULL) + last_proc = last_proc->previous_proc_; + return last_proc; } // static diff --git a/net/base/host_resolver_proc.h b/net/base/host_resolver_proc.h index f2bff25..ca0c55d 100644 --- a/net/base/host_resolver_proc.h +++ b/net/base/host_resolver_proc.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -47,10 +47,17 @@ class HostResolverProc : public base::RefCountedThreadSafe { friend class MockHostResolverBase; friend class ScopedDefaultHostResolverProc; - // Sets the previous procedure in the chain. - void set_previous_proc(HostResolverProc* proc) { - previous_proc_ = proc; - } + // Sets the previous procedure in the chain. Aborts if this would result in a + // cycle. + void SetPreviousProc(HostResolverProc* proc); + + // Sets the last procedure in the chain, i.e. appends |proc| to the end of the + // current chain. Aborts if this would result in a cycle. + void SetLastProc(HostResolverProc* proc); + + // Returns the last procedure in the chain starting at |proc|. Will return + // NULL iff |proc| is NULL. + static HostResolverProc* GetLastProc(HostResolverProc* proc); // Sets the default host resolver procedure that is used by HostResolverImpl. // This can be used through ScopedDefaultHostResolverProc to set a catch-all @@ -59,7 +66,6 @@ class HostResolverProc : public base::RefCountedThreadSafe { static HostResolverProc* SetDefault(HostResolverProc* proc); static HostResolverProc* GetDefault(); - private: scoped_refptr previous_proc_; static HostResolverProc* default_proc_; diff --git a/net/base/mock_host_resolver.cc b/net/base/mock_host_resolver.cc index b983724..cf38490 100644 --- a/net/base/mock_host_resolver.cc +++ b/net/base/mock_host_resolver.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -89,7 +89,7 @@ void MockHostResolverBase::Reset(HostResolverProc* interceptor) { // Lastly add the provided interceptor to the front of the chain. if (interceptor) { - interceptor->set_previous_proc(proc); + interceptor->SetPreviousProc(proc); proc = interceptor; } @@ -225,9 +225,8 @@ int RuleBasedHostResolverProc::Resolve(const std::string& host, //----------------------------------------------------------------------------- ScopedDefaultHostResolverProc::ScopedDefaultHostResolverProc( - HostResolverProc* proc) : current_proc_(proc) { - previous_proc_ = HostResolverProc::SetDefault(current_proc_); - current_proc_->set_previous_proc(previous_proc_); + HostResolverProc* proc) { + Init(proc); } ScopedDefaultHostResolverProc::~ScopedDefaultHostResolverProc() { @@ -239,7 +238,7 @@ ScopedDefaultHostResolverProc::~ScopedDefaultHostResolverProc() { void ScopedDefaultHostResolverProc::Init(HostResolverProc* proc) { current_proc_ = proc; previous_proc_ = HostResolverProc::SetDefault(current_proc_); - current_proc_->set_previous_proc(previous_proc_); + current_proc_->SetLastProc(previous_proc_); } } // namespace net diff --git a/net/base/mock_host_resolver.h b/net/base/mock_host_resolver.h index 72f10b4..523935a 100644 --- a/net/base/mock_host_resolver.h +++ b/net/base/mock_host_resolver.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -166,10 +166,12 @@ class WaitingHostResolverProc : public HostResolverProc { base::WaitableEvent event_; }; -// This class sets the HostResolverProc for a particular scope. If there are -// multiple ScopedDefaultHostResolverProc in existence, then the last one -// allocated will be used. However, if it does not provide a matching rule, -// then it should delegate to the previously set HostResolverProc. +// This class sets the default HostResolverProc for a particular scope. The +// chain of resolver procs starting at |proc| is placed in front of any existing +// default resolver proc(s). This means that if multiple +// ScopedDefaultHostResolverProcs are declared, then resolving will start with +// the procs given to the last-allocated one, then fall back to the procs given +// to the previously-allocated one, and so forth. // // NOTE: Only use this as a catch-all safety net. Individual tests should use // MockHostResolver. -- cgit v1.1