172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
24a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// Use of this source code is governed by a BSD-style license that can be
34a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// found in the LICENSE file.
44a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
54a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/browser/safe_browsing/client_side_detection_service.h"
64a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
74a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/command_line.h"
84a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/file_path.h"
94a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/file_util_proxy.h"
104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/logging.h"
11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_ptr.h"
124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/message_loop.h"
1372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/metrics/histogram.h"
144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/platform_file.h"
154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/stl_util-inl.h"
164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/task.h"
1772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/time.h"
184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/common/net/http_return.h"
194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/common/net/url_fetcher.h"
20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/common/safe_browsing/csd.pb.h"
21dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/browser_thread.h"
224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "googleurl/src/gurl.h"
234a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "net/base/load_flags.h"
24ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "net/url_request/url_request_context_getter.h"
254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "net/url_request/url_request_status.h"
264a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
274a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochnamespace safe_browsing {
284a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst int ClientSideDetectionService::kMaxReportsPerInterval = 3;
3072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
3172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst base::TimeDelta ClientSideDetectionService::kReportsInterval =
3272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    base::TimeDelta::FromDays(1);
3372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst base::TimeDelta ClientSideDetectionService::kNegativeCacheInterval =
3472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    base::TimeDelta::FromDays(1);
3572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst base::TimeDelta ClientSideDetectionService::kPositiveCacheInterval =
3672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    base::TimeDelta::FromMinutes(30);
3772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
384a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochconst char ClientSideDetectionService::kClientReportPhishingUrl[] =
394a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    "https://sb-ssl.google.com/safebrowsing/clientreport/phishing";
40ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Note: when updatng the model version, don't forget to change the filename
41ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// in chrome/common/chrome_constants.cc as well, or else existing users won't
42ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// download the new model.
43ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen//
44ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// TODO(bryner): add version metadata so that clients can download new models
45ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// without needing a new model filename.
464a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochconst char ClientSideDetectionService::kClientModelUrl[] =
47ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    "https://ssl.gstatic.com/safebrowsing/csd/client_model_v1.pb";
484a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
4921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenstruct ClientSideDetectionService::ClientReportInfo {
5021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  scoped_ptr<ClientReportPhishingRequestCallback> callback;
5121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  GURL phishing_url;
5221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen};
5321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
5472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenClientSideDetectionService::CacheState::CacheState(bool phish, base::Time time)
5572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    : is_phishing(phish),
5672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      timestamp(time) {}
5772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochClientSideDetectionService::ClientSideDetectionService(
594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const FilePath& model_path,
60ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    net::URLRequestContextGetter* request_context_getter)
614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    : model_path_(model_path),
624a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      model_status_(UNKNOWN_STATUS),
634a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      model_file_(base::kInvalidPlatformFileValue),
644a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
654a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      ALLOW_THIS_IN_INITIALIZER_LIST(callback_factory_(this)),
66dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      request_context_getter_(request_context_getter) {}
674a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
684a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochClientSideDetectionService::~ClientSideDetectionService() {
694a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  method_factory_.RevokeAll();
704a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  STLDeleteContainerPairPointers(client_phishing_reports_.begin(),
714a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                 client_phishing_reports_.end());
724a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  client_phishing_reports_.clear();
734a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  STLDeleteElements(&open_callbacks_);
744a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  CloseModelFile();
754a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
764a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
774a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch/* static */
784a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochClientSideDetectionService* ClientSideDetectionService::Create(
794a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const FilePath& model_path,
80ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    net::URLRequestContextGetter* request_context_getter) {
814a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
824a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  scoped_ptr<ClientSideDetectionService> service(
834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      new ClientSideDetectionService(model_path, request_context_getter));
84dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (!service->InitializePrivateNetworks()) {
85dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    UMA_HISTOGRAM_COUNTS("SBClientPhishing.InitPrivateNetworksFailed", 1);
86dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    return NULL;
87dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
88dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // We try to open the model file right away and start fetching it if
904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // it does not already exist on disk.
914a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  base::FileUtilProxy::CreateOrOpenCallback* cb =
924a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      service.get()->callback_factory_.NewCallback(
934a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          &ClientSideDetectionService::OpenModelFileDone);
944a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (!base::FileUtilProxy::CreateOrOpen(
954a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          model_path,
974a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
984a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          cb)) {
994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    delete cb;
1004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return NULL;
1014a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
102ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
103ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Delete the previous-version model file.
104ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // TODO(bryner): Remove this for M14.
105ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  base::FileUtilProxy::Delete(
106ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
107ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      model_path.DirName().AppendASCII("Safe Browsing Phishing Model"),
108ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      false /* not recursive */,
109ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NULL /* not interested in result */);
1104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  return service.release();
1114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
1124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid ClientSideDetectionService::GetModelFile(OpenModelDoneCallback* callback) {
1144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  MessageLoop::current()->PostTask(
1164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      FROM_HERE,
1174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      method_factory_.NewRunnableMethod(
1184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          &ClientSideDetectionService::StartGetModelFile, callback));
1194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
1204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1214a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid ClientSideDetectionService::SendClientReportPhishingRequest(
122ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ClientPhishingRequest* verdict,
1234a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    ClientReportPhishingRequestCallback* callback) {
1244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  MessageLoop::current()->PostTask(
1264a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      FROM_HERE,
1274a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      method_factory_.NewRunnableMethod(
1284a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          &ClientSideDetectionService::StartClientReportPhishingRequest,
129ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          verdict, callback));
1304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
1314a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
132dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenbool ClientSideDetectionService::IsPrivateIPAddress(
133dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    const std::string& ip_address) const {
134dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  net::IPAddressNumber ip_number;
135dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (!net::ParseIPLiteralToNumber(ip_address, &ip_number)) {
136dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    DLOG(WARNING) << "Unable to parse IP address: " << ip_address;
137dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    // Err on the side of safety and assume this might be private.
138dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    return true;
139dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
140dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
141dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  for (std::vector<AddressRange>::const_iterator it =
142dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen           private_networks_.begin();
143dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen       it != private_networks_.end(); ++it) {
144dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    if (net::IPNumberMatchesPrefix(ip_number, it->first, it->second)) {
145dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      return true;
146dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    }
147dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
148dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  return false;
149dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
150dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
1514a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid ClientSideDetectionService::OnURLFetchComplete(
1524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const URLFetcher* source,
1534a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const GURL& url,
15472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const net::URLRequestStatus& status,
1554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    int response_code,
1564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const ResponseCookies& cookies,
1574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const std::string& data) {
15872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (source == model_fetcher_.get()) {
1594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    HandleModelResponse(source, url, status, response_code, cookies, data);
1604a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  } else if (client_phishing_reports_.find(source) !=
1614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch             client_phishing_reports_.end()) {
1624a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    HandlePhishingVerdict(source, url, status, response_code, cookies, data);
1634a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  } else {
1644a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    NOTREACHED();
1654a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
16672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
16772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
1684a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid ClientSideDetectionService::SetModelStatus(ModelStatus status) {
1694a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK_NE(READY_STATUS, model_status_);
1704a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  model_status_ = status;
1714a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (READY_STATUS == status || ERROR_STATUS == status) {
1724a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    for (size_t i = 0; i < open_callbacks_.size(); ++i) {
1734a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      open_callbacks_[i]->Run(model_file_);
1744a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    }
1754a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    STLDeleteElements(&open_callbacks_);
1764a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  } else {
1774a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    NOTREACHED();
1784a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
1794a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
1804a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1814a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid ClientSideDetectionService::OpenModelFileDone(
1824a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    base::PlatformFileError error_code,
1834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    base::PassPlatformFile file,
1844a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    bool created) {
1854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(!created);
1864a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (base::PLATFORM_FILE_OK == error_code) {
1874a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // The model file already exists.  There is no need to fetch the model.
1884a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    model_file_ = file.ReleaseValue();
1894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    SetModelStatus(READY_STATUS);
1904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  } else if (base::PLATFORM_FILE_ERROR_NOT_FOUND == error_code) {
1914a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // We need to fetch the model since it does not exist yet.
19272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    model_fetcher_.reset(URLFetcher::Create(0 /* ID is not used */,
19372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                            GURL(kClientModelUrl),
19472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                            URLFetcher::GET,
19572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                            this));
1964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    model_fetcher_->set_request_context(request_context_getter_.get());
1974a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    model_fetcher_->Start();
1984a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  } else {
1994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // It is not clear what we should do in this case.  For now we simply fail.
2004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // Hopefully, we'll be able to read the model during the next browser
2014a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // restart.
2024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    SetModelStatus(ERROR_STATUS);
2034a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
2044a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
2054a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2064a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid ClientSideDetectionService::CreateModelFileDone(
2074a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    base::PlatformFileError error_code,
2084a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    base::PassPlatformFile file,
2094a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    bool created) {
2104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  model_file_ = file.ReleaseValue();
21172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::FileUtilProxy::WriteCallback* cb = callback_factory_.NewCallback(
2124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      &ClientSideDetectionService::WriteModelFileDone);
2134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (!created ||
2144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      base::PLATFORM_FILE_OK != error_code ||
2154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      !base::FileUtilProxy::Write(
2164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
2174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          model_file_,
2184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          0 /* offset */, tmp_model_string_->data(), tmp_model_string_->size(),
2194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch          cb)) {
2204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    delete cb;
2214a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // An error occurred somewhere.  We close the model file if necessary and
2224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // then run all the pending callbacks giving them an invalid model file.
2234a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    CloseModelFile();
2244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    SetModelStatus(ERROR_STATUS);
2254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
2264a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
2274a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2284a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid ClientSideDetectionService::WriteModelFileDone(
2294a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    base::PlatformFileError error_code,
2304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    int bytes_written) {
2314a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (base::PLATFORM_FILE_OK == error_code) {
2324a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    SetModelStatus(READY_STATUS);
2334a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  } else {
2344a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // TODO(noelutz): maybe we should retry writing the model since we
2354a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // did already fetch the model?
2364a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    CloseModelFile();
2374a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    SetModelStatus(ERROR_STATUS);
2384a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
2394a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // Delete the model string that we kept around while we were writing the
2404a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // string to disk - we don't need it anymore.
2414a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  tmp_model_string_.reset();
2424a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
2434a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2444a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid ClientSideDetectionService::CloseModelFile() {
2454a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (model_file_ != base::kInvalidPlatformFileValue) {
2464a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    base::FileUtilProxy::Close(
2474a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
2484a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        model_file_,
2494a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        NULL);
2504a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
2514a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  model_file_ = base::kInvalidPlatformFileValue;
2524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
2534a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2544a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid ClientSideDetectionService::StartGetModelFile(
2554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    OpenModelDoneCallback* callback) {
2564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (UNKNOWN_STATUS == model_status_) {
2584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // Store the callback which will be called once we know the status of the
2594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // model file.
2604a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    open_callbacks_.push_back(callback);
2614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  } else {
2624a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // The model is either in READY or ERROR state which means we can
2634a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // call the callback right away.
2644a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    callback->Run(model_file_);
2654a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    delete callback;
2664a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
2674a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
2684a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2694a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid ClientSideDetectionService::StartClientReportPhishingRequest(
270ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ClientPhishingRequest* verdict,
2714a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    ClientReportPhishingRequestCallback* callback) {
2724a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
273ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  scoped_ptr<ClientPhishingRequest> request(verdict);
2744a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  scoped_ptr<ClientReportPhishingRequestCallback> cb(callback);
27572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
2764a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  std::string request_data;
277ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (!request->SerializeToString(&request_data)) {
27872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    UMA_HISTOGRAM_COUNTS("SBClientPhishing.RequestNotSerialized", 1);
27972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    VLOG(1) << "Unable to serialize the CSD request. Proto file changed?";
280ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    cb->Run(GURL(request->url()), false);
2814a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return;
2824a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
2834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2844a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  URLFetcher* fetcher = URLFetcher::Create(0 /* ID is not used */,
2854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                           GURL(kClientReportPhishingUrl),
2864a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                           URLFetcher::POST,
2874a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                           this);
2884a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // Remember which callback and URL correspond to the current fetcher object.
2904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  ClientReportInfo* info = new ClientReportInfo;
2914a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  info->callback.swap(cb);  // takes ownership of the callback.
292ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  info->phishing_url = GURL(request->url());
2934a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  client_phishing_reports_[fetcher] = info;
2944a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2954a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  fetcher->set_load_flags(net::LOAD_DISABLE_CACHE);
2964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  fetcher->set_request_context(request_context_getter_.get());
2974a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  fetcher->set_upload_data("application/octet-stream", request_data);
2984a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  fetcher->Start();
29972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
30072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Record that we made a request
30172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  phishing_report_times_.push(base::Time::Now());
3024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
3034a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
3044a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid ClientSideDetectionService::HandleModelResponse(
3054a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const URLFetcher* source,
3064a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const GURL& url,
30772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const net::URLRequestStatus& status,
3084a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    int response_code,
3094a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const ResponseCookies& cookies,
3104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const std::string& data) {
3114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (status.is_success() && RC_REQUEST_OK == response_code) {
3124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // Copy the model because it has to be accessible after this function
3134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // returns.  Once we have written the model to a file we will delete the
3144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // temporary model string. TODO(noelutz): don't store the model to disk if
3154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // it's invalid.
3164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    tmp_model_string_.reset(new std::string(data));
3174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    base::FileUtilProxy::CreateOrOpenCallback* cb =
3184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        callback_factory_.NewCallback(
3194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch            &ClientSideDetectionService::CreateModelFileDone);
3204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    if (!base::FileUtilProxy::CreateOrOpen(
3214a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch            BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
3224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch            model_path_,
3234a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch            base::PLATFORM_FILE_CREATE_ALWAYS |
3244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch            base::PLATFORM_FILE_WRITE |
3254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch            base::PLATFORM_FILE_READ,
3264a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch            cb)) {
3274a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      delete cb;
3284a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      SetModelStatus(ERROR_STATUS);
3294a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    }
3304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  } else {
3314a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    SetModelStatus(ERROR_STATUS);
3324a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
3334a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
3344a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
3354a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid ClientSideDetectionService::HandlePhishingVerdict(
3364a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const URLFetcher* source,
3374a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const GURL& url,
33872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const net::URLRequestStatus& status,
3394a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    int response_code,
3404a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const ResponseCookies& cookies,
3414a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const std::string& data) {
3424a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  ClientPhishingResponse response;
3434a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  scoped_ptr<ClientReportInfo> info(client_phishing_reports_[source]);
34472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (status.is_success() && RC_REQUEST_OK == response_code &&
3454a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      response.ParseFromString(data)) {
34672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // Cache response, possibly flushing an old one.
34772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    cache_[info->phishing_url] =
34872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        make_linked_ptr(new CacheState(response.phishy(), base::Time::Now()));
3494a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    info->callback->Run(info->phishing_url, response.phishy());
3504a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  } else {
3514a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    DLOG(ERROR) << "Unable to get the server verdict for URL: "
352dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                << info->phishing_url << " status: " << status.status() << " "
353dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                << "response_code:" << response_code;
3544a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    info->callback->Run(info->phishing_url, false);
3554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
3564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  client_phishing_reports_.erase(source);
35772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  delete source;
35872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
35972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
360ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool ClientSideDetectionService::IsInCache(const GURL& url) {
361ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  UpdateCache();
362ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
363ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return cache_.find(url) != cache_.end();
364ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
365ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
366ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool ClientSideDetectionService::GetValidCachedResult(const GURL& url,
367ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                                      bool* is_phishing) {
36872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  UpdateCache();
36972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
37072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  PhishingCache::iterator it = cache_.find(url);
37172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (it == cache_.end()) {
37272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return false;
37372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
37472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
37572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // We still need to check if the result is valid.
37672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  const CacheState& cache_state = *it->second;
37772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (cache_state.is_phishing ?
37872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      cache_state.timestamp > base::Time::Now() - kPositiveCacheInterval :
37972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      cache_state.timestamp > base::Time::Now() - kNegativeCacheInterval) {
38072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    *is_phishing = cache_state.is_phishing;
38172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return true;
38272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
38372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return false;
38472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
38572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
38672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ClientSideDetectionService::UpdateCache() {
38772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Since we limit the number of requests but allow pass-through for cache
38872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // refreshes, we don't want to remove elements from the cache if they
38972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // could be used for this purpose even if we will not use the entry to
39072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // satisfy the request from the cache.
39172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::TimeDelta positive_cache_interval =
39272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      std::max(kPositiveCacheInterval, kReportsInterval);
39372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::TimeDelta negative_cache_interval =
39472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      std::max(kNegativeCacheInterval, kReportsInterval);
39572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
39672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Remove elements from the cache that will no longer be used.
39772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  for (PhishingCache::iterator it = cache_.begin(); it != cache_.end();) {
39872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const CacheState& cache_state = *it->second;
39972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (cache_state.is_phishing ?
40072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        cache_state.timestamp > base::Time::Now() - positive_cache_interval :
40172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        cache_state.timestamp > base::Time::Now() - negative_cache_interval) {
40272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      ++it;
40372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    } else {
40472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      cache_.erase(it++);
40572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    }
40672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
40772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
40872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
409ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool ClientSideDetectionService::OverReportLimit() {
410ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return GetNumReports() > kMaxReportsPerInterval;
411ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
412ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
41372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenint ClientSideDetectionService::GetNumReports() {
41472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::Time cutoff = base::Time::Now() - kReportsInterval;
41572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
41672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Erase items older than cutoff because we will never care about them again.
41772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  while (!phishing_report_times_.empty() &&
41872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen         phishing_report_times_.front() < cutoff) {
41972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    phishing_report_times_.pop();
42072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
42172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
42272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Return the number of elements that are above the cutoff.
42372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return phishing_report_times_.size();
4244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
4254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
426dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenbool ClientSideDetectionService::InitializePrivateNetworks() {
427dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  static const char* const kPrivateNetworks[] = {
428dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    "10.0.0.0/8",
429dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    "127.0.0.0/8",
430dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    "172.16.0.0/12",
431dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    "192.168.0.0/16",
432dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    // IPv6 address ranges
433dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    "fc00::/7",
434dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    "fec0::/10",
435dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    "::1/128",
436dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  };
437dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
438dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  for (size_t i = 0; i < arraysize(kPrivateNetworks); ++i) {
439dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    net::IPAddressNumber ip_number;
440dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    size_t prefix_length;
441dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    if (net::ParseCIDRBlock(kPrivateNetworks[i], &ip_number, &prefix_length)) {
442dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      private_networks_.push_back(std::make_pair(ip_number, prefix_length));
443dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    } else {
444dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      DLOG(FATAL) << "Unable to parse IP address range: "
445dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                  << kPrivateNetworks[i];
446dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      return false;
447dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    }
448dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
449dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  return true;
450dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
451dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
4524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}  // namespace safe_browsing
453