| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/safe_browsing_db/v4_get_hash_protocol_manager.h" | 5 #include "components/safe_browsing_db/v4_get_hash_protocol_manager.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/base64.h" | 9 #include "base/base64.h" |
| 10 #include "base/logging.h" | |
| 11 #include "base/macros.h" | 10 #include "base/macros.h" |
| 12 #include "base/metrics/histogram_macros.h" | 11 #include "base/metrics/histogram_macros.h" |
| 13 #include "base/metrics/sparse_histogram.h" | |
| 14 #include "base/rand_util.h" | |
| 15 #include "base/stl_util.h" | |
| 16 #include "base/strings/string_util.h" | |
| 17 #include "base/strings/stringprintf.h" | |
| 18 #include "base/timer/timer.h" | 12 #include "base/timer/timer.h" |
| 19 #include "net/base/escape.h" | |
| 20 #include "net/base/load_flags.h" | 13 #include "net/base/load_flags.h" |
| 21 #include "net/base/net_errors.h" | |
| 22 #include "net/http/http_response_headers.h" | 14 #include "net/http/http_response_headers.h" |
| 23 #include "net/http/http_status_code.h" | 15 #include "net/http/http_status_code.h" |
| 24 #include "net/url_request/url_fetcher.h" | 16 #include "net/url_request/url_fetcher.h" |
| 25 #include "net/url_request/url_request_context_getter.h" | 17 #include "net/url_request/url_request_context_getter.h" |
| 26 #include "net/url_request/url_request_status.h" | |
| 27 | 18 |
| 28 using base::Time; | 19 using base::Time; |
| 29 using base::TimeDelta; | 20 using base::TimeDelta; |
| 30 | 21 |
| 31 namespace { | 22 namespace { |
| 32 | 23 |
| 33 // Enumerate parsing failures for histogramming purposes. DO NOT CHANGE | 24 // Enumerate parsing failures for histogramming purposes. DO NOT CHANGE |
| 34 // THE ORDERING OF THESE VALUES. | 25 // THE ORDERING OF THESE VALUES. |
| 35 enum ParseResultType { | 26 enum ParseResultType { |
| 36 // Error parsing the protocol buffer from a string. | 27 // Error parsing the protocol buffer from a string. |
| (...skipping 11 matching lines...) Expand all Loading... |
| 48 // A match in the response contained no metadata where metadata was | 39 // A match in the response contained no metadata where metadata was |
| 49 // expected. | 40 // expected. |
| 50 NO_METADATA_ERROR = 4, | 41 NO_METADATA_ERROR = 4, |
| 51 | 42 |
| 52 // A match in the response contained a ThreatType that was inconsistent | 43 // A match in the response contained a ThreatType that was inconsistent |
| 53 // with the other matches. | 44 // with the other matches. |
| 54 INCONSISTENT_THREAT_TYPE_ERROR = 5, | 45 INCONSISTENT_THREAT_TYPE_ERROR = 5, |
| 55 | 46 |
| 56 // Memory space for histograms is determined by the max. ALWAYS | 47 // Memory space for histograms is determined by the max. ALWAYS |
| 57 // ADD NEW VALUES BEFORE THIS ONE. | 48 // ADD NEW VALUES BEFORE THIS ONE. |
| 58 PARSE_GET_HASH_RESULT_MAX = 6 | 49 PARSE_RESULT_TYPE_MAX = 6 |
| 59 }; | 50 }; |
| 60 | 51 |
| 61 // Record parsing errors of a GetHash result. | 52 // Record parsing errors of a GetHash result. |
| 62 void RecordParseGetHashResult(ParseResultType result_type) { | 53 void RecordParseGetHashResult(ParseResultType result_type) { |
| 63 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.ParseV4HashResult", result_type, | 54 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.ParseV4HashResult", result_type, |
| 64 PARSE_GET_HASH_RESULT_MAX); | 55 PARSE_RESULT_TYPE_MAX); |
| 65 } | 56 } |
| 66 | 57 |
| 67 } // namespace | 58 } // namespace |
| 68 | 59 |
| 69 namespace safe_browsing { | 60 namespace safe_browsing { |
| 70 | 61 |
| 71 const char kUmaV4HashResponseMetricName[] = | 62 const char kUmaV4HashResponseMetricName[] = |
| 72 "SafeBrowsing.GetV4HashHttpResponseOrErrorCode"; | 63 "SafeBrowsing.GetV4HashHttpResponseOrErrorCode"; |
| 73 | 64 |
| 74 // The default V4GetHashProtocolManagerFactory. | 65 // The default V4GetHashProtocolManagerFactory. |
| 75 class V4GetHashProtocolManagerFactoryImpl | 66 class V4GetHashProtocolManagerFactoryImpl |
| 76 : public V4GetHashProtocolManagerFactory { | 67 : public V4GetHashProtocolManagerFactory { |
| 77 public: | 68 public: |
| 78 V4GetHashProtocolManagerFactoryImpl() {} | 69 V4GetHashProtocolManagerFactoryImpl() {} |
| 79 ~V4GetHashProtocolManagerFactoryImpl() override {} | 70 ~V4GetHashProtocolManagerFactoryImpl() override {} |
| 80 V4GetHashProtocolManager* CreateProtocolManager( | 71 V4GetHashProtocolManager* CreateProtocolManager( |
| 81 net::URLRequestContextGetter* request_context_getter, | 72 net::URLRequestContextGetter* request_context_getter, |
| 82 const V4GetHashProtocolConfig& config) override { | 73 const V4ProtocolConfig& config) override { |
| 83 return new V4GetHashProtocolManager(request_context_getter, config); | 74 return new V4GetHashProtocolManager(request_context_getter, config); |
| 84 } | 75 } |
| 85 | 76 |
| 86 private: | 77 private: |
| 87 DISALLOW_COPY_AND_ASSIGN(V4GetHashProtocolManagerFactoryImpl); | 78 DISALLOW_COPY_AND_ASSIGN(V4GetHashProtocolManagerFactoryImpl); |
| 88 }; | 79 }; |
| 89 | 80 |
| 90 // V4GetHashProtocolManager implementation -------------------------------- | 81 // V4GetHashProtocolManager implementation -------------------------------- |
| 91 | 82 |
| 92 // static | 83 // static |
| 93 V4GetHashProtocolManagerFactory* V4GetHashProtocolManager::factory_ = NULL; | 84 V4GetHashProtocolManagerFactory* V4GetHashProtocolManager::factory_ = NULL; |
| 94 | 85 |
| 95 // static | 86 // static |
| 96 V4GetHashProtocolManager* V4GetHashProtocolManager::Create( | 87 V4GetHashProtocolManager* V4GetHashProtocolManager::Create( |
| 97 net::URLRequestContextGetter* request_context_getter, | 88 net::URLRequestContextGetter* request_context_getter, |
| 98 const V4GetHashProtocolConfig& config) { | 89 const V4ProtocolConfig& config) { |
| 99 if (!factory_) | 90 if (!factory_) |
| 100 factory_ = new V4GetHashProtocolManagerFactoryImpl(); | 91 factory_ = new V4GetHashProtocolManagerFactoryImpl(); |
| 101 return factory_->CreateProtocolManager(request_context_getter, config); | 92 return factory_->CreateProtocolManager(request_context_getter, config); |
| 102 } | 93 } |
| 103 | 94 |
| 104 // static | |
| 105 // Backoff interval is MIN(((2^(n-1))*15 minutes) * (RAND + 1), 24 hours) where | |
| 106 // n is the number of consecutive errors. | |
| 107 base::TimeDelta V4GetHashProtocolManager::GetNextBackOffInterval( | |
| 108 size_t* error_count, | |
| 109 size_t* multiplier) { | |
| 110 DCHECK(multiplier && error_count); | |
| 111 (*error_count)++; | |
| 112 if (*error_count > 1 && *error_count < 9) { | |
| 113 // With error count 9 and above we will hit the 24 hour max interval. | |
| 114 // Cap the multiplier here to prevent integer overflow errors. | |
| 115 *multiplier *= 2; | |
| 116 } | |
| 117 base::TimeDelta next = | |
| 118 base::TimeDelta::FromMinutes(*multiplier * (1 + base::RandDouble()) * 15); | |
| 119 | |
| 120 base::TimeDelta day = base::TimeDelta::FromHours(24); | |
| 121 | |
| 122 if (next < day) | |
| 123 return next; | |
| 124 else | |
| 125 return day; | |
| 126 } | |
| 127 | |
| 128 void V4GetHashProtocolManager::ResetGetHashErrors() { | 95 void V4GetHashProtocolManager::ResetGetHashErrors() { |
| 129 gethash_error_count_ = 0; | 96 gethash_error_count_ = 0; |
| 130 gethash_back_off_mult_ = 1; | 97 gethash_back_off_mult_ = 1; |
| 131 } | 98 } |
| 132 | 99 |
| 133 V4GetHashProtocolManager::V4GetHashProtocolManager( | 100 V4GetHashProtocolManager::V4GetHashProtocolManager( |
| 134 net::URLRequestContextGetter* request_context_getter, | 101 net::URLRequestContextGetter* request_context_getter, |
| 135 const V4GetHashProtocolConfig& config) | 102 const V4ProtocolConfig& config) |
| 136 : gethash_error_count_(0), | 103 : gethash_error_count_(0), |
| 137 gethash_back_off_mult_(1), | 104 gethash_back_off_mult_(1), |
| 138 next_gethash_time_(Time::FromDoubleT(0)), | 105 next_gethash_time_(Time::FromDoubleT(0)), |
| 139 version_(config.version), | 106 config_(config), |
| 140 client_name_(config.client_name), | |
| 141 key_param_(config.key_param), | |
| 142 request_context_getter_(request_context_getter), | 107 request_context_getter_(request_context_getter), |
| 143 url_fetcher_id_(0) { | 108 url_fetcher_id_(0) { |
| 144 DCHECK(!version_.empty()); | |
| 145 } | 109 } |
| 146 | 110 |
| 147 // static | 111 // static |
| 148 void V4GetHashProtocolManager::RecordGetHashResult(ResultType result_type) { | 112 void V4GetHashProtocolManager::RecordGetHashResult(ResultType result_type) { |
| 149 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.GetV4HashResult", result_type, | 113 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.GetV4HashResult", result_type, |
| 150 GET_HASH_RESULT_MAX); | 114 GET_HASH_RESULT_MAX); |
| 151 } | 115 } |
| 152 | 116 |
| 153 void V4GetHashProtocolManager::RecordHttpResponseOrErrorCode( | |
| 154 const char* metric_name, | |
| 155 const net::URLRequestStatus& status, | |
| 156 int response_code) { | |
| 157 UMA_HISTOGRAM_SPARSE_SLOWLY( | |
| 158 metric_name, status.is_success() ? response_code : status.error()); | |
| 159 } | |
| 160 | |
| 161 V4GetHashProtocolManager::~V4GetHashProtocolManager() { | 117 V4GetHashProtocolManager::~V4GetHashProtocolManager() { |
| 162 // Delete in-progress SafeBrowsing requests. | 118 // Delete in-progress SafeBrowsing requests. |
| 163 STLDeleteContainerPairFirstPointers(hash_requests_.begin(), | 119 STLDeleteContainerPairFirstPointers(hash_requests_.begin(), |
| 164 hash_requests_.end()); | 120 hash_requests_.end()); |
| 165 hash_requests_.clear(); | 121 hash_requests_.clear(); |
| 166 } | 122 } |
| 167 | 123 |
| 168 std::string V4GetHashProtocolManager::GetHashRequest( | 124 std::string V4GetHashProtocolManager::GetHashRequest( |
| 169 const std::vector<SBPrefix>& prefixes, | 125 const std::vector<SBPrefix>& prefixes, |
| 170 const std::vector<PlatformType>& platforms, | 126 const std::vector<PlatformType>& platforms, |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 331 | 287 |
| 332 HashRequests::iterator it = hash_requests_.find(source); | 288 HashRequests::iterator it = hash_requests_.find(source); |
| 333 DCHECK(it != hash_requests_.end()) << "Request not found"; | 289 DCHECK(it != hash_requests_.end()) << "Request not found"; |
| 334 | 290 |
| 335 // FindFullHashes response. | 291 // FindFullHashes response. |
| 336 // Reset the scoped pointer so the fetcher gets destroyed properly. | 292 // Reset the scoped pointer so the fetcher gets destroyed properly. |
| 337 scoped_ptr<const net::URLFetcher> fetcher(it->first); | 293 scoped_ptr<const net::URLFetcher> fetcher(it->first); |
| 338 | 294 |
| 339 int response_code = source->GetResponseCode(); | 295 int response_code = source->GetResponseCode(); |
| 340 net::URLRequestStatus status = source->GetStatus(); | 296 net::URLRequestStatus status = source->GetStatus(); |
| 341 RecordHttpResponseOrErrorCode(kUmaV4HashResponseMetricName, status, | 297 V4ProtocolManagerUtil::RecordHttpResponseOrErrorCode( |
| 342 response_code); | 298 kUmaV4HashResponseMetricName, status, response_code); |
| 343 | 299 |
| 344 const FullHashCallback& callback = it->second; | 300 const FullHashCallback& callback = it->second; |
| 345 std::vector<SBFullHashResult> full_hashes; | 301 std::vector<SBFullHashResult> full_hashes; |
| 346 base::TimeDelta negative_cache_duration; | 302 base::TimeDelta negative_cache_duration; |
| 347 if (status.is_success() && response_code == net::HTTP_OK) { | 303 if (status.is_success() && response_code == net::HTTP_OK) { |
| 348 RecordGetHashResult(GET_HASH_STATUS_200); | 304 RecordGetHashResult(GET_HASH_STATUS_200); |
| 349 ResetGetHashErrors(); | 305 ResetGetHashErrors(); |
| 350 std::string data; | 306 std::string data; |
| 351 source->GetResponseAsString(&data); | 307 source->GetResponseAsString(&data); |
| 352 if (!ParseHashResponse(data, &full_hashes, &negative_cache_duration)) { | 308 if (!ParseHashResponse(data, &full_hashes, &negative_cache_duration)) { |
| (...skipping 17 matching lines...) Expand all Loading... |
| 370 // Invoke the callback with full_hashes, even if there was a parse error or | 326 // Invoke the callback with full_hashes, even if there was a parse error or |
| 371 // an error response code (in which case full_hashes will be empty). The | 327 // an error response code (in which case full_hashes will be empty). The |
| 372 // caller can't be blocked indefinitely. | 328 // caller can't be blocked indefinitely. |
| 373 callback.Run(full_hashes, negative_cache_duration); | 329 callback.Run(full_hashes, negative_cache_duration); |
| 374 | 330 |
| 375 hash_requests_.erase(it); | 331 hash_requests_.erase(it); |
| 376 } | 332 } |
| 377 | 333 |
| 378 void V4GetHashProtocolManager::HandleGetHashError(const Time& now) { | 334 void V4GetHashProtocolManager::HandleGetHashError(const Time& now) { |
| 379 DCHECK(CalledOnValidThread()); | 335 DCHECK(CalledOnValidThread()); |
| 380 base::TimeDelta next = | 336 base::TimeDelta next = V4ProtocolManagerUtil::GetNextBackOffInterval( |
| 381 GetNextBackOffInterval(&gethash_error_count_, &gethash_back_off_mult_); | 337 &gethash_error_count_, &gethash_back_off_mult_); |
| 382 next_gethash_time_ = now + next; | 338 next_gethash_time_ = now + next; |
| 383 } | 339 } |
| 384 | 340 |
| 385 // The API hash call uses the pver4 Safe Browsing server. | 341 GURL V4GetHashProtocolManager::GetHashUrl(const std::string& req_base64) const { |
| 386 GURL V4GetHashProtocolManager::GetHashUrl( | 342 return V4ProtocolManagerUtil::GetRequestUrl(req_base64, "encodedFullHashes", |
| 387 const std::string& request_base64) const { | 343 config_); |
| 388 std::string url = | |
| 389 ComposePver4Url(kSbV4UrlPrefix, "encodedFullHashes", request_base64, | |
| 390 client_name_, version_, key_param_); | |
| 391 return GURL(url); | |
| 392 } | 344 } |
| 393 | 345 |
| 394 // static | |
| 395 std::string V4GetHashProtocolManager::ComposePver4Url( | |
| 396 const std::string& prefix, | |
| 397 const std::string& method, | |
| 398 const std::string& request_base64, | |
| 399 const std::string& client_id, | |
| 400 const std::string& version, | |
| 401 const std::string& key_param) { | |
| 402 DCHECK(!prefix.empty() && !method.empty() && !client_id.empty() && | |
| 403 !version.empty()); | |
| 404 std::string url = | |
| 405 base::StringPrintf("%s/%s/%s?alt=proto&client_id=%s&client_version=%s", | |
| 406 prefix.c_str(), method.c_str(), request_base64.c_str(), | |
| 407 client_id.c_str(), version.c_str()); | |
| 408 if (!key_param.empty()) { | |
| 409 base::StringAppendF(&url, "&key=%s", | |
| 410 net::EscapeQueryParamValue(key_param, true).c_str()); | |
| 411 } | |
| 412 return url; | |
| 413 } | |
| 414 | 346 |
| 415 } // namespace safe_browsing | 347 } // namespace safe_browsing |
| OLD | NEW |