C++程序  |  265行  |  10.96 KB

// Copyright (c) 2011 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.
//
// Helper class which handles communication with the SafeBrowsing backends for
// client-side phishing detection.  This class can be used to get a file
// descriptor to the client-side phishing model and also to send a ping back to
// Google to verify if a particular site is really phishing or not.
//
// This class is not thread-safe and expects all calls to GetModelFile() and
// SendClientReportPhishingRequest() to be made on the UI thread.  We also
// expect that the calling thread runs a message loop and that there is a FILE
// thread running to execute asynchronous file operations.

#ifndef CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_H_
#define CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_H_
#pragma once

#include <map>
#include <queue>
#include <string>
#include <utility>
#include <vector>

#include "base/basictypes.h"
#include "base/callback.h"
#include "base/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_callback_factory.h"
#include "base/memory/scoped_ptr.h"
#include "base/platform_file.h"
#include "base/task.h"
#include "base/time.h"
#include "chrome/common/net/url_fetcher.h"
#include "googleurl/src/gurl.h"
#include "net/base/net_util.h"

namespace net {
class URLRequestContextGetter;
class URLRequestStatus;
}  // namespace net

namespace safe_browsing {
class ClientPhishingRequest;

class ClientSideDetectionService : public URLFetcher::Delegate {
 public:
  typedef Callback1<base::PlatformFile>::Type OpenModelDoneCallback;

  typedef Callback2<GURL /* phishing URL */, bool /* is phishing */>::Type
      ClientReportPhishingRequestCallback;

  virtual ~ClientSideDetectionService();

  // Creates a client-side detection service and starts fetching the client-side
  // detection model if necessary.  The model will be stored in |model_path|.
  // The caller takes ownership of the object.  This function may return NULL.
  static ClientSideDetectionService* Create(
      const FilePath& model_path,
      net::URLRequestContextGetter* request_context_getter);

  // From the URLFetcher::Delegate interface.
  virtual void OnURLFetchComplete(const URLFetcher* source,
                                  const GURL& url,
                                  const net::URLRequestStatus& status,
                                  int response_code,
                                  const ResponseCookies& cookies,
                                  const std::string& data);

  // Gets the model file descriptor once the model is ready and stored
  // on disk.  If there was an error the callback is called and the
  // platform file is set to kInvalidPlatformFileValue. The
  // ClientSideDetectionService takes ownership of the |callback|.
  // The callback is always called after GetModelFile() returns and on the
  // same thread as GetModelFile() was called.
  void GetModelFile(OpenModelDoneCallback* callback);

  // Sends a request to the SafeBrowsing servers with the ClientPhishingRequest.
  // The URL scheme of the |url()| in the request should be HTTP.  This method
  // takes ownership of the |verdict| as well as the |callback| and calls the
  // the callback once the result has come back from the server or if an error
  // occurs during the fetch.  If an error occurs the phishing verdict will
  // always be false.  The callback is always called after
  // SendClientReportPhishingRequest() returns and on the same thread as
  // SendClientReportPhishingRequest() was called.
  virtual void SendClientReportPhishingRequest(
      ClientPhishingRequest* verdict,
      ClientReportPhishingRequestCallback* callback);

  // Returns true if the given IP address string falls within a private
  // (unroutable) network block.  Pages which are hosted on these IP addresses
  // are exempt from client-side phishing detection.  This is called by the
  // ClientSideDetectionHost prior to sending the renderer a
  // SafeBrowsingMsg_StartPhishingDetection IPC.
  //
  // ip_address should be a dotted IPv4 address, or an unbracketed IPv6
  // address.
  virtual bool IsPrivateIPAddress(const std::string& ip_address) const;

  // Returns true and sets is_phishing if url is in the cache and valid.
  virtual bool GetValidCachedResult(const GURL& url, bool* is_phishing);

  // Returns true if the url is in the cache.
  virtual bool IsInCache(const GURL& url);

  // Returns true if we have sent more than kMaxReportsPerInterval in the last
  // kReportsInterval.
  virtual bool OverReportLimit();

 protected:
  // Use Create() method to create an instance of this object.
  ClientSideDetectionService(
      const FilePath& model_path,
      net::URLRequestContextGetter* request_context_getter);

 private:
  friend class ClientSideDetectionServiceTest;

  enum ModelStatus {
    // It's unclear whether or not the model was already fetched.
    UNKNOWN_STATUS,
    // Model is fetched and is stored on disk.
    READY_STATUS,
    // Error occured during fetching or writing.
    ERROR_STATUS,
  };

  // CacheState holds all information necessary to respond to a caller without
  // actually making a HTTP request.
  struct CacheState {
    bool is_phishing;
    base::Time timestamp;

    CacheState(bool phish, base::Time time);
  };
  typedef std::map<GURL, linked_ptr<CacheState> > PhishingCache;

  // A tuple of (IP address block, prefix size) representing a private
  // IP address range.
  typedef std::pair<net::IPAddressNumber, size_t> AddressRange;

  static const char kClientReportPhishingUrl[];
  static const char kClientModelUrl[];
  static const int kMaxReportsPerInterval;
  static const base::TimeDelta kReportsInterval;
  static const base::TimeDelta kNegativeCacheInterval;
  static const base::TimeDelta kPositiveCacheInterval;

  // Sets the model status and invokes all the pending callbacks in
  // |open_callbacks_| with the current |model_file_| as parameter.
  void SetModelStatus(ModelStatus status);

  // Called once the initial open() of the model file is done.  If the file
  // exists we're done and we can call all the pending callbacks.  If the
  // file doesn't exist this method will asynchronously fetch the model
  // from the server by invoking StartFetchingModel().
  void OpenModelFileDone(base::PlatformFileError error_code,
                         base::PassPlatformFile file,
                         bool created);

  // Callback that is invoked once the attempt to create the model
  // file on disk is done.  If the file was created successfully we
  // start writing the model to disk (asynchronously).  Otherwise, we
  // give up and send an invalid platform file to all the pending callbacks.
  void CreateModelFileDone(base::PlatformFileError error_code,
                           base::PassPlatformFile file,
                           bool created);

  // Callback is invoked once we're done writing the model file to disk.
  // If everything went well then |model_file_| is a valid platform file
  // that can be sent to all the pending callbacks.  If an error occurs
  // we give up and send an invalid platform file to all the pending callbacks.
  void WriteModelFileDone(base::PlatformFileError error_code,
                          int bytes_written);

  // Helper function which closes the |model_file_| if necessary.
  void CloseModelFile();

  // Starts sending the request to the client-side detection frontends.
  // This method takes ownership of both pointers.
  void StartClientReportPhishingRequest(
      ClientPhishingRequest* verdict,
      ClientReportPhishingRequestCallback* callback);

  // Starts getting the model file.
  void StartGetModelFile(OpenModelDoneCallback* callback);

  // Called by OnURLFetchComplete to handle the response from fetching the
  // model.
  void HandleModelResponse(const URLFetcher* source,
                           const GURL& url,
                           const net::URLRequestStatus& status,
                           int response_code,
                           const ResponseCookies& cookies,
                           const std::string& data);

  // Called by OnURLFetchComplete to handle the server response from
  // sending the client-side phishing request.
  void HandlePhishingVerdict(const URLFetcher* source,
                             const GURL& url,
                             const net::URLRequestStatus& status,
                             int response_code,
                             const ResponseCookies& cookies,
                             const std::string& data);

  // Invalidate cache results which are no longer useful.
  void UpdateCache();

  // Get the number of phishing reports that we have sent over kReportsInterval
  int GetNumReports();

  // Initializes the |private_networks_| vector with the network blocks
  // that we consider non-public IP addresses.  Returns true on success.
  bool InitializePrivateNetworks();

  FilePath model_path_;
  ModelStatus model_status_;
  base::PlatformFile model_file_;
  scoped_ptr<URLFetcher> model_fetcher_;
  scoped_ptr<std::string> tmp_model_string_;
  std::vector<OpenModelDoneCallback*> open_callbacks_;

  // Map of client report phishing request to the corresponding callback that
  // has to be invoked when the request is done.
  struct ClientReportInfo;
  std::map<const URLFetcher*, ClientReportInfo*> client_phishing_reports_;

  // Cache of completed requests. Used to satisfy requests for the same urls
  // as long as the next request falls within our caching window (which is
  // determined by kNegativeCacheInterval and kPositiveCacheInterval). The
  // size of this cache is limited by kMaxReportsPerDay *
  // ceil(InDays(max(kNegativeCacheInterval, kPositiveCacheInterval))).
  // TODO(gcasto): Serialize this so that it doesn't reset on browser restart.
  PhishingCache cache_;

  // Timestamp of when we sent a phishing request. Used to limit the number
  // of phishing requests that we send in a day.
  // TODO(gcasto): Serialize this so that it doesn't reset on browser restart.
  std::queue<base::Time> phishing_report_times_;

  // Used to asynchronously call the callbacks for GetModelFile and
  // SendClientReportPhishingRequest.
  ScopedRunnableMethodFactory<ClientSideDetectionService> method_factory_;

  // The client-side detection service object (this) might go away before some
  // of the callbacks are done (e.g., asynchronous file operations).  The
  // callback factory will revoke all pending callbacks if this goes away to
  // avoid a crash.
  base::ScopedCallbackFactory<ClientSideDetectionService> callback_factory_;

  // The context we use to issue network requests.
  scoped_refptr<net::URLRequestContextGetter> request_context_getter_;

  // The network blocks that we consider private IP address ranges.
  std::vector<AddressRange> private_networks_;

  DISALLOW_COPY_AND_ASSIGN(ClientSideDetectionService);
};

}  // namepsace safe_browsing

#endif  // CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_H_