172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/intranet_redirect_detector.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 73345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/command_line.h" 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/rand_util.h" 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/stl_util-inl.h" 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.h" 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/browser_process.h" 123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/prefs/pref_service.h" 133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/common/chrome_switches.h" 14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/pref_names.h" 15ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_service.h" 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/load_flags.h" 17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/net_errors.h" 18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/registry_controlled_domain.h" 19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "net/url_request/url_request_context_getter.h" 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/url_request/url_request_status.h" 21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst size_t IntranetRedirectDetector::kNumCharsInHostnames = 10; 23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochIntranetRedirectDetector::IntranetRedirectDetector() 25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : redirect_origin_(g_browser_process->local_state()->GetString( 26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch prefs::kLastKnownIntranetRedirectOrigin)), 27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ALLOW_THIS_IN_INITIALIZER_LIST(fetcher_factory_(this)), 28ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen in_sleep_(true) { 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Because this function can be called during startup, when kicking off a URL 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // fetch can eat up 20 ms of time, we delay seven seconds, which is hopefully 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // long enough to be after startup, but still get results back quickly. 32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Ideally, instead of this timer, we'd do something like "check if the 33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // browser is starting up, and if so, come back later", but there is currently 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // no function to do this. 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static const int kStartFetchDelayMS = 7000; 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MessageLoop::current()->PostDelayedTask(FROM_HERE, 37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch fetcher_factory_.NewRunnableMethod( 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &IntranetRedirectDetector::FinishSleep), 39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch kStartFetchDelayMS); 40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 41dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen net::NetworkChangeNotifier::AddIPAddressObserver(this); 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochIntranetRedirectDetector::~IntranetRedirectDetector() { 45dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen net::NetworkChangeNotifier::RemoveIPAddressObserver(this); 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch STLDeleteElements(&fetchers_); 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochGURL IntranetRedirectDetector::RedirectOrigin() { 51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const IntranetRedirectDetector* const detector = 52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_browser_process->intranet_redirect_detector(); 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return detector ? detector->redirect_origin_ : GURL(); 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid IntranetRedirectDetector::RegisterPrefs(PrefService* prefs) { 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch prefs->RegisterStringPref(prefs::kLastKnownIntranetRedirectOrigin, 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string()); 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid IntranetRedirectDetector::FinishSleep() { 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch in_sleep_ = false; 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If another fetch operation is still running, cancel it. 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch STLDeleteElements(&fetchers_); 67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch resulting_origins_.clear(); 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 69513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch // The detector is not needed in Chrome Frame since we have no omnibox there. 70513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); 71513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (cmd_line->HasSwitch(switches::kDisableBackgroundNetworking) || 72513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch cmd_line->HasSwitch(switches::kChromeFrame)) 733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return; 743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(fetchers_.empty() && resulting_origins_.empty()); 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Start three fetchers on random hostnames. 78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t i = 0; i < 3; ++i) { 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string url_string("http://"); 80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t j = 0; j < kNumCharsInHostnames; ++j) 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch url_string += ('a' + base::RandInt(0, 'z' - 'a')); 82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GURL random_url(url_string + '/'); 83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch URLFetcher* fetcher = new URLFetcher(random_url, URLFetcher::HEAD, this); 84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We don't want these fetches to affect existing state in the profile. 85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch fetcher->set_load_flags(net::LOAD_DISABLE_CACHE | 86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch net::LOAD_DO_NOT_SAVE_COOKIES); 87ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen fetcher->set_request_context(g_browser_process->system_request_context()); 88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch fetcher->Start(); 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch fetchers_.insert(fetcher); 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid IntranetRedirectDetector::OnURLFetchComplete( 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const URLFetcher* source, 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const GURL& url, 9672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const net::URLRequestStatus& status, 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int response_code, 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const ResponseCookies& cookies, 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::string& data) { 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Delete the fetcher on this function's exit. 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Fetchers::iterator fetcher = fetchers_.find(const_cast<URLFetcher*>(source)); 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(fetcher != fetchers_.end()); 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch scoped_ptr<URLFetcher> clean_up_fetcher(*fetcher); 104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch fetchers_.erase(fetcher); 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If any two fetches result in the same domain/host, we set the redirect 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // origin to that; otherwise we set it to nothing. 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!status.is_success() || (response_code != 200)) { 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if ((resulting_origins_.empty()) || 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ((resulting_origins_.size() == 1) && 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch resulting_origins_.front().is_valid())) { 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch resulting_origins_.push_back(GURL()); 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch redirect_origin_ = GURL(); 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(url.is_valid()); 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GURL origin(url.GetOrigin()); 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (resulting_origins_.empty()) { 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch resulting_origins_.push_back(origin); 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (net::RegistryControlledDomainService::SameDomainOrHost( 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch resulting_origins_.front(), origin)) { 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch redirect_origin_ = origin; 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!fetchers_.empty()) { 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Cancel remaining fetch, we don't need it. 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(fetchers_.size() == 1); 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch delete (*fetchers_.begin()); 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch fetchers_.clear(); 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (resulting_origins_.size() == 1) { 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch resulting_origins_.push_back(origin); 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(resulting_origins_.size() == 2); 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch redirect_origin_ = net::RegistryControlledDomainService::SameDomainOrHost( 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch resulting_origins_.back(), origin) ? origin : GURL(); 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_browser_process->local_state()->SetString( 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch prefs::kLastKnownIntranetRedirectOrigin, redirect_origin_.is_valid() ? 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch redirect_origin_.spec() : std::string()); 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid IntranetRedirectDetector::OnIPAddressChanged() { 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If a request is already scheduled, do not scheduled yet another one. 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (in_sleep_) 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Since presumably many programs open connections after network changes, 153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // delay this a little bit. 154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch in_sleep_ = true; 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static const int kNetworkSwitchDelayMS = 1000; 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MessageLoop::current()->PostDelayedTask(FROM_HERE, 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch fetcher_factory_.NewRunnableMethod( 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &IntranetRedirectDetector::FinishSleep), 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch kNetworkSwitchDelayMS); 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochIntranetRedirectHostResolverProc::IntranetRedirectHostResolverProc( 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch net::HostResolverProc* previous) 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : net::HostResolverProc(previous) { 165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint IntranetRedirectHostResolverProc::Resolve( 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::string& host, 169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch net::AddressFamily address_family, 170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch net::HostResolverFlags host_resolver_flags, 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch net::AddressList* addrlist, 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int* os_error) { 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We'd love to just ask the IntranetRedirectDetector, but we may not be on 174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the same thread. So just use the heuristic that any all-lowercase a-z 175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // hostname with the right number of characters is likely from the detector 176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // (and thus should be blocked). 177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return ((host.length() == IntranetRedirectDetector::kNumCharsInHostnames) && 178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (host.find_first_not_of("abcdefghijklmnopqrstuvwxyz") == 179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string::npos)) ? 180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch net::ERR_NAME_NOT_RESOLVED : 181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ResolveUsingPrevious(host, address_family, host_resolver_flags, addrlist, 182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch os_error); 183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 184