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 | |
34 // THE ORDERING OF THESE VALUES. | |
35 enum ParseResultType { | |
36 // Error parsing the protocol buffer from a string. | |
37 PARSE_FROM_STRING_ERROR = 0, | |
38 | |
39 // A match in the response had an unexpected THREAT_ENTRY_TYPE. | |
40 UNEXPECTED_THREAT_ENTRY_TYPE_ERROR = 1, | |
41 | |
42 // A match in the response had an unexpected THREAT_TYPE. | |
43 UNEXPECTED_THREAT_TYPE_ERROR = 2, | |
44 | |
45 // A match in the response had an unexpected PLATFORM_TYPE. | |
46 UNEXPECTED_PLATFORM_TYPE_ERROR = 3, | |
47 | |
48 // A match in the response contained no metadata where metadata was | |
49 // expected. | |
50 NO_METADATA_ERROR = 4, | |
51 | |
52 // A match in the response contained a ThreatType that was inconsistent | |
53 // with the other matches. | |
54 INCONSISTENT_THREAT_TYPE_ERROR = 5, | |
55 | |
56 // Memory space for histograms is determined by the max. ALWAYS | |
57 // ADD NEW VALUES BEFORE THIS ONE. | |
58 PARSE_GET_HASH_RESULT_MAX = 6 | |
59 }; | |
60 | |
61 // Record parsing errors of a GetHash result. | 24 // Record parsing errors of a GetHash result. |
62 void RecordParseGetHashResult(ParseResultType result_type) { | 25 void RecordParseGetHashResult(ParseResultType result_type) { |
63 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.ParseV4HashResult", result_type, | 26 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.ParseV4HashResult", result_type, |
64 PARSE_GET_HASH_RESULT_MAX); | 27 PARSE_RESULT_TYPE_MAX); |
65 } | 28 } |
66 | 29 |
67 } // namespace | 30 } // namespace |
68 | 31 |
69 namespace safe_browsing { | 32 namespace safe_browsing { |
70 | 33 |
71 const char kUmaV4HashResponseMetricName[] = | 34 const char kUmaV4HashResponseMetricName[] = |
72 "SafeBrowsing.GetV4HashHttpResponseOrErrorCode"; | 35 "SafeBrowsing.GetV4HashHttpResponseOrErrorCode"; |
73 | 36 |
74 // The default V4GetHashProtocolManagerFactory. | 37 // The default V4GetHashProtocolManagerFactory. |
75 class V4GetHashProtocolManagerFactoryImpl | 38 class V4GetHashProtocolManagerFactoryImpl |
76 : public V4GetHashProtocolManagerFactory { | 39 : public V4GetHashProtocolManagerFactory { |
77 public: | 40 public: |
78 V4GetHashProtocolManagerFactoryImpl() {} | 41 V4GetHashProtocolManagerFactoryImpl() {} |
79 ~V4GetHashProtocolManagerFactoryImpl() override {} | 42 ~V4GetHashProtocolManagerFactoryImpl() override {} |
80 V4GetHashProtocolManager* CreateProtocolManager( | 43 V4GetHashProtocolManager* CreateProtocolManager( |
81 net::URLRequestContextGetter* request_context_getter, | 44 net::URLRequestContextGetter* request_context_getter, |
82 const V4GetHashProtocolConfig& config) override { | 45 const V4ProtocolConfig& config) override { |
83 return new V4GetHashProtocolManager(request_context_getter, config); | 46 return new V4GetHashProtocolManager(request_context_getter, config); |
84 } | 47 } |
85 | 48 |
86 private: | 49 private: |
87 DISALLOW_COPY_AND_ASSIGN(V4GetHashProtocolManagerFactoryImpl); | 50 DISALLOW_COPY_AND_ASSIGN(V4GetHashProtocolManagerFactoryImpl); |
88 }; | 51 }; |
89 | 52 |
90 // V4GetHashProtocolManager implementation -------------------------------- | 53 // V4GetHashProtocolManager implementation -------------------------------- |
91 | 54 |
92 // static | 55 // static |
93 V4GetHashProtocolManagerFactory* V4GetHashProtocolManager::factory_ = NULL; | 56 V4GetHashProtocolManagerFactory* V4GetHashProtocolManager::factory_ = NULL; |
94 | 57 |
95 // static | 58 // static |
96 V4GetHashProtocolManager* V4GetHashProtocolManager::Create( | 59 V4GetHashProtocolManager* V4GetHashProtocolManager::Create( |
97 net::URLRequestContextGetter* request_context_getter, | 60 net::URLRequestContextGetter* request_context_getter, |
98 const V4GetHashProtocolConfig& config) { | 61 const V4ProtocolConfig& config) { |
99 if (!factory_) | 62 if (!factory_) |
100 factory_ = new V4GetHashProtocolManagerFactoryImpl(); | 63 factory_ = new V4GetHashProtocolManagerFactoryImpl(); |
101 return factory_->CreateProtocolManager(request_context_getter, config); | 64 return factory_->CreateProtocolManager(request_context_getter, config); |
102 } | 65 } |
103 | 66 |
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() { | 67 void V4GetHashProtocolManager::ResetGetHashErrors() { |
129 gethash_error_count_ = 0; | 68 gethash_error_count_ = 0; |
130 gethash_back_off_mult_ = 1; | 69 gethash_back_off_mult_ = 1; |
131 } | 70 } |
132 | 71 |
133 V4GetHashProtocolManager::V4GetHashProtocolManager( | 72 V4GetHashProtocolManager::V4GetHashProtocolManager( |
134 net::URLRequestContextGetter* request_context_getter, | 73 net::URLRequestContextGetter* request_context_getter, |
135 const V4GetHashProtocolConfig& config) | 74 const V4ProtocolConfig& config) |
136 : gethash_error_count_(0), | 75 : gethash_error_count_(0), |
137 gethash_back_off_mult_(1), | 76 gethash_back_off_mult_(1), |
138 next_gethash_time_(Time::FromDoubleT(0)), | 77 next_gethash_time_(Time::FromDoubleT(0)), |
139 version_(config.version), | 78 config_(config), |
140 client_name_(config.client_name), | |
141 key_param_(config.key_param), | |
142 request_context_getter_(request_context_getter), | 79 request_context_getter_(request_context_getter), |
143 url_fetcher_id_(0) { | 80 url_fetcher_id_(0) { |
144 DCHECK(!version_.empty()); | |
145 } | 81 } |
146 | 82 |
147 // static | 83 // static |
148 void V4GetHashProtocolManager::RecordGetHashResult(ResultType result_type) { | 84 void V4GetHashProtocolManager::RecordGetHashResult(ResultType result_type) { |
149 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.GetV4HashResult", result_type, | 85 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.GetV4HashResult", result_type, |
150 GET_HASH_RESULT_MAX); | 86 GET_HASH_RESULT_MAX); |
151 } | 87 } |
152 | 88 |
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() { | 89 V4GetHashProtocolManager::~V4GetHashProtocolManager() { |
162 // Delete in-progress SafeBrowsing requests. | 90 // Delete in-progress SafeBrowsing requests. |
163 STLDeleteContainerPairFirstPointers(hash_requests_.begin(), | 91 STLDeleteContainerPairFirstPointers(hash_requests_.begin(), |
164 hash_requests_.end()); | 92 hash_requests_.end()); |
165 hash_requests_.clear(); | 93 hash_requests_.clear(); |
166 } | 94 } |
167 | 95 |
168 std::string V4GetHashProtocolManager::GetHashRequest( | 96 std::string V4GetHashProtocolManager::GetHashRequest( |
169 const std::vector<SBPrefix>& prefixes, | 97 const std::vector<SBPrefix>& prefixes, |
170 const std::vector<PlatformType>& platforms, | 98 const std::vector<PlatformType>& platforms, |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
331 | 259 |
332 HashRequests::iterator it = hash_requests_.find(source); | 260 HashRequests::iterator it = hash_requests_.find(source); |
333 DCHECK(it != hash_requests_.end()) << "Request not found"; | 261 DCHECK(it != hash_requests_.end()) << "Request not found"; |
334 | 262 |
335 // FindFullHashes response. | 263 // FindFullHashes response. |
336 // Reset the scoped pointer so the fetcher gets destroyed properly. | 264 // Reset the scoped pointer so the fetcher gets destroyed properly. |
337 scoped_ptr<const net::URLFetcher> fetcher(it->first); | 265 scoped_ptr<const net::URLFetcher> fetcher(it->first); |
338 | 266 |
339 int response_code = source->GetResponseCode(); | 267 int response_code = source->GetResponseCode(); |
340 net::URLRequestStatus status = source->GetStatus(); | 268 net::URLRequestStatus status = source->GetStatus(); |
341 RecordHttpResponseOrErrorCode(kUmaV4HashResponseMetricName, status, | 269 V4ProtocolManagerUtil::RecordHttpResponseOrErrorCode( |
342 response_code); | 270 kUmaV4HashResponseMetricName, status, response_code); |
343 | 271 |
344 const FullHashCallback& callback = it->second; | 272 const FullHashCallback& callback = it->second; |
345 std::vector<SBFullHashResult> full_hashes; | 273 std::vector<SBFullHashResult> full_hashes; |
346 base::TimeDelta negative_cache_duration; | 274 base::TimeDelta negative_cache_duration; |
347 if (status.is_success() && response_code == net::HTTP_OK) { | 275 if (status.is_success() && response_code == net::HTTP_OK) { |
348 RecordGetHashResult(GET_HASH_STATUS_200); | 276 RecordGetHashResult(GET_HASH_STATUS_200); |
349 ResetGetHashErrors(); | 277 ResetGetHashErrors(); |
350 std::string data; | 278 std::string data; |
351 source->GetResponseAsString(&data); | 279 source->GetResponseAsString(&data); |
352 if (!ParseHashResponse(data, &full_hashes, &negative_cache_duration)) { | 280 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 | 298 // 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 | 299 // an error response code (in which case full_hashes will be empty). The |
372 // caller can't be blocked indefinitely. | 300 // caller can't be blocked indefinitely. |
373 callback.Run(full_hashes, negative_cache_duration); | 301 callback.Run(full_hashes, negative_cache_duration); |
374 | 302 |
375 hash_requests_.erase(it); | 303 hash_requests_.erase(it); |
376 } | 304 } |
377 | 305 |
378 void V4GetHashProtocolManager::HandleGetHashError(const Time& now) { | 306 void V4GetHashProtocolManager::HandleGetHashError(const Time& now) { |
379 DCHECK(CalledOnValidThread()); | 307 DCHECK(CalledOnValidThread()); |
380 base::TimeDelta next = | 308 base::TimeDelta next = V4ProtocolManagerUtil::GetNextBackOffInterval( |
381 GetNextBackOffInterval(&gethash_error_count_, &gethash_back_off_mult_); | 309 &gethash_error_count_, &gethash_back_off_mult_); |
382 next_gethash_time_ = now + next; | 310 next_gethash_time_ = now + next; |
383 } | 311 } |
384 | 312 |
385 // The API hash call uses the pver4 Safe Browsing server. | 313 GURL V4GetHashProtocolManager::GetHashUrl(const std::string& req_base64) const { |
386 GURL V4GetHashProtocolManager::GetHashUrl( | 314 return V4ProtocolManagerUtil::GetRequestUrl(req_base64, "encodedFullHashes", |
387 const std::string& request_base64) const { | 315 config_); |
388 std::string url = | |
389 ComposePver4Url(kSbV4UrlPrefix, "encodedFullHashes", request_base64, | |
390 client_name_, version_, key_param_); | |
391 return GURL(url); | |
392 } | 316 } |
393 | 317 |
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 | 318 |
415 } // namespace safe_browsing | 319 } // namespace safe_browsing |
OLD | NEW |