1 // Copyright (c) 2012 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 "chrome/browser/safe_browsing/protocol_manager.h" 6 7 #include "base/environment.h" 8 #include "base/logging.h" 9 #include "base/memory/scoped_vector.h" 10 #include "base/metrics/histogram.h" 11 #include "base/rand_util.h" 12 #include "base/stl_util.h" 13 #include "base/strings/string_util.h" 14 #include "base/strings/stringprintf.h" 15 #include "base/timer/timer.h" 16 #include "chrome/browser/safe_browsing/protocol_parser.h" 17 #include "chrome/common/chrome_version_info.h" 18 #include "chrome/common/env_vars.h" 19 #include "google_apis/google_api_keys.h" 20 #include "net/base/escape.h" 21 #include "net/base/load_flags.h" 22 #include "net/base/net_errors.h" 23 #include "net/url_request/url_fetcher.h" 24 #include "net/url_request/url_request_context_getter.h" 25 #include "net/url_request/url_request_status.h" 26 27 #if defined(OS_ANDROID) 28 #include "net/base/network_change_notifier.h" 29 #endif 30 31 using base::Time; 32 using base::TimeDelta; 33 34 namespace { 35 36 // UpdateResult indicates what happened with the primary and/or backup update 37 // requests. The ordering of the values must stay the same for UMA consistency, 38 // and is also ordered in this way to match ProtocolManager::BackupUpdateReason. 39 enum UpdateResult { 40 UPDATE_RESULT_FAIL, 41 UPDATE_RESULT_SUCCESS, 42 UPDATE_RESULT_BACKUP_CONNECT_FAIL, 43 UPDATE_RESULT_BACKUP_CONNECT_SUCCESS, 44 UPDATE_RESULT_BACKUP_HTTP_FAIL, 45 UPDATE_RESULT_BACKUP_HTTP_SUCCESS, 46 UPDATE_RESULT_BACKUP_NETWORK_FAIL, 47 UPDATE_RESULT_BACKUP_NETWORK_SUCCESS, 48 UPDATE_RESULT_MAX, 49 UPDATE_RESULT_BACKUP_START = UPDATE_RESULT_BACKUP_CONNECT_FAIL, 50 }; 51 52 void RecordUpdateResult(UpdateResult result) { 53 DCHECK(result >= 0 && result < UPDATE_RESULT_MAX); 54 UMA_HISTOGRAM_ENUMERATION("SB2.UpdateResult", result, UPDATE_RESULT_MAX); 55 } 56 57 } // namespace 58 59 // Minimum time, in seconds, from start up before we must issue an update query. 60 static const int kSbTimerStartIntervalSecMin = 60; 61 62 // Maximum time, in seconds, from start up before we must issue an update query. 63 static const int kSbTimerStartIntervalSecMax = 300; 64 65 // The maximum time, in seconds, to wait for a response to an update request. 66 static const int kSbMaxUpdateWaitSec = 30; 67 68 // Maximum back off multiplier. 69 static const size_t kSbMaxBackOff = 8; 70 71 // The default SBProtocolManagerFactory. 72 class SBProtocolManagerFactoryImpl : public SBProtocolManagerFactory { 73 public: 74 SBProtocolManagerFactoryImpl() { } 75 virtual ~SBProtocolManagerFactoryImpl() { } 76 virtual SafeBrowsingProtocolManager* CreateProtocolManager( 77 SafeBrowsingProtocolManagerDelegate* delegate, 78 net::URLRequestContextGetter* request_context_getter, 79 const SafeBrowsingProtocolConfig& config) OVERRIDE { 80 return new SafeBrowsingProtocolManager( 81 delegate, request_context_getter, config); 82 } 83 private: 84 DISALLOW_COPY_AND_ASSIGN(SBProtocolManagerFactoryImpl); 85 }; 86 87 // SafeBrowsingProtocolManager implementation ---------------------------------- 88 89 // static 90 SBProtocolManagerFactory* SafeBrowsingProtocolManager::factory_ = NULL; 91 92 // static 93 SafeBrowsingProtocolManager* SafeBrowsingProtocolManager::Create( 94 SafeBrowsingProtocolManagerDelegate* delegate, 95 net::URLRequestContextGetter* request_context_getter, 96 const SafeBrowsingProtocolConfig& config) { 97 if (!factory_) 98 factory_ = new SBProtocolManagerFactoryImpl(); 99 return factory_->CreateProtocolManager( 100 delegate, request_context_getter, config); 101 } 102 103 SafeBrowsingProtocolManager::SafeBrowsingProtocolManager( 104 SafeBrowsingProtocolManagerDelegate* delegate, 105 net::URLRequestContextGetter* request_context_getter, 106 const SafeBrowsingProtocolConfig& config) 107 : delegate_(delegate), 108 request_type_(NO_REQUEST), 109 update_error_count_(0), 110 gethash_error_count_(0), 111 update_back_off_mult_(1), 112 gethash_back_off_mult_(1), 113 next_update_interval_(base::TimeDelta::FromSeconds( 114 base::RandInt(kSbTimerStartIntervalSecMin, 115 kSbTimerStartIntervalSecMax))), 116 update_state_(FIRST_REQUEST), 117 chunk_pending_to_write_(false), 118 version_(config.version), 119 update_size_(0), 120 client_name_(config.client_name), 121 request_context_getter_(request_context_getter), 122 url_prefix_(config.url_prefix), 123 backup_update_reason_(BACKUP_UPDATE_REASON_MAX), 124 disable_auto_update_(config.disable_auto_update), 125 #if defined(OS_ANDROID) 126 disable_connection_check_(config.disable_connection_check), 127 #endif 128 url_fetcher_id_(0), 129 app_in_foreground_(true) { 130 DCHECK(!url_prefix_.empty()); 131 132 backup_url_prefixes_[BACKUP_UPDATE_REASON_CONNECT] = 133 config.backup_connect_error_url_prefix; 134 backup_url_prefixes_[BACKUP_UPDATE_REASON_HTTP] = 135 config.backup_http_error_url_prefix; 136 backup_url_prefixes_[BACKUP_UPDATE_REASON_NETWORK] = 137 config.backup_network_error_url_prefix; 138 139 // Set the backoff multiplier fuzz to a random value between 0 and 1. 140 back_off_fuzz_ = static_cast<float>(base::RandDouble()); 141 if (version_.empty()) 142 version_ = SafeBrowsingProtocolManagerHelper::Version(); 143 } 144 145 // static 146 void SafeBrowsingProtocolManager::RecordGetHashResult( 147 bool is_download, ResultType result_type) { 148 if (is_download) { 149 UMA_HISTOGRAM_ENUMERATION("SB2.GetHashResultDownload", result_type, 150 GET_HASH_RESULT_MAX); 151 } else { 152 UMA_HISTOGRAM_ENUMERATION("SB2.GetHashResult", result_type, 153 GET_HASH_RESULT_MAX); 154 } 155 } 156 157 bool SafeBrowsingProtocolManager::IsUpdateScheduled() const { 158 return update_timer_.IsRunning(); 159 } 160 161 SafeBrowsingProtocolManager::~SafeBrowsingProtocolManager() { 162 // Delete in-progress SafeBrowsing requests. 163 STLDeleteContainerPairFirstPointers(hash_requests_.begin(), 164 hash_requests_.end()); 165 hash_requests_.clear(); 166 } 167 168 // We can only have one update or chunk request outstanding, but there may be 169 // multiple GetHash requests pending since we don't want to serialize them and 170 // slow down the user. 171 void SafeBrowsingProtocolManager::GetFullHash( 172 const std::vector<SBPrefix>& prefixes, 173 FullHashCallback callback, 174 bool is_download) { 175 DCHECK(CalledOnValidThread()); 176 // If we are in GetHash backoff, we need to check if we're past the next 177 // allowed time. If we are, we can proceed with the request. If not, we are 178 // required to return empty results (i.e. treat the page as safe). 179 if (gethash_error_count_ && Time::Now() <= next_gethash_time_) { 180 RecordGetHashResult(is_download, GET_HASH_BACKOFF_ERROR); 181 std::vector<SBFullHashResult> full_hashes; 182 callback.Run(full_hashes, base::TimeDelta()); 183 return; 184 } 185 GURL gethash_url = GetHashUrl(); 186 net::URLFetcher* fetcher = net::URLFetcher::Create( 187 url_fetcher_id_++, gethash_url, net::URLFetcher::POST, this); 188 hash_requests_[fetcher] = FullHashDetails(callback, is_download); 189 190 const std::string get_hash = safe_browsing::FormatGetHash(prefixes); 191 192 fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE); 193 fetcher->SetRequestContext(request_context_getter_.get()); 194 fetcher->SetUploadData("text/plain", get_hash); 195 fetcher->Start(); 196 } 197 198 void SafeBrowsingProtocolManager::GetNextUpdate() { 199 DCHECK(CalledOnValidThread()); 200 if (request_.get() || request_type_ != NO_REQUEST) 201 return; 202 203 #if defined(OS_ANDROID) 204 if (!disable_connection_check_) { 205 net::NetworkChangeNotifier::ConnectionType type = 206 net::NetworkChangeNotifier::GetConnectionType(); 207 if (type != net::NetworkChangeNotifier::CONNECTION_WIFI) { 208 ScheduleNextUpdate(false /* no back off */); 209 return; 210 } 211 } 212 #endif 213 214 IssueUpdateRequest(); 215 } 216 217 // net::URLFetcherDelegate implementation ---------------------------------- 218 219 // All SafeBrowsing request responses are handled here. 220 // TODO(paulg): Clarify with the SafeBrowsing team whether a failed parse of a 221 // chunk should retry the download and parse of that chunk (and 222 // what back off / how many times to try), and if that effects the 223 // update back off. For now, a failed parse of the chunk means we 224 // drop it. This isn't so bad because the next UPDATE_REQUEST we 225 // do will report all the chunks we have. If that chunk is still 226 // required, the SafeBrowsing servers will tell us to get it again. 227 void SafeBrowsingProtocolManager::OnURLFetchComplete( 228 const net::URLFetcher* source) { 229 DCHECK(CalledOnValidThread()); 230 scoped_ptr<const net::URLFetcher> fetcher; 231 232 HashRequests::iterator it = hash_requests_.find(source); 233 if (it != hash_requests_.end()) { 234 // GetHash response. 235 fetcher.reset(it->first); 236 const FullHashDetails& details = it->second; 237 std::vector<SBFullHashResult> full_hashes; 238 base::TimeDelta cache_lifetime; 239 if (source->GetStatus().is_success() && 240 (source->GetResponseCode() == 200 || 241 source->GetResponseCode() == 204)) { 242 // For tracking our GetHash false positive (204) rate, compared to real 243 // (200) responses. 244 if (source->GetResponseCode() == 200) 245 RecordGetHashResult(details.is_download, GET_HASH_STATUS_200); 246 else 247 RecordGetHashResult(details.is_download, GET_HASH_STATUS_204); 248 249 gethash_error_count_ = 0; 250 gethash_back_off_mult_ = 1; 251 std::string data; 252 source->GetResponseAsString(&data); 253 if (!safe_browsing::ParseGetHash( 254 data.data(), data.length(), &cache_lifetime, &full_hashes)) { 255 full_hashes.clear(); 256 RecordGetHashResult(details.is_download, GET_HASH_PARSE_ERROR); 257 // TODO(cbentzel): Should cache_lifetime be set to 0 here? (See 258 // http://crbug.com/360232.) 259 } 260 } else { 261 HandleGetHashError(Time::Now()); 262 if (source->GetStatus().status() == net::URLRequestStatus::FAILED) { 263 RecordGetHashResult(details.is_download, GET_HASH_NETWORK_ERROR); 264 VLOG(1) << "SafeBrowsing GetHash request for: " << source->GetURL() 265 << " failed with error: " << source->GetStatus().error(); 266 } else { 267 RecordGetHashResult(details.is_download, GET_HASH_HTTP_ERROR); 268 VLOG(1) << "SafeBrowsing GetHash request for: " << source->GetURL() 269 << " failed with error: " << source->GetResponseCode(); 270 } 271 } 272 273 // Invoke the callback with full_hashes, even if there was a parse error or 274 // an error response code (in which case full_hashes will be empty). The 275 // caller can't be blocked indefinitely. 276 details.callback.Run(full_hashes, cache_lifetime); 277 278 hash_requests_.erase(it); 279 } else { 280 // Update or chunk response. 281 fetcher.reset(request_.release()); 282 283 if (request_type_ == UPDATE_REQUEST || 284 request_type_ == BACKUP_UPDATE_REQUEST) { 285 if (!fetcher.get()) { 286 // We've timed out waiting for an update response, so we've cancelled 287 // the update request and scheduled a new one. Ignore this response. 288 return; 289 } 290 291 // Cancel the update response timeout now that we have the response. 292 timeout_timer_.Stop(); 293 } 294 295 net::URLRequestStatus status = source->GetStatus(); 296 if (status.is_success() && source->GetResponseCode() == 200) { 297 // We have data from the SafeBrowsing service. 298 std::string data; 299 source->GetResponseAsString(&data); 300 301 // TODO(shess): Cleanup the flow of this code so that |parsed_ok| can be 302 // removed or omitted. 303 const bool parsed_ok = HandleServiceResponse( 304 source->GetURL(), data.data(), data.length()); 305 if (!parsed_ok) { 306 VLOG(1) << "SafeBrowsing request for: " << source->GetURL() 307 << " failed parse."; 308 chunk_request_urls_.clear(); 309 if (request_type_ == UPDATE_REQUEST && 310 IssueBackupUpdateRequest(BACKUP_UPDATE_REASON_HTTP)) { 311 return; 312 } 313 UpdateFinished(false); 314 } 315 316 switch (request_type_) { 317 case CHUNK_REQUEST: 318 if (parsed_ok) { 319 chunk_request_urls_.pop_front(); 320 if (chunk_request_urls_.empty() && !chunk_pending_to_write_) 321 UpdateFinished(true); 322 } 323 break; 324 case UPDATE_REQUEST: 325 case BACKUP_UPDATE_REQUEST: 326 if (chunk_request_urls_.empty() && parsed_ok) { 327 // We are up to date since the servers gave us nothing new, so we 328 // are done with this update cycle. 329 UpdateFinished(true); 330 } 331 break; 332 case NO_REQUEST: 333 // This can happen if HandleServiceResponse fails above. 334 break; 335 default: 336 NOTREACHED(); 337 break; 338 } 339 } else { 340 if (status.status() == net::URLRequestStatus::FAILED) { 341 VLOG(1) << "SafeBrowsing request for: " << source->GetURL() 342 << " failed with error: " << source->GetStatus().error(); 343 } else { 344 VLOG(1) << "SafeBrowsing request for: " << source->GetURL() 345 << " failed with error: " << source->GetResponseCode(); 346 } 347 if (request_type_ == CHUNK_REQUEST) { 348 // The SafeBrowsing service error, or very bad response code: back off. 349 chunk_request_urls_.clear(); 350 } else if (request_type_ == UPDATE_REQUEST) { 351 BackupUpdateReason backup_update_reason = BACKUP_UPDATE_REASON_MAX; 352 if (status.is_success()) { 353 backup_update_reason = BACKUP_UPDATE_REASON_HTTP; 354 } else { 355 switch (status.error()) { 356 case net::ERR_INTERNET_DISCONNECTED: 357 case net::ERR_NETWORK_CHANGED: 358 backup_update_reason = BACKUP_UPDATE_REASON_NETWORK; 359 break; 360 default: 361 backup_update_reason = BACKUP_UPDATE_REASON_CONNECT; 362 break; 363 } 364 } 365 if (backup_update_reason != BACKUP_UPDATE_REASON_MAX && 366 IssueBackupUpdateRequest(backup_update_reason)) { 367 return; 368 } 369 } 370 UpdateFinished(false); 371 } 372 } 373 374 // Get the next chunk if available. 375 IssueChunkRequest(); 376 } 377 378 bool SafeBrowsingProtocolManager::HandleServiceResponse( 379 const GURL& url, const char* data, size_t length) { 380 DCHECK(CalledOnValidThread()); 381 382 switch (request_type_) { 383 case UPDATE_REQUEST: 384 case BACKUP_UPDATE_REQUEST: { 385 size_t next_update_sec = 0; 386 bool reset = false; 387 scoped_ptr<std::vector<SBChunkDelete> > chunk_deletes( 388 new std::vector<SBChunkDelete>); 389 std::vector<ChunkUrl> chunk_urls; 390 if (!safe_browsing::ParseUpdate(data, length, &next_update_sec, &reset, 391 chunk_deletes.get(), &chunk_urls)) { 392 return false; 393 } 394 395 base::TimeDelta next_update_interval = 396 base::TimeDelta::FromSeconds(next_update_sec); 397 last_update_ = Time::Now(); 398 399 if (update_state_ == FIRST_REQUEST) 400 update_state_ = SECOND_REQUEST; 401 else if (update_state_ == SECOND_REQUEST) 402 update_state_ = NORMAL_REQUEST; 403 404 // New time for the next update. 405 if (next_update_interval > base::TimeDelta()) { 406 next_update_interval_ = next_update_interval; 407 } else if (update_state_ == SECOND_REQUEST) { 408 next_update_interval_ = base::TimeDelta::FromSeconds( 409 base::RandInt(15, 45)); 410 } 411 412 // New chunks to download. 413 if (!chunk_urls.empty()) { 414 UMA_HISTOGRAM_COUNTS("SB2.UpdateUrls", chunk_urls.size()); 415 for (size_t i = 0; i < chunk_urls.size(); ++i) 416 chunk_request_urls_.push_back(chunk_urls[i]); 417 } 418 419 // Handle the case were the SafeBrowsing service tells us to dump our 420 // database. 421 if (reset) { 422 delegate_->ResetDatabase(); 423 return true; 424 } 425 426 // Chunks to delete from our storage. 427 if (!chunk_deletes->empty()) 428 delegate_->DeleteChunks(chunk_deletes.Pass()); 429 430 break; 431 } 432 case CHUNK_REQUEST: { 433 UMA_HISTOGRAM_TIMES("SB2.ChunkRequest", 434 base::Time::Now() - chunk_request_start_); 435 436 const ChunkUrl chunk_url = chunk_request_urls_.front(); 437 scoped_ptr<ScopedVector<SBChunkData> > 438 chunks(new ScopedVector<SBChunkData>); 439 UMA_HISTOGRAM_COUNTS("SB2.ChunkSize", length); 440 update_size_ += length; 441 if (!safe_browsing::ParseChunk(data, length, chunks.get())) 442 return false; 443 444 // Chunks to add to storage. Pass ownership of |chunks|. 445 if (!chunks->empty()) { 446 chunk_pending_to_write_ = true; 447 delegate_->AddChunks( 448 chunk_url.list_name, chunks.Pass(), 449 base::Bind(&SafeBrowsingProtocolManager::OnAddChunksComplete, 450 base::Unretained(this))); 451 } 452 453 break; 454 } 455 456 default: 457 return false; 458 } 459 460 return true; 461 } 462 463 void SafeBrowsingProtocolManager::Initialize() { 464 DCHECK(CalledOnValidThread()); 465 // Don't want to hit the safe browsing servers on build/chrome bots. 466 scoped_ptr<base::Environment> env(base::Environment::Create()); 467 if (env->HasVar(env_vars::kHeadless)) 468 return; 469 ScheduleNextUpdate(false /* no back off */); 470 } 471 472 void SafeBrowsingProtocolManager::ScheduleNextUpdate(bool back_off) { 473 DCHECK(CalledOnValidThread()); 474 if (disable_auto_update_) { 475 // Unschedule any current timer. 476 update_timer_.Stop(); 477 return; 478 } 479 // Reschedule with the new update. 480 base::TimeDelta next_update_interval = GetNextUpdateInterval(back_off); 481 ForceScheduleNextUpdate(next_update_interval); 482 } 483 484 void SafeBrowsingProtocolManager::ForceScheduleNextUpdate( 485 base::TimeDelta interval) { 486 DCHECK(CalledOnValidThread()); 487 DCHECK(interval >= base::TimeDelta()); 488 // Unschedule any current timer. 489 update_timer_.Stop(); 490 update_timer_.Start(FROM_HERE, interval, this, 491 &SafeBrowsingProtocolManager::GetNextUpdate); 492 } 493 494 // According to section 5 of the SafeBrowsing protocol specification, we must 495 // back off after a certain number of errors. We only change |next_update_sec_| 496 // when we receive a response from the SafeBrowsing service. 497 base::TimeDelta SafeBrowsingProtocolManager::GetNextUpdateInterval( 498 bool back_off) { 499 DCHECK(CalledOnValidThread()); 500 DCHECK(next_update_interval_ > base::TimeDelta()); 501 base::TimeDelta next = next_update_interval_; 502 if (back_off) { 503 next = GetNextBackOffInterval(&update_error_count_, &update_back_off_mult_); 504 } else { 505 // Successful response means error reset. 506 update_error_count_ = 0; 507 update_back_off_mult_ = 1; 508 } 509 return next; 510 } 511 512 base::TimeDelta SafeBrowsingProtocolManager::GetNextBackOffInterval( 513 size_t* error_count, size_t* multiplier) const { 514 DCHECK(CalledOnValidThread()); 515 DCHECK(multiplier && error_count); 516 (*error_count)++; 517 if (*error_count > 1 && *error_count < 6) { 518 base::TimeDelta next = base::TimeDelta::FromMinutes( 519 *multiplier * (1 + back_off_fuzz_) * 30); 520 *multiplier *= 2; 521 if (*multiplier > kSbMaxBackOff) 522 *multiplier = kSbMaxBackOff; 523 return next; 524 } 525 if (*error_count >= 6) 526 return base::TimeDelta::FromHours(8); 527 return base::TimeDelta::FromMinutes(1); 528 } 529 530 // This request requires getting a list of all the chunks for each list from the 531 // database asynchronously. The request will be issued when we're called back in 532 // OnGetChunksComplete. 533 // TODO(paulg): We should get this at start up and maintain a ChunkRange cache 534 // to avoid hitting the database with each update request. On the 535 // otherhand, this request will only occur ~20-30 minutes so there 536 // isn't that much overhead. Measure! 537 void SafeBrowsingProtocolManager::IssueUpdateRequest() { 538 DCHECK(CalledOnValidThread()); 539 request_type_ = UPDATE_REQUEST; 540 delegate_->UpdateStarted(); 541 delegate_->GetChunks( 542 base::Bind(&SafeBrowsingProtocolManager::OnGetChunksComplete, 543 base::Unretained(this))); 544 } 545 546 // The backup request can run immediately since the chunks have already been 547 // retrieved from the DB. 548 bool SafeBrowsingProtocolManager::IssueBackupUpdateRequest( 549 BackupUpdateReason backup_update_reason) { 550 DCHECK(CalledOnValidThread()); 551 DCHECK_EQ(request_type_, UPDATE_REQUEST); 552 DCHECK(backup_update_reason >= 0 && 553 backup_update_reason < BACKUP_UPDATE_REASON_MAX); 554 if (backup_url_prefixes_[backup_update_reason].empty()) 555 return false; 556 request_type_ = BACKUP_UPDATE_REQUEST; 557 backup_update_reason_ = backup_update_reason; 558 559 GURL backup_update_url = BackupUpdateUrl(backup_update_reason); 560 request_.reset(net::URLFetcher::Create( 561 url_fetcher_id_++, backup_update_url, net::URLFetcher::POST, this)); 562 request_->SetLoadFlags(net::LOAD_DISABLE_CACHE); 563 request_->SetRequestContext(request_context_getter_.get()); 564 request_->SetUploadData("text/plain", update_list_data_); 565 request_->Start(); 566 567 // Begin the update request timeout. 568 timeout_timer_.Start(FROM_HERE, TimeDelta::FromSeconds(kSbMaxUpdateWaitSec), 569 this, 570 &SafeBrowsingProtocolManager::UpdateResponseTimeout); 571 572 return true; 573 } 574 575 void SafeBrowsingProtocolManager::IssueChunkRequest() { 576 DCHECK(CalledOnValidThread()); 577 // We are only allowed to have one request outstanding at any time. Also, 578 // don't get the next url until the previous one has been written to disk so 579 // that we don't use too much memory. 580 if (request_.get() || chunk_request_urls_.empty() || chunk_pending_to_write_) 581 return; 582 583 ChunkUrl next_chunk = chunk_request_urls_.front(); 584 DCHECK(!next_chunk.url.empty()); 585 GURL chunk_url = NextChunkUrl(next_chunk.url); 586 request_type_ = CHUNK_REQUEST; 587 request_.reset(net::URLFetcher::Create( 588 url_fetcher_id_++, chunk_url, net::URLFetcher::GET, this)); 589 request_->SetLoadFlags(net::LOAD_DISABLE_CACHE); 590 request_->SetRequestContext(request_context_getter_.get()); 591 chunk_request_start_ = base::Time::Now(); 592 request_->Start(); 593 } 594 595 void SafeBrowsingProtocolManager::OnGetChunksComplete( 596 const std::vector<SBListChunkRanges>& lists, bool database_error) { 597 DCHECK(CalledOnValidThread()); 598 DCHECK_EQ(request_type_, UPDATE_REQUEST); 599 DCHECK(update_list_data_.empty()); 600 if (database_error) { 601 // The update was not successful, but don't back off. 602 UpdateFinished(false, false); 603 return; 604 } 605 606 // Format our stored chunks: 607 bool found_malware = false; 608 bool found_phishing = false; 609 for (size_t i = 0; i < lists.size(); ++i) { 610 update_list_data_.append(safe_browsing::FormatList(lists[i])); 611 if (lists[i].name == safe_browsing_util::kPhishingList) 612 found_phishing = true; 613 614 if (lists[i].name == safe_browsing_util::kMalwareList) 615 found_malware = true; 616 } 617 618 // If we have an empty database, let the server know we want data for these 619 // lists. 620 // TODO(shess): These cases never happen because the database fills in the 621 // lists in GetChunks(). Refactor the unit tests so that this code can be 622 // removed. 623 if (!found_phishing) { 624 update_list_data_.append(safe_browsing::FormatList( 625 SBListChunkRanges(safe_browsing_util::kPhishingList))); 626 } 627 if (!found_malware) { 628 update_list_data_.append(safe_browsing::FormatList( 629 SBListChunkRanges(safe_browsing_util::kMalwareList))); 630 } 631 632 // Large requests are (probably) a sign of database corruption. 633 // Record stats to inform decisions about whether to automate 634 // deletion of such databases. http://crbug.com/120219 635 UMA_HISTOGRAM_COUNTS("SB2.UpdateRequestSize", update_list_data_.size()); 636 637 GURL update_url = UpdateUrl(); 638 request_.reset(net::URLFetcher::Create( 639 url_fetcher_id_++, update_url, net::URLFetcher::POST, this)); 640 request_->SetLoadFlags(net::LOAD_DISABLE_CACHE); 641 request_->SetRequestContext(request_context_getter_.get()); 642 request_->SetUploadData("text/plain", update_list_data_); 643 request_->Start(); 644 645 // Begin the update request timeout. 646 timeout_timer_.Start(FROM_HERE, TimeDelta::FromSeconds(kSbMaxUpdateWaitSec), 647 this, 648 &SafeBrowsingProtocolManager::UpdateResponseTimeout); 649 } 650 651 // If we haven't heard back from the server with an update response, this method 652 // will run. Close the current update session and schedule another update. 653 void SafeBrowsingProtocolManager::UpdateResponseTimeout() { 654 DCHECK(CalledOnValidThread()); 655 DCHECK(request_type_ == UPDATE_REQUEST || 656 request_type_ == BACKUP_UPDATE_REQUEST); 657 request_.reset(); 658 if (request_type_ == UPDATE_REQUEST && 659 IssueBackupUpdateRequest(BACKUP_UPDATE_REASON_CONNECT)) { 660 return; 661 } 662 UpdateFinished(false); 663 } 664 665 void SafeBrowsingProtocolManager::OnAddChunksComplete() { 666 DCHECK(CalledOnValidThread()); 667 chunk_pending_to_write_ = false; 668 669 if (chunk_request_urls_.empty()) { 670 UMA_HISTOGRAM_LONG_TIMES("SB2.Update", Time::Now() - last_update_); 671 UpdateFinished(true); 672 } else { 673 IssueChunkRequest(); 674 } 675 } 676 677 void SafeBrowsingProtocolManager::HandleGetHashError(const Time& now) { 678 DCHECK(CalledOnValidThread()); 679 base::TimeDelta next = GetNextBackOffInterval( 680 &gethash_error_count_, &gethash_back_off_mult_); 681 next_gethash_time_ = now + next; 682 } 683 684 void SafeBrowsingProtocolManager::UpdateFinished(bool success) { 685 UpdateFinished(success, !success); 686 } 687 688 void SafeBrowsingProtocolManager::UpdateFinished(bool success, bool back_off) { 689 DCHECK(CalledOnValidThread()); 690 #if defined(OS_ANDROID) 691 if (app_in_foreground_) 692 UMA_HISTOGRAM_COUNTS("SB2.UpdateSizeForeground", update_size_); 693 else 694 UMA_HISTOGRAM_COUNTS("SB2.UpdateSizeBackground", update_size_); 695 #endif 696 UMA_HISTOGRAM_COUNTS("SB2.UpdateSize", update_size_); 697 update_size_ = 0; 698 bool update_success = success || request_type_ == CHUNK_REQUEST; 699 if (backup_update_reason_ == BACKUP_UPDATE_REASON_MAX) { 700 RecordUpdateResult( 701 update_success ? UPDATE_RESULT_SUCCESS : UPDATE_RESULT_FAIL); 702 } else { 703 UpdateResult update_result = static_cast<UpdateResult>( 704 UPDATE_RESULT_BACKUP_START + 705 (static_cast<int>(backup_update_reason_) * 2) + 706 update_success); 707 RecordUpdateResult(update_result); 708 } 709 backup_update_reason_ = BACKUP_UPDATE_REASON_MAX; 710 request_type_ = NO_REQUEST; 711 update_list_data_.clear(); 712 delegate_->UpdateFinished(success); 713 ScheduleNextUpdate(back_off); 714 } 715 716 GURL SafeBrowsingProtocolManager::UpdateUrl() const { 717 std::string url = SafeBrowsingProtocolManagerHelper::ComposeUrl( 718 url_prefix_, "downloads", client_name_, version_, additional_query_); 719 return GURL(url); 720 } 721 722 GURL SafeBrowsingProtocolManager::BackupUpdateUrl( 723 BackupUpdateReason backup_update_reason) const { 724 DCHECK(backup_update_reason >= 0 && 725 backup_update_reason < BACKUP_UPDATE_REASON_MAX); 726 DCHECK(!backup_url_prefixes_[backup_update_reason].empty()); 727 std::string url = SafeBrowsingProtocolManagerHelper::ComposeUrl( 728 backup_url_prefixes_[backup_update_reason], "downloads", client_name_, 729 version_, additional_query_); 730 return GURL(url); 731 } 732 733 GURL SafeBrowsingProtocolManager::GetHashUrl() const { 734 std::string url = SafeBrowsingProtocolManagerHelper::ComposeUrl( 735 url_prefix_, "gethash", client_name_, version_, additional_query_); 736 return GURL(url); 737 } 738 739 GURL SafeBrowsingProtocolManager::NextChunkUrl(const std::string& url) const { 740 DCHECK(CalledOnValidThread()); 741 std::string next_url; 742 if (!StartsWithASCII(url, "http://", false) && 743 !StartsWithASCII(url, "https://", false)) { 744 // Use https if we updated via https, otherwise http (useful for testing). 745 if (StartsWithASCII(url_prefix_, "https://", false)) 746 next_url.append("https://"); 747 else 748 next_url.append("http://"); 749 next_url.append(url); 750 } else { 751 next_url = url; 752 } 753 if (!additional_query_.empty()) { 754 if (next_url.find("?") != std::string::npos) { 755 next_url.append("&"); 756 } else { 757 next_url.append("?"); 758 } 759 next_url.append(additional_query_); 760 } 761 return GURL(next_url); 762 } 763 764 SafeBrowsingProtocolManager::FullHashDetails::FullHashDetails() 765 : callback(), 766 is_download(false) { 767 } 768 769 SafeBrowsingProtocolManager::FullHashDetails::FullHashDetails( 770 FullHashCallback callback, bool is_download) 771 : callback(callback), 772 is_download(is_download) { 773 } 774 775 SafeBrowsingProtocolManager::FullHashDetails::~FullHashDetails() { 776 } 777 778 SafeBrowsingProtocolManagerDelegate::~SafeBrowsingProtocolManagerDelegate() { 779 } 780