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