summaryrefslogtreecommitdiffstats
path: root/components/safe_browsing_db/v4_update_protocol_manager.cc
diff options
context:
space:
mode:
authorvakh <vakh@chromium.org>2016-03-25 18:15:54 -0700
committerCommit bot <commit-bot@chromium.org>2016-03-26 01:16:54 +0000
commitddcb01e6116caefe5b89c589455df887ba18b1cd (patch)
tree31ff974d024fa0c8698339e085fb8c9e115a460e /components/safe_browsing_db/v4_update_protocol_manager.cc
parentd0b8c473ba902e27b5d844c72be1d8fd179de177 (diff)
downloadchromium_src-ddcb01e6116caefe5b89c589455df887ba18b1cd.zip
chromium_src-ddcb01e6116caefe5b89c589455df887ba18b1cd.tar.gz
chromium_src-ddcb01e6116caefe5b89c589455df887ba18b1cd.tar.bz2
v4_update_protocol_manager: Basic implementation with TODOs
BUG=543161 Review URL: https://codereview.chromium.org/1727033003 Cr-Commit-Position: refs/heads/master@{#383433}
Diffstat (limited to 'components/safe_browsing_db/v4_update_protocol_manager.cc')
-rw-r--r--components/safe_browsing_db/v4_update_protocol_manager.cc277
1 files changed, 277 insertions, 0 deletions
diff --git a/components/safe_browsing_db/v4_update_protocol_manager.cc b/components/safe_browsing_db/v4_update_protocol_manager.cc
new file mode 100644
index 0000000..c5dbfb5
--- /dev/null
+++ b/components/safe_browsing_db/v4_update_protocol_manager.cc
@@ -0,0 +1,277 @@
+// Copyright 2016 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.
+
+#include "components/safe_browsing_db/v4_update_protocol_manager.h"
+
+#include <utility>
+
+#include "base/base64.h"
+#include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/timer/timer.h"
+#include "components/safe_browsing_db/safebrowsing.pb.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_context_getter.h"
+
+using base::Time;
+using base::TimeDelta;
+
+namespace {
+
+// Enumerate parsing failures for histogramming purposes. DO NOT CHANGE
+// THE ORDERING OF THESE VALUES.
+enum ParseResultType {
+ // Error parsing the protocol buffer from a string.
+ PARSE_FROM_STRING_ERROR = 0,
+
+ // No platform_type set in the response.
+ NO_PLATFORM_TYPE_ERROR = 1,
+
+ // No threat_entry_type set in the response.
+ NO_THREAT_ENTRY_TYPE_ERROR = 2,
+
+ // No threat_type set in the response.
+ NO_THREAT_TYPE_ERROR = 3,
+
+ // No state set in the response for one or more lists.
+ NO_STATE_ERROR = 4,
+
+ // Memory space for histograms is determined by the max. ALWAYS
+ // ADD NEW VALUES BEFORE THIS ONE.
+ PARSE_RESULT_TYPE_MAX = 5
+};
+
+// Record parsing errors of an update result.
+void RecordParseUpdateResult(ParseResultType result_type) {
+ UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.ParseV4UpdateResult", result_type,
+ PARSE_RESULT_TYPE_MAX);
+}
+
+void RecordUpdateResult(safe_browsing::V4OperationResult result) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "SafeBrowsing.V4UpdateResult", result,
+ safe_browsing::V4OperationResult::OPERATION_RESULT_MAX);
+}
+
+} // namespace
+
+namespace safe_browsing {
+
+// The default V4UpdateProtocolManagerFactory.
+class V4UpdateProtocolManagerFactoryImpl
+ : public V4UpdateProtocolManagerFactory {
+ public:
+ V4UpdateProtocolManagerFactoryImpl() {}
+ ~V4UpdateProtocolManagerFactoryImpl() override {}
+ V4UpdateProtocolManager* CreateProtocolManager(
+ net::URLRequestContextGetter* request_context_getter,
+ const V4ProtocolConfig& config) override {
+ return new V4UpdateProtocolManager(request_context_getter, config);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(V4UpdateProtocolManagerFactoryImpl);
+};
+
+// V4UpdateProtocolManager implementation --------------------------------
+
+// static
+V4UpdateProtocolManagerFactory* V4UpdateProtocolManager::factory_ = NULL;
+
+// static
+V4UpdateProtocolManager* V4UpdateProtocolManager::Create(
+ net::URLRequestContextGetter* request_context_getter,
+ const V4ProtocolConfig& config) {
+ if (!factory_)
+ factory_ = new V4UpdateProtocolManagerFactoryImpl();
+ return factory_->CreateProtocolManager(request_context_getter, config);
+}
+
+void V4UpdateProtocolManager::ResetUpdateErrors() {
+ update_error_count_ = 0;
+ update_back_off_mult_ = 1;
+}
+
+V4UpdateProtocolManager::V4UpdateProtocolManager(
+ net::URLRequestContextGetter* request_context_getter,
+ const V4ProtocolConfig& config)
+ : update_error_count_(0),
+ update_back_off_mult_(1),
+ next_update_time_(Time::Now()),
+ config_(config),
+ request_context_getter_(request_context_getter),
+ url_fetcher_id_(0) {}
+
+V4UpdateProtocolManager::~V4UpdateProtocolManager() {
+}
+
+std::string V4UpdateProtocolManager::GetUpdateRequest(
+ const base::hash_set<UpdateListIdentifier>& lists_to_update,
+ const base::hash_map<UpdateListIdentifier, std::string>&
+ current_list_states) {
+ // Build the request. Client info and client states are not added to the
+ // request protocol buffer. Client info is passed as params in the url.
+ FetchThreatListUpdatesRequest request;
+ for (const auto& list_to_update : lists_to_update) {
+ ListUpdateRequest* list_update_request = request.add_list_update_requests();
+ list_update_request->set_platform_type(list_to_update.platform_type);
+ list_update_request->set_threat_entry_type(
+ list_to_update.threat_entry_type);
+ list_update_request->set_threat_type(list_to_update.threat_type);
+
+ // If the current state of the list is available, add that to the proto.
+ base::hash_map<UpdateListIdentifier, std::string>::const_iterator
+ list_iter = current_list_states.find(list_to_update);
+ if (list_iter != current_list_states.end()) {
+ list_update_request->set_state(list_iter->second);
+ }
+ }
+
+ // Serialize and Base64 encode.
+ std::string req_data, req_base64;
+ request.SerializeToString(&req_data);
+ base::Base64Encode(req_data, &req_base64);
+
+ return req_base64;
+}
+
+bool V4UpdateProtocolManager::ParseUpdateResponse(
+ const std::string& data,
+ std::vector<ListUpdateResponse>* list_update_responses) {
+ FetchThreatListUpdatesResponse response;
+
+ if (!response.ParseFromString(data)) {
+ RecordParseUpdateResult(PARSE_FROM_STRING_ERROR);
+ return false;
+ }
+
+ if (response.has_minimum_wait_duration()) {
+ // Seconds resolution is good enough so we ignore the nanos field.
+ base::TimeDelta next_update_interval = base::TimeDelta::FromSeconds(
+ response.minimum_wait_duration().seconds());
+ next_update_time_ = Time::Now() + next_update_interval;
+ }
+
+ // TODO(vakh): Do something useful with this response.
+ for (const ListUpdateResponse& list_update_response :
+ response.list_update_responses()) {
+ if (!list_update_response.has_platform_type()) {
+ RecordParseUpdateResult(NO_PLATFORM_TYPE_ERROR);
+ } else if (!list_update_response.has_threat_entry_type()) {
+ RecordParseUpdateResult(NO_THREAT_ENTRY_TYPE_ERROR);
+ } else if (!list_update_response.has_threat_type()) {
+ RecordParseUpdateResult(NO_THREAT_TYPE_ERROR);
+ } else if (!list_update_response.has_new_client_state()) {
+ RecordParseUpdateResult(NO_STATE_ERROR);
+ } else {
+ list_update_responses->push_back(list_update_response);
+ }
+ }
+ return true;
+}
+
+void V4UpdateProtocolManager::GetUpdates(
+ const base::hash_set<UpdateListIdentifier>& lists_to_update,
+ const base::hash_map<UpdateListIdentifier, std::string>&
+ current_list_states,
+ UpdateCallback callback) {
+ DCHECK(CalledOnValidThread());
+
+ // If an update request is already pending, return an empty result.
+ if (request_) {
+ RecordUpdateResult(V4OperationResult::ALREADY_PENDING_ERROR);
+ std::vector<ListUpdateResponse> list_update_responses;
+ callback.Run(list_update_responses);
+ return;
+ }
+
+ // We need to wait the minimum waiting duration, and if we are in backoff,
+ // we need to check if we're past the next allowed time. If we are, we can
+ // proceed with the request. If not, we are required to return empty results.
+ if (Time::Now() <= next_update_time_) {
+ if (update_error_count_) {
+ RecordUpdateResult(V4OperationResult::BACKOFF_ERROR);
+ } else {
+ RecordUpdateResult(V4OperationResult::MIN_WAIT_DURATION_ERROR);
+ }
+ std::vector<ListUpdateResponse> list_update_responses;
+ callback.Run(list_update_responses);
+ return;
+ }
+
+ std::string req_base64 =
+ GetUpdateRequest(lists_to_update, current_list_states);
+ GURL update_url = GetUpdateUrl(req_base64);
+
+ request_.reset(net::URLFetcher::Create(url_fetcher_id_++, update_url,
+ net::URLFetcher::GET, this)
+ .release());
+ callback_ = callback;
+
+ request_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
+ request_->SetRequestContext(request_context_getter_.get());
+ request_->Start();
+ //TODO(vakh): Handle request timeout.
+}
+
+// net::URLFetcherDelegate implementation ----------------------------------
+
+// SafeBrowsing request responses are handled here.
+void V4UpdateProtocolManager::OnURLFetchComplete(
+ const net::URLFetcher* source) {
+ DCHECK(CalledOnValidThread());
+
+ int response_code = source->GetResponseCode();
+ net::URLRequestStatus status = source->GetStatus();
+ V4ProtocolManagerUtil::RecordHttpResponseOrErrorCode(
+ "SafeBrowsing.V4UpdateHttpResponseOrErrorCode", status, response_code);
+
+ std::vector<ListUpdateResponse> list_update_responses;
+ if (status.is_success() && response_code == net::HTTP_OK) {
+ RecordUpdateResult(V4OperationResult::STATUS_200);
+ ResetUpdateErrors();
+ std::string data;
+ source->GetResponseAsString(&data);
+ if (!ParseUpdateResponse(data, &list_update_responses)) {
+ list_update_responses.clear();
+ RecordUpdateResult(V4OperationResult::PARSE_ERROR);
+ }
+ } else {
+ HandleUpdateError(Time::Now());
+
+ DVLOG(1) << "SafeBrowsing GetEncodedUpdates request for: "
+ << source->GetURL() << " failed with error: " << status.error()
+ << " and response code: " << response_code;
+
+ if (status.status() == net::URLRequestStatus::FAILED) {
+ RecordUpdateResult(V4OperationResult::NETWORK_ERROR);
+ } else {
+ RecordUpdateResult(V4OperationResult::HTTP_ERROR);
+ }
+ }
+
+ // Invoke the callback with list_update_responses, even if there was a parse
+ // error or an error response code (in which case list_update_responses will
+ // be empty). The caller can't be blocked indefinitely.
+ callback_.Run(list_update_responses);
+ request_.reset();
+}
+
+void V4UpdateProtocolManager::HandleUpdateError(const Time& now) {
+ DCHECK(CalledOnValidThread());
+ base::TimeDelta next = V4ProtocolManagerUtil::GetNextBackOffInterval(
+ &update_error_count_, &update_back_off_mult_);
+ next_update_time_ = now + next;
+}
+
+GURL V4UpdateProtocolManager::GetUpdateUrl(
+ const std::string& req_base64) const {
+ return V4ProtocolManagerUtil::GetRequestUrl(req_base64, "encodedUpdates",
+ config_);
+}
+
+} // namespace safe_browsing