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