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 |