Chromium Code Reviews| 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 |