Home | History | Annotate | Download | only in safe_browsing
      1 // Copyright (c) 2011 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 #ifndef NDEBUG
      8 #include "base/base64.h"
      9 #endif
     10 #include "base/environment.h"
     11 #include "base/logging.h"
     12 #include "base/metrics/histogram.h"
     13 #include "base/rand_util.h"
     14 #include "base/stl_util-inl.h"
     15 #include "base/string_util.h"
     16 #include "base/task.h"
     17 #include "base/timer.h"
     18 #include "chrome/browser/safe_browsing/protocol_parser.h"
     19 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
     20 #include "chrome/common/chrome_version_info.h"
     21 #include "chrome/common/env_vars.h"
     22 #include "content/browser/browser_thread.h"
     23 #include "net/base/escape.h"
     24 #include "net/base/load_flags.h"
     25 #include "net/url_request/url_request_context_getter.h"
     26 #include "net/url_request/url_request_status.h"
     27 
     28 using base::Time;
     29 using base::TimeDelta;
     30 
     31 // Maximum time, in seconds, from start up before we must issue an update query.
     32 static const int kSbTimerStartIntervalSec = 5 * 60;
     33 
     34 // The maximum time, in seconds, to wait for a response to an update request.
     35 static const int kSbMaxUpdateWaitSec = 10;
     36 
     37 // Maximum back off multiplier.
     38 static const int kSbMaxBackOff = 8;
     39 
     40 // The default SBProtocolManagerFactory.
     41 class SBProtocolManagerFactoryImpl : public SBProtocolManagerFactory {
     42  public:
     43   SBProtocolManagerFactoryImpl() { }
     44   virtual ~SBProtocolManagerFactoryImpl() { }
     45   virtual SafeBrowsingProtocolManager* CreateProtocolManager(
     46       SafeBrowsingService* sb_service,
     47       const std::string& client_name,
     48       const std::string& client_key,
     49       const std::string& wrapped_key,
     50       net::URLRequestContextGetter* request_context_getter,
     51       const std::string& info_url_prefix,
     52       const std::string& mackey_url_prefix,
     53       bool disable_auto_update) {
     54     return new SafeBrowsingProtocolManager(
     55         sb_service, client_name, client_key, wrapped_key,
     56         request_context_getter, info_url_prefix, mackey_url_prefix,
     57         disable_auto_update);
     58   }
     59  private:
     60   DISALLOW_COPY_AND_ASSIGN(SBProtocolManagerFactoryImpl);
     61 };
     62 
     63 // SafeBrowsingProtocolManager implementation ----------------------------------
     64 
     65 // static
     66 SBProtocolManagerFactory* SafeBrowsingProtocolManager::factory_ = NULL;
     67 
     68 // static
     69 SafeBrowsingProtocolManager* SafeBrowsingProtocolManager::Create(
     70     SafeBrowsingService* sb_service,
     71     const std::string& client_name,
     72     const std::string& client_key,
     73     const std::string& wrapped_key,
     74     net::URLRequestContextGetter* request_context_getter,
     75     const std::string& info_url_prefix,
     76     const std::string& mackey_url_prefix,
     77     bool disable_auto_update) {
     78   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     79   if (!factory_)
     80     factory_ = new SBProtocolManagerFactoryImpl();
     81   return factory_->CreateProtocolManager(sb_service, client_name, client_key,
     82                                          wrapped_key, request_context_getter,
     83                                          info_url_prefix, mackey_url_prefix,
     84                                          disable_auto_update);
     85 }
     86 
     87 SafeBrowsingProtocolManager::SafeBrowsingProtocolManager(
     88     SafeBrowsingService* sb_service,
     89     const std::string& client_name,
     90     const std::string& client_key,
     91     const std::string& wrapped_key,
     92     net::URLRequestContextGetter* request_context_getter,
     93     const std::string& http_url_prefix,
     94     const std::string& https_url_prefix,
     95     bool disable_auto_update)
     96     : sb_service_(sb_service),
     97       request_type_(NO_REQUEST),
     98       update_error_count_(0),
     99       gethash_error_count_(0),
    100       update_back_off_mult_(1),
    101       gethash_back_off_mult_(1),
    102       next_update_sec_(-1),
    103       update_state_(FIRST_REQUEST),
    104       initial_request_(true),
    105       chunk_pending_to_write_(false),
    106       client_key_(client_key),
    107       wrapped_key_(wrapped_key),
    108       update_size_(0),
    109       client_name_(client_name),
    110       request_context_getter_(request_context_getter),
    111       http_url_prefix_(http_url_prefix),
    112       https_url_prefix_(https_url_prefix),
    113       disable_auto_update_(disable_auto_update) {
    114   DCHECK(!http_url_prefix_.empty() && !https_url_prefix_.empty());
    115 
    116   // Set the backoff multiplier fuzz to a random value between 0 and 1.
    117   back_off_fuzz_ = static_cast<float>(base::RandDouble());
    118   // The first update must happen between 1-5 minutes of start up.
    119   next_update_sec_ = base::RandInt(60, kSbTimerStartIntervalSec);
    120 
    121   chrome::VersionInfo version_info;
    122   if (!version_info.is_valid() || version_info.Version().empty())
    123     version_ = "0.1";
    124   else
    125     version_ = version_info.Version();
    126 }
    127 
    128 // static
    129 void SafeBrowsingProtocolManager::RecordGetHashResult(
    130     bool is_download, ResultType result_type) {
    131   if (is_download) {
    132     UMA_HISTOGRAM_ENUMERATION("SB2.GetHashResultDownload", result_type,
    133                               GET_HASH_RESULT_MAX);
    134   } else {
    135     UMA_HISTOGRAM_ENUMERATION("SB2.GetHashResult", result_type,
    136                               GET_HASH_RESULT_MAX);
    137   }
    138 }
    139 
    140 SafeBrowsingProtocolManager::~SafeBrowsingProtocolManager() {
    141   // Delete in-progress SafeBrowsing requests.
    142   STLDeleteContainerPairFirstPointers(hash_requests_.begin(),
    143                                       hash_requests_.end());
    144   hash_requests_.clear();
    145 
    146   // Delete in-progress safebrowsing reports (hits and details).
    147   STLDeleteContainerPointers(safebrowsing_reports_.begin(),
    148                              safebrowsing_reports_.end());
    149   safebrowsing_reports_.clear();
    150 }
    151 
    152 // Public API used by the SafeBrowsingService ----------------------------------
    153 
    154 // We can only have one update or chunk request outstanding, but there may be
    155 // multiple GetHash requests pending since we don't want to serialize them and
    156 // slow down the user.
    157 void SafeBrowsingProtocolManager::GetFullHash(
    158     SafeBrowsingService::SafeBrowsingCheck* check,
    159     const std::vector<SBPrefix>& prefixes) {
    160   // If we are in GetHash backoff, we need to check if we're past the next
    161   // allowed time. If we are, we can proceed with the request. If not, we are
    162   // required to return empty results (i.e. treat the page as safe).
    163   if (gethash_error_count_ && Time::Now() <= next_gethash_time_) {
    164     std::vector<SBFullHashResult> full_hashes;
    165     sb_service_->HandleGetHashResults(check, full_hashes, false);
    166     return;
    167   }
    168   bool use_mac = !client_key_.empty();
    169   GURL gethash_url = GetHashUrl(use_mac);
    170   URLFetcher* fetcher = new URLFetcher(gethash_url, URLFetcher::POST, this);
    171   hash_requests_[fetcher] = check;
    172 
    173   std::string get_hash;
    174   SafeBrowsingProtocolParser parser;
    175   parser.FormatGetHash(prefixes, &get_hash);
    176 
    177   fetcher->set_load_flags(net::LOAD_DISABLE_CACHE);
    178   fetcher->set_request_context(request_context_getter_);
    179   fetcher->set_upload_data("text/plain", get_hash);
    180   fetcher->Start();
    181 }
    182 
    183 void SafeBrowsingProtocolManager::GetNextUpdate() {
    184   if (initial_request_) {
    185     if (client_key_.empty() || wrapped_key_.empty()) {
    186       IssueKeyRequest();
    187       return;
    188     } else {
    189       initial_request_ = false;
    190     }
    191   }
    192 
    193   if (!request_.get())
    194     IssueUpdateRequest();
    195 }
    196 
    197 // URLFetcher::Delegate implementation -----------------------------------------
    198 
    199 // All SafeBrowsing request responses are handled here.
    200 // TODO(paulg): Clarify with the SafeBrowsing team whether a failed parse of a
    201 //              chunk should retry the download and parse of that chunk (and
    202 //              what back off / how many times to try), and if that effects the
    203 //              update back off. For now, a failed parse of the chunk means we
    204 //              drop it. This isn't so bad because the next UPDATE_REQUEST we
    205 //              do will report all the chunks we have. If that chunk is still
    206 //              required, the SafeBrowsing servers will tell us to get it again.
    207 void SafeBrowsingProtocolManager::OnURLFetchComplete(
    208     const URLFetcher* source,
    209     const GURL& url,
    210     const net::URLRequestStatus& status,
    211     int response_code,
    212     const ResponseCookies& cookies,
    213     const std::string& data) {
    214   scoped_ptr<const URLFetcher> fetcher;
    215   bool parsed_ok = true;
    216   bool must_back_off = false;  // Reduce SafeBrowsing service query frequency.
    217 
    218   // See if this is a safebrowsing report fetcher. We don't take any action for
    219   // the response to those.
    220   std::set<const URLFetcher*>::iterator sit = safebrowsing_reports_.find(
    221       source);
    222   if (sit != safebrowsing_reports_.end()) {
    223     const URLFetcher* report = *sit;
    224     safebrowsing_reports_.erase(sit);
    225     delete report;
    226     return;
    227   }
    228 
    229   HashRequests::iterator it = hash_requests_.find(source);
    230   if (it != hash_requests_.end()) {
    231     // GetHash response.
    232     fetcher.reset(it->first);
    233     SafeBrowsingService::SafeBrowsingCheck* check = it->second;
    234     std::vector<SBFullHashResult> full_hashes;
    235     bool can_cache = false;
    236     if (response_code == 200 || response_code == 204) {
    237       // For tracking our GetHash false positive (204) rate, compared to real
    238       // (200) responses.
    239       if (response_code == 200)
    240         RecordGetHashResult(check->is_download, GET_HASH_STATUS_200);
    241       else
    242         RecordGetHashResult(check->is_download, GET_HASH_STATUS_204);
    243       can_cache = true;
    244       gethash_error_count_ = 0;
    245       gethash_back_off_mult_ = 1;
    246       bool re_key = false;
    247       SafeBrowsingProtocolParser parser;
    248       parsed_ok = parser.ParseGetHash(data.data(),
    249                                       static_cast<int>(data.length()),
    250                                       client_key_,
    251                                       &re_key,
    252                                       &full_hashes);
    253       if (!parsed_ok) {
    254         // If we fail to parse it, we must still inform the SafeBrowsingService
    255         // so that it doesn't hold up the user's request indefinitely. Not sure
    256         // what to do at that point though!
    257         full_hashes.clear();
    258       } else {
    259         if (re_key)
    260           HandleReKey();
    261       }
    262     } else {
    263       HandleGetHashError(Time::Now());
    264       if (status.status() == net::URLRequestStatus::FAILED) {
    265         VLOG(1) << "SafeBrowsing GetHash request for: " << source->url()
    266                 << " failed with os error: " << status.os_error();
    267       } else {
    268         VLOG(1) << "SafeBrowsing GetHash request for: " << source->url()
    269                 << " failed with error: " << response_code;
    270       }
    271     }
    272 
    273     // Call back the SafeBrowsingService with full_hashes, even if there was a
    274     // parse error or an error response code (in which case full_hashes will be
    275     // empty). We can't block the user regardless of the error status.
    276     sb_service_->HandleGetHashResults(check, full_hashes, can_cache);
    277 
    278     hash_requests_.erase(it);
    279   } else {
    280     // Update, chunk or key response.
    281     fetcher.reset(request_.release());
    282 
    283     if (request_type_ == UPDATE_REQUEST) {
    284       if (!fetcher.get()) {
    285         // We've timed out waiting for an update response, so we've cancelled
    286         // the update request and scheduled a new one. Ignore this response.
    287         return;
    288       }
    289 
    290       // Cancel the update response timeout now that we have the response.
    291       update_timer_.Stop();
    292     }
    293 
    294     if (response_code == 200) {
    295       // We have data from the SafeBrowsing service.
    296       parsed_ok = HandleServiceResponse(source->url(),
    297                                         data.data(),
    298                                         static_cast<int>(data.length()));
    299       if (!parsed_ok) {
    300         VLOG(1) << "SafeBrowsing request for: " << source->url()
    301                 << " failed parse.";
    302         must_back_off = true;
    303         chunk_request_urls_.clear();
    304         UpdateFinished(false);
    305       }
    306 
    307       switch (request_type_) {
    308         case CHUNK_REQUEST:
    309           if (parsed_ok)
    310             chunk_request_urls_.pop_front();
    311           break;
    312         case GETKEY_REQUEST:
    313           if (initial_request_) {
    314             // This is the first request we've made this session. Now that we
    315             // have the keys, do the regular update request.
    316             initial_request_ = false;
    317             GetNextUpdate();
    318             return;
    319           }
    320           break;
    321         case UPDATE_REQUEST:
    322           if (chunk_request_urls_.empty() && parsed_ok) {
    323             // We are up to date since the servers gave us nothing new, so we
    324             // are done with this update cycle.
    325             UpdateFinished(true);
    326           }
    327           break;
    328         default:
    329           NOTREACHED();
    330           break;
    331       }
    332     } else {
    333       // The SafeBrowsing service error, or very bad response code: back off.
    334       must_back_off = true;
    335       if (request_type_ == CHUNK_REQUEST)
    336         chunk_request_urls_.clear();
    337       UpdateFinished(false);
    338       if (status.status() == net::URLRequestStatus::FAILED) {
    339         VLOG(1) << "SafeBrowsing request for: " << source->url()
    340                 << " failed with os error: " << status.os_error();
    341       } else {
    342         VLOG(1) << "SafeBrowsing request for: " << source->url()
    343                 << " failed with error: " << response_code;
    344       }
    345     }
    346   }
    347 
    348   // Schedule a new update request if we've finished retrieving all the chunks
    349   // from the previous update. We treat the update request and the chunk URLs it
    350   // contains as an atomic unit as far as back off is concerned.
    351   if (chunk_request_urls_.empty() &&
    352       (request_type_ == CHUNK_REQUEST || request_type_ == UPDATE_REQUEST))
    353     ScheduleNextUpdate(must_back_off);
    354 
    355   // Get the next chunk if available.
    356   IssueChunkRequest();
    357 }
    358 
    359 bool SafeBrowsingProtocolManager::HandleServiceResponse(const GURL& url,
    360                                                         const char* data,
    361                                                         int length) {
    362   SafeBrowsingProtocolParser parser;
    363 
    364   switch (request_type_) {
    365     case UPDATE_REQUEST: {
    366       int next_update_sec = -1;
    367       bool re_key = false;
    368       bool reset = false;
    369       scoped_ptr<std::vector<SBChunkDelete> > chunk_deletes(
    370           new std::vector<SBChunkDelete>);
    371       std::vector<ChunkUrl> chunk_urls;
    372       if (!parser.ParseUpdate(data, length, client_key_,
    373                               &next_update_sec, &re_key,
    374                               &reset, chunk_deletes.get(), &chunk_urls)) {
    375         return false;
    376       }
    377 
    378       last_update_ = Time::Now();
    379 
    380       if (update_state_ == FIRST_REQUEST)
    381         update_state_ = SECOND_REQUEST;
    382       else if (update_state_ == SECOND_REQUEST)
    383         update_state_ = NORMAL_REQUEST;
    384 
    385       // New time for the next update.
    386       if (next_update_sec > 0) {
    387         next_update_sec_ = next_update_sec;
    388       } else if (update_state_ == SECOND_REQUEST) {
    389         next_update_sec_ = base::RandInt(15 * 60, 45 * 60);
    390       }
    391 
    392       // We need to request a new set of keys for MAC.
    393       if (re_key)
    394         HandleReKey();
    395 
    396       // New chunks to download.
    397       if (!chunk_urls.empty()) {
    398         UMA_HISTOGRAM_COUNTS("SB2.UpdateUrls", chunk_urls.size());
    399         for (size_t i = 0; i < chunk_urls.size(); ++i)
    400           chunk_request_urls_.push_back(chunk_urls[i]);
    401       }
    402 
    403       // Handle the case were the SafeBrowsing service tells us to dump our
    404       // database.
    405       if (reset) {
    406         sb_service_->ResetDatabase();
    407         return true;
    408       }
    409 
    410       // Chunks to delete from our storage.  Pass ownership of
    411       // |chunk_deletes|.
    412       if (!chunk_deletes->empty())
    413         sb_service_->HandleChunkDelete(chunk_deletes.release());
    414 
    415       break;
    416     }
    417     case CHUNK_REQUEST: {
    418       UMA_HISTOGRAM_TIMES("SB2.ChunkRequest",
    419                           base::Time::Now() - chunk_request_start_);
    420 
    421       const ChunkUrl chunk_url = chunk_request_urls_.front();
    422       bool re_key = false;
    423       scoped_ptr<SBChunkList> chunks(new SBChunkList);
    424       UMA_HISTOGRAM_COUNTS("SB2.ChunkSize", length);
    425       update_size_ += length;
    426       if (!parser.ParseChunk(chunk_url.list_name, data, length,
    427                              client_key_, chunk_url.mac,
    428                              &re_key, chunks.get())) {
    429 #ifndef NDEBUG
    430         std::string data_str;
    431         data_str.assign(data, length);
    432         std::string encoded_chunk;
    433         base::Base64Encode(data, &encoded_chunk);
    434         VLOG(1) << "ParseChunk error for chunk: " << chunk_url.url
    435                 << ", client_key: " << client_key_
    436                 << ", wrapped_key: " << wrapped_key_
    437                 << ", mac: " << chunk_url.mac
    438                 << ", Base64Encode(data): " << encoded_chunk
    439                 << ", length: " << length;
    440 #endif
    441         return false;
    442       }
    443 
    444       if (re_key)
    445         HandleReKey();
    446 
    447       // Chunks to add to storage.  Pass ownership of |chunks|.
    448       if (!chunks->empty()) {
    449         chunk_pending_to_write_ = true;
    450         sb_service_->HandleChunk(chunk_url.list_name, chunks.release());
    451       }
    452 
    453       break;
    454     }
    455     case GETKEY_REQUEST: {
    456       std::string client_key, wrapped_key;
    457       if (!parser.ParseNewKey(data, length, &client_key, &wrapped_key))
    458         return false;
    459 
    460       client_key_ = client_key;
    461       wrapped_key_ = wrapped_key;
    462       BrowserThread::PostTask(
    463           BrowserThread::UI, FROM_HERE,
    464           NewRunnableMethod(
    465               sb_service_, &SafeBrowsingService::OnNewMacKeys, client_key_,
    466               wrapped_key_));
    467       break;
    468     }
    469 
    470     default:
    471       return false;
    472   }
    473 
    474   return true;
    475 }
    476 
    477 void SafeBrowsingProtocolManager::Initialize() {
    478   // Don't want to hit the safe browsing servers on build/chrome bots.
    479   scoped_ptr<base::Environment> env(base::Environment::Create());
    480   if (env->HasVar(env_vars::kHeadless))
    481     return;
    482 
    483   ScheduleNextUpdate(false /* no back off */);
    484 }
    485 
    486 void SafeBrowsingProtocolManager::ScheduleNextUpdate(bool back_off) {
    487   DCHECK_GT(next_update_sec_, 0);
    488 
    489   if (disable_auto_update_) {
    490     // Unschedule any current timer.
    491     update_timer_.Stop();
    492     return;
    493   }
    494   // Reschedule with the new update.
    495   const int next_update = GetNextUpdateTime(back_off);
    496   ForceScheduleNextUpdate(next_update);
    497 }
    498 
    499 void SafeBrowsingProtocolManager::ForceScheduleNextUpdate(
    500     const int next_update_msec) {
    501   DCHECK_GE(next_update_msec, 0);
    502   // Unschedule any current timer.
    503   update_timer_.Stop();
    504   update_timer_.Start(TimeDelta::FromMilliseconds(next_update_msec), this,
    505                       &SafeBrowsingProtocolManager::GetNextUpdate);
    506 }
    507 
    508 // According to section 5 of the SafeBrowsing protocol specification, we must
    509 // back off after a certain number of errors. We only change 'next_update_sec_'
    510 // when we receive a response from the SafeBrowsing service.
    511 int SafeBrowsingProtocolManager::GetNextUpdateTime(bool back_off) {
    512   int next = next_update_sec_;
    513   if (back_off) {
    514     next = GetNextBackOffTime(&update_error_count_, &update_back_off_mult_);
    515   } else {
    516     // Successful response means error reset.
    517     update_error_count_ = 0;
    518     update_back_off_mult_ = 1;
    519   }
    520   return next * 1000;  // milliseconds
    521 }
    522 
    523 int SafeBrowsingProtocolManager::GetNextBackOffTime(int* error_count,
    524                                                     int* multiplier) {
    525   DCHECK(multiplier && error_count);
    526   (*error_count)++;
    527   if (*error_count > 1 && *error_count < 6) {
    528     int next = static_cast<int>(*multiplier * (1 + back_off_fuzz_) * 30 * 60);
    529     *multiplier *= 2;
    530     if (*multiplier > kSbMaxBackOff)
    531       *multiplier = kSbMaxBackOff;
    532     return next;
    533   }
    534 
    535   if (*error_count >= 6)
    536     return 60 * 60 * 8;  // 8 hours
    537 
    538   return 60;  // 1 minute
    539 }
    540 
    541 // This request requires getting a list of all the chunks for each list from the
    542 // database asynchronously. The request will be issued when we're called back in
    543 // OnGetChunksComplete.
    544 // TODO(paulg): We should get this at start up and maintain a ChunkRange cache
    545 //              to avoid hitting the database with each update request. On the
    546 //              otherhand, this request will only occur ~20-30 minutes so there
    547 //              isn't that much overhead. Measure!
    548 void SafeBrowsingProtocolManager::IssueUpdateRequest() {
    549   request_type_ = UPDATE_REQUEST;
    550   sb_service_->UpdateStarted();
    551 }
    552 
    553 void SafeBrowsingProtocolManager::IssueChunkRequest() {
    554   // We are only allowed to have one request outstanding at any time.  Also,
    555   // don't get the next url until the previous one has been written to disk so
    556   // that we don't use too much memory.
    557   if (request_.get() || chunk_request_urls_.empty() || chunk_pending_to_write_)
    558     return;
    559 
    560   ChunkUrl next_chunk = chunk_request_urls_.front();
    561   DCHECK(!next_chunk.url.empty());
    562   GURL chunk_url = NextChunkUrl(next_chunk.url);
    563   request_type_ = CHUNK_REQUEST;
    564   request_.reset(new URLFetcher(chunk_url, URLFetcher::GET, this));
    565   request_->set_load_flags(net::LOAD_DISABLE_CACHE);
    566   request_->set_request_context(request_context_getter_);
    567   chunk_request_start_ = base::Time::Now();
    568   request_->Start();
    569 }
    570 
    571 void SafeBrowsingProtocolManager::IssueKeyRequest() {
    572   GURL key_url = MacKeyUrl();
    573   request_type_ = GETKEY_REQUEST;
    574   request_.reset(new URLFetcher(key_url, URLFetcher::GET, this));
    575   request_->set_load_flags(net::LOAD_DISABLE_CACHE);
    576   request_->set_request_context(request_context_getter_);
    577   request_->Start();
    578 }
    579 
    580 void SafeBrowsingProtocolManager::OnGetChunksComplete(
    581     const std::vector<SBListChunkRanges>& lists, bool database_error) {
    582   DCHECK_EQ(request_type_, UPDATE_REQUEST);
    583   if (database_error) {
    584     UpdateFinished(false);
    585     ScheduleNextUpdate(false);
    586     return;
    587   }
    588 
    589   const bool use_mac = !client_key_.empty();
    590 
    591   // Format our stored chunks:
    592   std::string list_data;
    593   bool found_malware = false;
    594   bool found_phishing = false;
    595   for (size_t i = 0; i < lists.size(); ++i) {
    596     list_data.append(FormatList(lists[i], use_mac));
    597     if (lists[i].name == safe_browsing_util::kPhishingList)
    598       found_phishing = true;
    599 
    600     if (lists[i].name == safe_browsing_util::kMalwareList)
    601       found_malware = true;
    602   }
    603 
    604   // If we have an empty database, let the server know we want data for these
    605   // lists.
    606   if (!found_phishing)
    607     list_data.append(FormatList(
    608         SBListChunkRanges(safe_browsing_util::kPhishingList), use_mac));
    609 
    610   if (!found_malware)
    611     list_data.append(FormatList(
    612         SBListChunkRanges(safe_browsing_util::kMalwareList), use_mac));
    613 
    614   GURL update_url = UpdateUrl(use_mac);
    615   request_.reset(new URLFetcher(update_url, URLFetcher::POST, this));
    616   request_->set_load_flags(net::LOAD_DISABLE_CACHE);
    617   request_->set_request_context(request_context_getter_);
    618   request_->set_upload_data("text/plain", list_data);
    619   request_->Start();
    620 
    621   // Begin the update request timeout.
    622   update_timer_.Start(TimeDelta::FromSeconds(kSbMaxUpdateWaitSec), this,
    623                       &SafeBrowsingProtocolManager::UpdateResponseTimeout);
    624 }
    625 
    626 // If we haven't heard back from the server with an update response, this method
    627 // will run. Close the current update session and schedule another update.
    628 void SafeBrowsingProtocolManager::UpdateResponseTimeout() {
    629   DCHECK_EQ(request_type_, UPDATE_REQUEST);
    630   request_.reset();
    631   UpdateFinished(false);
    632   ScheduleNextUpdate(false);
    633 }
    634 
    635 void SafeBrowsingProtocolManager::OnChunkInserted() {
    636   chunk_pending_to_write_ = false;
    637 
    638   if (chunk_request_urls_.empty()) {
    639     UMA_HISTOGRAM_LONG_TIMES("SB2.Update", Time::Now() - last_update_);
    640     UpdateFinished(true);
    641   } else {
    642     IssueChunkRequest();
    643   }
    644 }
    645 
    646 // Sends a SafeBrowsing "hit" for UMA users.
    647 void SafeBrowsingProtocolManager::ReportSafeBrowsingHit(
    648     const GURL& malicious_url,
    649     const GURL& page_url,
    650     const GURL& referrer_url,
    651     bool is_subresource,
    652     SafeBrowsingService::UrlCheckResult threat_type,
    653     const std::string& post_data) {
    654   GURL report_url = SafeBrowsingHitUrl(malicious_url, page_url,
    655                                        referrer_url, is_subresource,
    656                                        threat_type);
    657   URLFetcher* report = new URLFetcher(
    658       report_url, post_data.empty() ? URLFetcher::GET : URLFetcher::POST, this);
    659   report->set_load_flags(net::LOAD_DISABLE_CACHE);
    660   report->set_request_context(request_context_getter_);
    661   if (!post_data.empty())
    662     report->set_upload_data("text/plain", post_data);
    663   report->Start();
    664   safebrowsing_reports_.insert(report);
    665 }
    666 
    667 // Sends malware details for users who opt-in.
    668 void SafeBrowsingProtocolManager::ReportMalwareDetails(
    669     const std::string& report) {
    670   GURL report_url = MalwareDetailsUrl();
    671   URLFetcher* fetcher = new URLFetcher(report_url, URLFetcher::POST, this);
    672   fetcher->set_load_flags(net::LOAD_DISABLE_CACHE);
    673   fetcher->set_request_context(request_context_getter_);
    674   fetcher->set_upload_data("application/octet-stream", report);
    675   // Don't try too hard to send reports on failures.
    676   fetcher->set_automatically_retry_on_5xx(false);
    677   fetcher->Start();
    678   safebrowsing_reports_.insert(fetcher);
    679 }
    680 
    681 
    682 // static
    683 std::string SafeBrowsingProtocolManager::FormatList(
    684     const SBListChunkRanges& list, bool use_mac) {
    685   std::string formatted_results;
    686   formatted_results.append(list.name);
    687   formatted_results.append(";");
    688   if (!list.adds.empty()) {
    689     formatted_results.append("a:" + list.adds);
    690     if (!list.subs.empty() || use_mac)
    691       formatted_results.append(":");
    692   }
    693   if (!list.subs.empty()) {
    694     formatted_results.append("s:" + list.subs);
    695     if (use_mac)
    696       formatted_results.append(":");
    697   }
    698   if (use_mac)
    699     formatted_results.append("mac");
    700   formatted_results.append("\n");
    701 
    702   return formatted_results;
    703 }
    704 
    705 void SafeBrowsingProtocolManager::HandleReKey() {
    706   client_key_.clear();
    707   wrapped_key_.clear();
    708   IssueKeyRequest();
    709 }
    710 
    711 void SafeBrowsingProtocolManager::HandleGetHashError(const Time& now) {
    712   int next = GetNextBackOffTime(&gethash_error_count_, &gethash_back_off_mult_);
    713   next_gethash_time_ = now + TimeDelta::FromSeconds(next);
    714 }
    715 
    716 void SafeBrowsingProtocolManager::UpdateFinished(bool success) {
    717   UMA_HISTOGRAM_COUNTS("SB2.UpdateSize", update_size_);
    718   update_size_ = 0;
    719   sb_service_->UpdateFinished(success);
    720 }
    721 
    722 std::string SafeBrowsingProtocolManager::ComposeUrl(
    723     const std::string& prefix, const std::string& method,
    724     const std::string& client_name, const std::string& version,
    725     const std::string& additional_query) {
    726   DCHECK(!prefix.empty() && !method.empty() &&
    727          !client_name.empty() && !version.empty());
    728   std::string url = StringPrintf("%s/%s?client=%s&appver=%s&pver=2.2",
    729                                  prefix.c_str(), method.c_str(),
    730                                  client_name.c_str(), version.c_str());
    731   if (!additional_query.empty()) {
    732     DCHECK(url.find("?") != std::string::npos);
    733     url.append("&");
    734     url.append(additional_query);
    735   }
    736   return url;
    737 }
    738 
    739 GURL SafeBrowsingProtocolManager::UpdateUrl(bool use_mac) const {
    740   std::string url = ComposeUrl(http_url_prefix_, "downloads", client_name_,
    741                                version_, additional_query_);
    742   if (use_mac) {
    743     url.append("&wrkey=");
    744     url.append(wrapped_key_);
    745   }
    746   return GURL(url);
    747 }
    748 
    749 GURL SafeBrowsingProtocolManager::GetHashUrl(bool use_mac) const {
    750   std::string url= ComposeUrl(http_url_prefix_, "gethash", client_name_,
    751                               version_, additional_query_);
    752   if (use_mac) {
    753     url.append("&wrkey=");
    754     url.append(wrapped_key_);
    755   }
    756   return GURL(url);
    757 }
    758 
    759 GURL SafeBrowsingProtocolManager::MacKeyUrl() const {
    760   return GURL(ComposeUrl(https_url_prefix_, "newkey", client_name_, version_,
    761                          additional_query_));
    762 }
    763 
    764 GURL SafeBrowsingProtocolManager::SafeBrowsingHitUrl(
    765     const GURL& malicious_url, const GURL& page_url,
    766     const GURL& referrer_url, bool is_subresource,
    767     SafeBrowsingService::UrlCheckResult threat_type) const {
    768   DCHECK(threat_type == SafeBrowsingService::URL_MALWARE ||
    769          threat_type == SafeBrowsingService::URL_PHISHING ||
    770          threat_type == SafeBrowsingService::BINARY_MALWARE_URL ||
    771          threat_type == SafeBrowsingService::BINARY_MALWARE_HASH);
    772   // The malware and phishing hits go over HTTP.
    773   std::string url = ComposeUrl(http_url_prefix_, "report", client_name_,
    774                                version_, additional_query_);
    775   std::string threat_list = "none";
    776   switch (threat_type) {
    777     case SafeBrowsingService::URL_MALWARE:
    778       threat_list = "malblhit";
    779       break;
    780     case SafeBrowsingService::URL_PHISHING:
    781       threat_list = "phishblhit";
    782       break;
    783     case SafeBrowsingService::BINARY_MALWARE_URL:
    784       threat_list = "binurlhit";
    785       break;
    786     case SafeBrowsingService::BINARY_MALWARE_HASH:
    787       threat_list = "binhashhit";
    788       break;
    789     default:
    790       NOTREACHED();
    791   }
    792   return GURL(StringPrintf("%s&evts=%s&evtd=%s&evtr=%s&evhr=%s&evtb=%d",
    793       url.c_str(), threat_list.c_str(),
    794       EscapeQueryParamValue(malicious_url.spec(), true).c_str(),
    795       EscapeQueryParamValue(page_url.spec(), true).c_str(),
    796       EscapeQueryParamValue(referrer_url.spec(), true).c_str(),
    797       is_subresource));
    798 }
    799 
    800 GURL SafeBrowsingProtocolManager::MalwareDetailsUrl() const {
    801   // The malware details go over HTTPS.
    802   std::string url = StringPrintf(
    803           "%s/clientreport/malware?client=%s&appver=%s&pver=1.0",
    804           https_url_prefix_.c_str(),
    805           client_name_.c_str(),
    806           version_.c_str());
    807   return GURL(url);
    808 }
    809 
    810 GURL SafeBrowsingProtocolManager::NextChunkUrl(const std::string& url) const {
    811   std::string next_url;
    812   if (!StartsWithASCII(url, "http://", false) &&
    813       !StartsWithASCII(url, "https://", false)) {
    814     next_url.append("http://");
    815     next_url.append(url);
    816   } else {
    817     next_url = url;
    818   }
    819   if (!additional_query_.empty()) {
    820     if (next_url.find("?") != std::string::npos) {
    821       next_url.append("&");
    822     } else {
    823       next_url.append("?");
    824     }
    825     next_url.append(additional_query_);
    826   }
    827   return GURL(next_url);
    828 }
    829