OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "components/safe_browsing_db/v4_update_protocol_manager.h" | |
6 | |
7 #include <utility> | |
8 | |
9 #include "base/base64.h" | |
10 #include "base/macros.h" | |
11 #include "base/metrics/histogram_macros.h" | |
12 #include "base/timer/timer.h" | |
13 #include "components/safe_browsing_db/safebrowsing.pb.h" | |
14 #include "net/base/load_flags.h" | |
15 #include "net/http/http_response_headers.h" | |
16 #include "net/http/http_status_code.h" | |
17 #include "net/url_request/url_fetcher.h" | |
18 #include "net/url_request/url_request_context_getter.h" | |
19 | |
20 using base::Time; | |
21 using base::TimeDelta; | |
22 | |
23 namespace { | |
24 | |
25 // Enumerate parsing failures for histogramming purposes. DO NOT CHANGE | |
26 // THE ORDERING OF THESE VALUES. | |
27 enum ParseResultType { | |
28 // Error parsing the protocol buffer from a string. | |
29 PARSE_FROM_STRING_ERROR = 0, | |
30 | |
31 // Memory space for histograms is determined by the max. ALWAYS | |
32 // ADD NEW VALUES BEFORE THIS ONE. | |
33 PARSE_RESULT_TYPE_MAX = 1 | |
34 }; | |
35 | |
36 // Record parsing errors of an update result. | |
37 void RecordParseUpdateResult(ParseResultType result_type) { | |
38 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.ParseV4UpdateResult", result_type, | |
39 PARSE_RESULT_TYPE_MAX); | |
40 } | |
41 | |
42 } // namespace | |
43 | |
44 namespace safe_browsing { | |
45 | |
46 const char kUmaV4UpdateResponseMetricName[] = | |
47 "SafeBrowsing.V4UpdateHttpResponseOrErrorCode"; | |
48 | |
49 // The default V4UpdateProtocolManagerFactory. | |
50 class V4UpdateProtocolManagerFactoryImpl | |
51 : public V4UpdateProtocolManagerFactory { | |
52 public: | |
53 V4UpdateProtocolManagerFactoryImpl() {} | |
54 ~V4UpdateProtocolManagerFactoryImpl() override {} | |
55 V4UpdateProtocolManager* CreateProtocolManager( | |
56 net::URLRequestContextGetter* request_context_getter, | |
57 const V4ProtocolConfig& config) override { | |
58 return new V4UpdateProtocolManager(request_context_getter, config); | |
59 } | |
60 | |
61 private: | |
62 DISALLOW_COPY_AND_ASSIGN(V4UpdateProtocolManagerFactoryImpl); | |
63 }; | |
64 | |
65 // V4UpdateProtocolManager implementation -------------------------------- | |
66 | |
67 // static | |
68 V4UpdateProtocolManagerFactory* V4UpdateProtocolManager::factory_ = NULL; | |
69 | |
70 // static | |
71 V4UpdateProtocolManager* V4UpdateProtocolManager::Create( | |
72 net::URLRequestContextGetter* request_context_getter, | |
73 const V4ProtocolConfig& config) { | |
74 if (!factory_) | |
75 factory_ = new V4UpdateProtocolManagerFactoryImpl(); | |
76 return factory_->CreateProtocolManager(request_context_getter, config); | |
77 } | |
78 | |
79 void V4UpdateProtocolManager::ResetUpdateErrors() { | |
80 update_error_count_ = 0; | |
81 update_back_off_mult_ = 1; | |
82 } | |
83 | |
84 V4UpdateProtocolManager::V4UpdateProtocolManager( | |
85 net::URLRequestContextGetter* request_context_getter, | |
86 const V4ProtocolConfig& config) | |
87 : update_error_count_(0), | |
88 update_back_off_mult_(1), | |
89 next_update_time_(Time::FromDoubleT(0)), | |
90 config_(config), | |
91 request_context_getter_(request_context_getter), | |
92 url_fetcher_id_(0) { | |
93 } | |
94 | |
95 // static | |
96 void V4UpdateProtocolManager::RecordUpdateResult( | |
97 OperationResultType result_type) { | |
98 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4UpdateResult", result_type, | |
99 OPERATION_RESULT_TYPE_MAX); | |
100 } | |
101 | |
102 V4UpdateProtocolManager::~V4UpdateProtocolManager() { | |
103 // Delete in-progress SafeBrowsing requests. | |
104 STLDeleteContainerPairFirstPointers(update_requests_.begin(), | |
105 update_requests_.end()); | |
106 update_requests_.clear(); | |
107 } | |
108 | |
109 std::string V4UpdateProtocolManager::GetUpdateRequest( | |
110 const base::hash_set<UpdateListIdentifier>& lists_to_update) { | |
111 // Build the request. Client info and client states are not added to the | |
112 // request protocol buffer. Client info is passed as params in the url. | |
113 FetchThreatListUpdatesRequest request; | |
114 for (const UpdateListIdentifier& list_to_update : lists_to_update) { | |
115 ListUpdateRequest* list_update_request = request.add_list_update_requests(); | |
116 list_update_request->set_platform_type(list_to_update.platform_type); | |
117 list_update_request->set_threat_entry_type( | |
118 list_to_update.threat_entry_type); | |
119 list_update_request->set_threat_type(list_to_update.threat_type); | |
120 } | |
121 | |
122 // Serialize and Base64 encode. | |
123 std::string req_data, req_base64; | |
124 request.SerializeToString(&req_data); | |
125 base::Base64Encode(req_data, &req_base64); | |
126 | |
127 return req_base64; | |
128 } | |
129 | |
130 bool V4UpdateProtocolManager::ParseUpdateResponse( | |
131 const std::string& data, | |
132 std::vector<ListUpdateResponse>* list_update_responses) { | |
133 FetchThreatListUpdatesResponse response; | |
134 | |
135 if (!response.ParseFromString(data)) { | |
136 RecordParseUpdateResult(PARSE_FROM_STRING_ERROR); | |
137 return false; | |
138 } | |
139 | |
140 if (response.has_minimum_wait_duration()) { | |
141 // Seconds resolution is good enough so we ignore the nanos field. | |
142 next_update_time_ = | |
143 Time::Now() + base::TimeDelta::FromSeconds( | |
144 response.minimum_wait_duration().seconds()); | |
145 } | |
146 | |
147 // TODO(vakh): Do something useful with this response. | |
148 // TODO(vakh): Update the list_states_ map correctly. | |
Nathan Parker
2016/02/23 18:47:06
I wonder if the client state should be passed into
vakh (use Gerrit instead)
2016/02/23 23:58:20
Done.
| |
149 for (const ListUpdateResponse& list_update_response : | |
150 response.list_update_responses()) { | |
151 list_update_responses->push_back(list_update_response); | |
152 } | |
153 return true; | |
154 } | |
155 | |
156 void V4UpdateProtocolManager::GetUpdates( | |
157 const base::hash_set<UpdateListIdentifier>& lists_to_update, | |
158 UpdateCallback callback) { | |
159 DCHECK(CalledOnValidThread()); | |
160 // We need to wait the minimum waiting duration, and if we are in backoff, | |
161 // we need to check if we're past the next allowed time. If we are, we can | |
162 // proceed with the request. If not, we are required to return empty results | |
163 // (i.e. treat the page as safe). | |
164 if (Time::Now() <= next_update_time_) { | |
165 if (update_error_count_) { | |
166 RecordUpdateResult(ORT_BACKOFF_ERROR); | |
167 } else { | |
168 RecordUpdateResult(ORT_MIN_WAIT_DURATION_ERROR); | |
169 } | |
170 std::vector<ListUpdateResponse> list_update_responses; | |
171 callback.Run(list_update_responses); | |
172 return; | |
173 } | |
174 | |
175 std::string req_base64 = GetUpdateRequest(lists_to_update); | |
176 GURL update_url = GetUpdateUrl(req_base64); | |
177 | |
178 net::URLFetcher* fetcher = | |
179 net::URLFetcher::Create(url_fetcher_id_++, update_url, | |
180 net::URLFetcher::GET, this) | |
181 .release(); | |
182 update_requests_[fetcher] = callback; | |
183 | |
184 fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE); | |
185 fetcher->SetRequestContext(request_context_getter_.get()); | |
186 fetcher->Start(); | |
187 } | |
188 | |
189 // net::URLFetcherDelegate implementation ---------------------------------- | |
190 | |
191 // SafeBrowsing request responses are handled here. | |
192 void V4UpdateProtocolManager::OnURLFetchComplete( | |
193 const net::URLFetcher* source) { | |
194 DCHECK(CalledOnValidThread()); | |
195 | |
196 UpdateRequests::iterator it = update_requests_.find(source); | |
197 DCHECK(it != update_requests_.end()) << "Request not found"; | |
198 | |
199 // FetchThreatListUpdatesResponse response. | |
200 // Reset the scoped pointer so the fetcher gets destroyed properly. | |
201 scoped_ptr<const net::URLFetcher> fetcher(it->first); | |
202 | |
203 int response_code = source->GetResponseCode(); | |
204 net::URLRequestStatus status = source->GetStatus(); | |
205 V4ProtocolManagerUtil::RecordHttpResponseOrErrorCode( | |
206 kUmaV4UpdateResponseMetricName, status, response_code); | |
207 | |
208 const UpdateCallback& callback = it->second; | |
209 std::vector<FetchThreatListUpdatesResponse::ListUpdateResponse> | |
210 list_update_responses; | |
211 if (status.is_success() && response_code == net::HTTP_OK) { | |
212 RecordUpdateResult(ORT_STATUS_200); | |
213 ResetUpdateErrors(); | |
214 std::string data; | |
215 source->GetResponseAsString(&data); | |
216 if (!ParseUpdateResponse(data, &list_update_responses)) { | |
217 list_update_responses.clear(); | |
218 RecordUpdateResult(ORT_PARSE_ERROR); | |
219 } | |
220 } else { | |
221 HandleUpdateError(Time::Now()); | |
222 | |
223 DVLOG(1) << "SafeBrowsing GetEncodedUpdates request for: " | |
224 << source->GetURL() << " failed with error: " << status.error() | |
225 << " and response code: " << response_code; | |
226 | |
227 if (status.status() == net::URLRequestStatus::FAILED) { | |
228 RecordUpdateResult(ORT_NETWORK_ERROR); | |
229 } else { | |
230 RecordUpdateResult(ORT_HTTP_ERROR); | |
231 } | |
232 } | |
233 | |
234 // Invoke the callback with list_update_responses, even if there was a parse | |
235 // error or an error response code (in which case list_update_responses will | |
236 // be empty). The caller can't be blocked indefinitely. | |
237 callback.Run(list_update_responses); | |
238 | |
239 update_requests_.erase(it); | |
240 } | |
241 | |
242 void V4UpdateProtocolManager::HandleUpdateError(const Time& now) { | |
243 DCHECK(CalledOnValidThread()); | |
244 base::TimeDelta next = V4ProtocolManagerUtil::GetNextBackOffInterval( | |
245 &update_error_count_, &update_back_off_mult_); | |
246 next_update_time_ = now + next; | |
247 } | |
248 | |
249 GURL V4UpdateProtocolManager::GetUpdateUrl( | |
250 const std::string& req_base64) const { | |
251 return V4ProtocolManagerUtil::GetRequestUrl(req_base64, "encodedUpdates", | |
252 config_); | |
253 } | |
254 | |
255 } // namespace safe_browsing | |
OLD | NEW |