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/client_side_detection_service.h"
6
7#include "base/bind.h"
8#include "base/command_line.h"
9#include "base/logging.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/message_loop/message_loop.h"
12#include "base/metrics/histogram.h"
13#include "base/metrics/sparse_histogram.h"
14#include "base/prefs/pref_service.h"
15#include "base/stl_util.h"
16#include "base/strings/string_util.h"
17#include "base/time/time.h"
18#include "chrome/browser/browser_process.h"
19#include "chrome/browser/profiles/profile.h"
20#include "chrome/common/chrome_switches.h"
21#include "chrome/common/pref_names.h"
22#include "chrome/common/safe_browsing/client_model.pb.h"
23#include "chrome/common/safe_browsing/csd.pb.h"
24#include "chrome/common/safe_browsing/safebrowsing_messages.h"
25#include "content/public/browser/browser_thread.h"
26#include "content/public/browser/notification_service.h"
27#include "content/public/browser/notification_types.h"
28#include "content/public/browser/render_process_host.h"
29#include "crypto/sha2.h"
30#include "google_apis/google_api_keys.h"
31#include "net/base/escape.h"
32#include "net/base/load_flags.h"
33#include "net/http/http_response_headers.h"
34#include "net/http/http_status_code.h"
35#include "net/url_request/url_fetcher.h"
36#include "net/url_request/url_request_context_getter.h"
37#include "net/url_request/url_request_status.h"
38#include "url/gurl.h"
39
40using content::BrowserThread;
41
42namespace safe_browsing {
43
44namespace {
45
46  // malware report type for UMA histogram counting.
47  enum MalwareReportTypes {
48    REPORT_SENT,
49    REPORT_HIT_LIMIT,
50    REPORT_FAILED_SERIALIZATION,
51
52    // Always at the end
53    REPORT_RESULT_MAX
54  };
55
56  void UpdateEnumUMAHistogram(MalwareReportTypes report_type) {
57    DCHECK(report_type >= 0 && report_type < REPORT_RESULT_MAX);
58    UMA_HISTOGRAM_ENUMERATION("SBClientMalware.SentReports",
59                              report_type, REPORT_RESULT_MAX);
60  }
61
62}  // namespace
63
64const size_t ClientSideDetectionService::kMaxModelSizeBytes = 90 * 1024;
65const int ClientSideDetectionService::kMaxReportsPerInterval = 3;
66// TODO(noelutz): once we know this mechanism works as intended we should fetch
67// the model much more frequently.  E.g., every 5 minutes or so.
68const int ClientSideDetectionService::kClientModelFetchIntervalMs = 3600 * 1000;
69const int ClientSideDetectionService::kInitialClientModelFetchDelayMs = 10000;
70
71const int ClientSideDetectionService::kReportsIntervalDays = 1;
72const int ClientSideDetectionService::kNegativeCacheIntervalDays = 1;
73const int ClientSideDetectionService::kPositiveCacheIntervalMinutes = 30;
74
75const char ClientSideDetectionService::kClientReportPhishingUrl[] =
76    "https://sb-ssl.google.com/safebrowsing/clientreport/phishing";
77const char ClientSideDetectionService::kClientReportMalwareUrl[] =
78    "https://sb-ssl.google.com/safebrowsing/clientreport/malware-check";
79const char ClientSideDetectionService::kClientModelUrl[] =
80    "https://ssl.gstatic.com/safebrowsing/csd/client_model_v5.pb";
81
82struct ClientSideDetectionService::ClientReportInfo {
83  ClientReportPhishingRequestCallback callback;
84  GURL phishing_url;
85};
86
87struct ClientSideDetectionService::ClientMalwareReportInfo {
88  ClientReportMalwareRequestCallback callback;
89  // This is the original landing url, may not be the malware url.
90  GURL original_url;
91};
92
93ClientSideDetectionService::CacheState::CacheState(bool phish, base::Time time)
94    : is_phishing(phish),
95      timestamp(time) {}
96
97ClientSideDetectionService::ClientSideDetectionService(
98    net::URLRequestContextGetter* request_context_getter)
99    : enabled_(false),
100      weak_factory_(this),
101      request_context_getter_(request_context_getter) {
102  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
103                 content::NotificationService::AllBrowserContextsAndSources());
104}
105
106ClientSideDetectionService::~ClientSideDetectionService() {
107  weak_factory_.InvalidateWeakPtrs();
108  STLDeleteContainerPairPointers(client_phishing_reports_.begin(),
109                                 client_phishing_reports_.end());
110  client_phishing_reports_.clear();
111  STLDeleteContainerPairPointers(client_malware_reports_.begin(),
112                                 client_malware_reports_.end());
113  client_malware_reports_.clear();
114}
115
116// static
117ClientSideDetectionService* ClientSideDetectionService::Create(
118    net::URLRequestContextGetter* request_context_getter) {
119  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
120  scoped_ptr<ClientSideDetectionService> service(
121      new ClientSideDetectionService(request_context_getter));
122  if (!service->InitializePrivateNetworks()) {
123    UMA_HISTOGRAM_COUNTS("SBClientPhishing.InitPrivateNetworksFailed", 1);
124    return NULL;
125  }
126  return service.release();
127}
128
129void ClientSideDetectionService::SetEnabledAndRefreshState(bool enabled) {
130  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
131  SendModelToRenderers();  // always refresh the renderer state
132  if (enabled == enabled_)
133    return;
134  enabled_ = enabled;
135  if (enabled_) {
136    // Refresh the model when the service is enabled.  This can happen when the
137    // preference is toggled, or early during startup if the preference is
138    // already enabled. In a lot of cases the model will be in the cache so it
139    // won't actually be fetched from the network.
140    // We delay the first model fetch to avoid slowing down browser startup.
141    ScheduleFetchModel(kInitialClientModelFetchDelayMs);
142  } else {
143    // Cancel pending requests.
144    model_fetcher_.reset();
145    // Invoke pending callbacks with a false verdict.
146    for (std::map<const net::URLFetcher*, ClientReportInfo*>::iterator it =
147             client_phishing_reports_.begin();
148         it != client_phishing_reports_.end(); ++it) {
149      ClientReportInfo* info = it->second;
150      if (!info->callback.is_null())
151        info->callback.Run(info->phishing_url, false);
152    }
153    STLDeleteContainerPairPointers(client_phishing_reports_.begin(),
154                                   client_phishing_reports_.end());
155    client_phishing_reports_.clear();
156    for (std::map<const net::URLFetcher*, ClientMalwareReportInfo*>::iterator it
157             = client_malware_reports_.begin();
158         it != client_malware_reports_.end(); ++it) {
159      ClientMalwareReportInfo* info = it->second;
160      if (!info->callback.is_null())
161        info->callback.Run(info->original_url, info->original_url, false);
162    }
163    STLDeleteContainerPairPointers(client_malware_reports_.begin(),
164                                   client_malware_reports_.end());
165    client_malware_reports_.clear();
166    cache_.clear();
167  }
168}
169
170void ClientSideDetectionService::SendClientReportPhishingRequest(
171    ClientPhishingRequest* verdict,
172    const ClientReportPhishingRequestCallback& callback) {
173  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
174  base::MessageLoop::current()->PostTask(
175      FROM_HERE,
176      base::Bind(&ClientSideDetectionService::StartClientReportPhishingRequest,
177                 weak_factory_.GetWeakPtr(), verdict, callback));
178}
179
180void ClientSideDetectionService::SendClientReportMalwareRequest(
181    ClientMalwareRequest* verdict,
182    const ClientReportMalwareRequestCallback& callback) {
183  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
184  base::MessageLoop::current()->PostTask(
185      FROM_HERE,
186      base::Bind(&ClientSideDetectionService::StartClientReportMalwareRequest,
187                 weak_factory_.GetWeakPtr(), verdict, callback));
188}
189
190bool ClientSideDetectionService::IsPrivateIPAddress(
191    const std::string& ip_address) const {
192  net::IPAddressNumber ip_number;
193  if (!net::ParseIPLiteralToNumber(ip_address, &ip_number)) {
194    VLOG(2) << "Unable to parse IP address: '" << ip_address << "'";
195    // Err on the side of safety and assume this might be private.
196    return true;
197  }
198
199  for (std::vector<AddressRange>::const_iterator it =
200           private_networks_.begin();
201       it != private_networks_.end(); ++it) {
202    if (net::IPNumberMatchesPrefix(ip_number, it->first, it->second)) {
203      return true;
204    }
205  }
206  return false;
207}
208
209void ClientSideDetectionService::OnURLFetchComplete(
210    const net::URLFetcher* source) {
211  std::string data;
212  source->GetResponseAsString(&data);
213  if (source == model_fetcher_.get()) {
214    HandleModelResponse(
215        source, source->GetURL(), source->GetStatus(),
216        source->GetResponseCode(), source->GetCookies(), data);
217  } else if (client_phishing_reports_.find(source) !=
218             client_phishing_reports_.end()) {
219    HandlePhishingVerdict(
220        source, source->GetURL(), source->GetStatus(),
221        source->GetResponseCode(), source->GetCookies(), data);
222  } else if (client_malware_reports_.find(source) !=
223             client_malware_reports_.end()) {
224    HandleMalwareVerdict(
225        source, source->GetURL(), source->GetStatus(),
226        source->GetResponseCode(), source->GetCookies(), data);
227  } else {
228    NOTREACHED();
229  }
230}
231
232void ClientSideDetectionService::Observe(
233    int type,
234    const content::NotificationSource& source,
235    const content::NotificationDetails& details) {
236  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
237  DCHECK(type == content::NOTIFICATION_RENDERER_PROCESS_CREATED);
238  if (!model_.get()) {
239    // Model might not be ready or maybe there was an error.
240    return;
241  }
242  SendModelToProcess(
243      content::Source<content::RenderProcessHost>(source).ptr());
244}
245
246void ClientSideDetectionService::SendModelToProcess(
247    content::RenderProcessHost* process) {
248  // The ClientSideDetectionService is enabled if _any_ active profile has
249  // SafeBrowsing turned on.  Here we check the profile for each renderer
250  // process and only send the model to those that have SafeBrowsing enabled.
251  Profile* profile = Profile::FromBrowserContext(process->GetBrowserContext());
252  std::string model;
253  if (profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) {
254    VLOG(2) << "Sending phishing model to RenderProcessHost @" << process;
255    model = model_str_;
256  } else {
257    VLOG(2) << "Disabling client-side phishing detection for "
258            << "RenderProcessHost @" << process;
259  }
260  process->Send(new SafeBrowsingMsg_SetPhishingModel(model));
261}
262
263void ClientSideDetectionService::SendModelToRenderers() {
264  for (content::RenderProcessHost::iterator i(
265          content::RenderProcessHost::AllHostsIterator());
266       !i.IsAtEnd(); i.Advance()) {
267    SendModelToProcess(i.GetCurrentValue());
268  }
269}
270
271void ClientSideDetectionService::ScheduleFetchModel(int64 delay_ms) {
272  if (CommandLine::ForCurrentProcess()->HasSwitch(
273      switches::kSbDisableAutoUpdate))
274    return;
275  base::MessageLoop::current()->PostDelayedTask(
276      FROM_HERE,
277      base::Bind(&ClientSideDetectionService::StartFetchModel,
278                 weak_factory_.GetWeakPtr()),
279      base::TimeDelta::FromMilliseconds(delay_ms));
280}
281
282void ClientSideDetectionService::StartFetchModel() {
283  if (enabled_) {
284    // Start fetching the model either from the cache or possibly from the
285    // network if the model isn't in the cache.
286    model_fetcher_.reset(net::URLFetcher::Create(
287        0 /* ID used for testing */, GURL(kClientModelUrl),
288        net::URLFetcher::GET, this));
289    model_fetcher_->SetRequestContext(request_context_getter_.get());
290    model_fetcher_->Start();
291  }
292}
293
294void ClientSideDetectionService::EndFetchModel(ClientModelStatus status) {
295  UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.ClientModelStatus",
296                            status,
297                            MODEL_STATUS_MAX);
298  if (status == MODEL_SUCCESS) {
299    SetBadSubnets(*model_, &bad_subnets_);
300    SendModelToRenderers();
301  }
302  int delay_ms = kClientModelFetchIntervalMs;
303  // If the most recently fetched model had a valid max-age and the model was
304  // valid we're scheduling the next model update for after the max-age expired.
305  if (model_max_age_.get() &&
306      (status == MODEL_SUCCESS || status == MODEL_NOT_CHANGED)) {
307    // We're adding 60s of additional delay to make sure we're past
308    // the model's age.
309    *model_max_age_ += base::TimeDelta::FromMinutes(1);
310    delay_ms = model_max_age_->InMilliseconds();
311  }
312  model_max_age_.reset();
313
314  // Schedule the next model reload.
315  ScheduleFetchModel(delay_ms);
316}
317
318void ClientSideDetectionService::StartClientReportPhishingRequest(
319    ClientPhishingRequest* verdict,
320    const ClientReportPhishingRequestCallback& callback) {
321  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
322  scoped_ptr<ClientPhishingRequest> request(verdict);
323
324  if (!enabled_) {
325    if (!callback.is_null())
326      callback.Run(GURL(request->url()), false);
327    return;
328  }
329
330  std::string request_data;
331  if (!request->SerializeToString(&request_data)) {
332    UMA_HISTOGRAM_COUNTS("SBClientPhishing.RequestNotSerialized", 1);
333    VLOG(1) << "Unable to serialize the CSD request. Proto file changed?";
334    if (!callback.is_null())
335      callback.Run(GURL(request->url()), false);
336    return;
337  }
338
339  net::URLFetcher* fetcher = net::URLFetcher::Create(
340      0 /* ID used for testing */,
341      GetClientReportUrl(kClientReportPhishingUrl),
342      net::URLFetcher::POST, this);
343
344  // Remember which callback and URL correspond to the current fetcher object.
345  ClientReportInfo* info = new ClientReportInfo;
346  info->callback = callback;
347  info->phishing_url = GURL(request->url());
348  client_phishing_reports_[fetcher] = info;
349
350  fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE);
351  fetcher->SetRequestContext(request_context_getter_.get());
352  fetcher->SetUploadData("application/octet-stream", request_data);
353  fetcher->Start();
354
355  // Record that we made a request
356  phishing_report_times_.push(base::Time::Now());
357}
358
359void ClientSideDetectionService::StartClientReportMalwareRequest(
360    ClientMalwareRequest* verdict,
361    const ClientReportMalwareRequestCallback& callback) {
362  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
363  scoped_ptr<ClientMalwareRequest> request(verdict);
364
365  if (!enabled_) {
366    if (!callback.is_null())
367      callback.Run(GURL(request->url()), GURL(request->url()), false);
368    return;
369  }
370
371  if (OverMalwareReportLimit()) {
372    UpdateEnumUMAHistogram(REPORT_HIT_LIMIT);
373    DVLOG(1) << "Too many malware report requests sent recently."
374             << "Skip sending malware report for " << GURL(request->url());
375    if (!callback.is_null())
376      callback.Run(GURL(request->url()), GURL(request->url()), false);
377    return;
378  }
379
380  std::string request_data;
381  if (!request->SerializeToString(&request_data)) {
382    UpdateEnumUMAHistogram(REPORT_FAILED_SERIALIZATION);
383    DVLOG(1) << "Unable to serialize the CSD request. Proto file changed?";
384    if (!callback.is_null())
385      callback.Run(GURL(request->url()), GURL(request->url()), false);
386    return;
387  }
388
389  net::URLFetcher* fetcher = net::URLFetcher::Create(
390      0 /* ID used for testing */,
391      GetClientReportUrl(kClientReportMalwareUrl),
392      net::URLFetcher::POST, this);
393
394  // Remember which callback and URL correspond to the current fetcher object.
395  ClientMalwareReportInfo* info = new ClientMalwareReportInfo;
396  info->callback = callback;
397  info->original_url = GURL(request->url());
398  client_malware_reports_[fetcher] = info;
399
400  fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE);
401  fetcher->SetRequestContext(request_context_getter_.get());
402  fetcher->SetUploadData("application/octet-stream", request_data);
403  fetcher->Start();
404
405  UMA_HISTOGRAM_ENUMERATION("SBClientMalware.SentReports",
406                            REPORT_SENT, REPORT_RESULT_MAX);
407
408  UMA_HISTOGRAM_COUNTS("SBClientMalware.IPBlacklistRequestPayloadSize",
409                       request_data.size());
410
411  // Record that we made a malware request
412  malware_report_times_.push(base::Time::Now());
413}
414
415void ClientSideDetectionService::HandleModelResponse(
416    const net::URLFetcher* source,
417    const GURL& url,
418    const net::URLRequestStatus& status,
419    int response_code,
420    const net::ResponseCookies& cookies,
421    const std::string& data) {
422  base::TimeDelta max_age;
423  if (status.is_success() && net::HTTP_OK == response_code &&
424      source->GetResponseHeaders() &&
425      source->GetResponseHeaders()->GetMaxAgeValue(&max_age)) {
426    model_max_age_.reset(new base::TimeDelta(max_age));
427  }
428  scoped_ptr<ClientSideModel> model(new ClientSideModel());
429  ClientModelStatus model_status;
430  if (!status.is_success() || net::HTTP_OK != response_code) {
431    model_status = MODEL_FETCH_FAILED;
432  } else if (data.empty()) {
433    model_status = MODEL_EMPTY;
434  } else if (data.size() > kMaxModelSizeBytes) {
435    model_status = MODEL_TOO_LARGE;
436  } else if (!model->ParseFromString(data)) {
437    model_status = MODEL_PARSE_ERROR;
438  } else if (!model->IsInitialized() || !model->has_version()) {
439    model_status = MODEL_MISSING_FIELDS;
440  } else if (!ModelHasValidHashIds(*model)) {
441    model_status = MODEL_BAD_HASH_IDS;
442  } else if (model->version() < 0 ||
443             (model_.get() && model->version() < model_->version())) {
444    model_status = MODEL_INVALID_VERSION_NUMBER;
445  } else if (model_.get() && model->version() == model_->version()) {
446    model_status = MODEL_NOT_CHANGED;
447  } else {
448    // The model is valid => replace the existing model with the new one.
449    model_str_.assign(data);
450    model_.swap(model);
451    model_status = MODEL_SUCCESS;
452  }
453  EndFetchModel(model_status);
454}
455
456void ClientSideDetectionService::HandlePhishingVerdict(
457    const net::URLFetcher* source,
458    const GURL& url,
459    const net::URLRequestStatus& status,
460    int response_code,
461    const net::ResponseCookies& cookies,
462    const std::string& data) {
463  ClientPhishingResponse response;
464  scoped_ptr<ClientReportInfo> info(client_phishing_reports_[source]);
465  bool is_phishing = false;
466  if (status.is_success() && net::HTTP_OK == response_code &&
467      response.ParseFromString(data)) {
468    // Cache response, possibly flushing an old one.
469    cache_[info->phishing_url] =
470        make_linked_ptr(new CacheState(response.phishy(), base::Time::Now()));
471    is_phishing = response.phishy();
472  } else {
473    DLOG(ERROR) << "Unable to get the server verdict for URL: "
474                << info->phishing_url << " status: " << status.status() << " "
475                << "response_code:" << response_code;
476  }
477  if (!info->callback.is_null())
478    info->callback.Run(info->phishing_url, is_phishing);
479  client_phishing_reports_.erase(source);
480  delete source;
481}
482
483void ClientSideDetectionService::HandleMalwareVerdict(
484    const net::URLFetcher* source,
485    const GURL& url,
486    const net::URLRequestStatus& status,
487    int response_code,
488    const net::ResponseCookies& cookies,
489    const std::string& data) {
490  if (status.is_success()) {
491    UMA_HISTOGRAM_SPARSE_SLOWLY(
492        "SBClientMalware.IPBlacklistRequestResponseCode", response_code);
493  }
494  // status error is negative, so we put - in front of it.
495  UMA_HISTOGRAM_SPARSE_SLOWLY(
496      "SBClientMalware.IPBlacklistRequestNetError", -status.error());
497
498  ClientMalwareResponse response;
499  scoped_ptr<ClientMalwareReportInfo> info(client_malware_reports_[source]);
500  bool should_blacklist = false;
501  if (status.is_success() && net::HTTP_OK == response_code &&
502      response.ParseFromString(data)) {
503    should_blacklist = response.blacklist();
504  } else {
505    DLOG(ERROR) << "Unable to get the server verdict for URL: "
506                << info->original_url << " status: " << status.status() << " "
507                << "response_code:" << response_code;
508  }
509
510  if (!info->callback.is_null()) {
511    if (response.has_bad_url())
512      info->callback.Run(info->original_url, GURL(response.bad_url()),
513                         should_blacklist);
514    else
515      info->callback.Run(info->original_url, info->original_url, false);
516  }
517
518  client_malware_reports_.erase(source);
519  delete source;
520}
521
522bool ClientSideDetectionService::IsInCache(const GURL& url) {
523  UpdateCache();
524
525  return cache_.find(url) != cache_.end();
526}
527
528bool ClientSideDetectionService::GetValidCachedResult(const GURL& url,
529                                                      bool* is_phishing) {
530  UpdateCache();
531
532  PhishingCache::iterator it = cache_.find(url);
533  if (it == cache_.end()) {
534    return false;
535  }
536
537  // We still need to check if the result is valid.
538  const CacheState& cache_state = *it->second;
539  if (cache_state.is_phishing ?
540      cache_state.timestamp > base::Time::Now() -
541          base::TimeDelta::FromMinutes(kPositiveCacheIntervalMinutes) :
542      cache_state.timestamp > base::Time::Now() -
543          base::TimeDelta::FromDays(kNegativeCacheIntervalDays)) {
544    *is_phishing = cache_state.is_phishing;
545    return true;
546  }
547  return false;
548}
549
550void ClientSideDetectionService::UpdateCache() {
551  // Since we limit the number of requests but allow pass-through for cache
552  // refreshes, we don't want to remove elements from the cache if they
553  // could be used for this purpose even if we will not use the entry to
554  // satisfy the request from the cache.
555  base::TimeDelta positive_cache_interval =
556      std::max(base::TimeDelta::FromMinutes(kPositiveCacheIntervalMinutes),
557               base::TimeDelta::FromDays(kReportsIntervalDays));
558  base::TimeDelta negative_cache_interval =
559      std::max(base::TimeDelta::FromDays(kNegativeCacheIntervalDays),
560               base::TimeDelta::FromDays(kReportsIntervalDays));
561
562  // Remove elements from the cache that will no longer be used.
563  for (PhishingCache::iterator it = cache_.begin(); it != cache_.end();) {
564    const CacheState& cache_state = *it->second;
565    if (cache_state.is_phishing ?
566        cache_state.timestamp > base::Time::Now() - positive_cache_interval :
567        cache_state.timestamp > base::Time::Now() - negative_cache_interval) {
568      ++it;
569    } else {
570      cache_.erase(it++);
571    }
572  }
573}
574
575bool ClientSideDetectionService::OverMalwareReportLimit() {
576  return GetMalwareNumReports() > kMaxReportsPerInterval;
577}
578
579bool ClientSideDetectionService::OverPhishingReportLimit() {
580  return GetPhishingNumReports() > kMaxReportsPerInterval;
581}
582
583int ClientSideDetectionService::GetMalwareNumReports() {
584  return GetNumReports(&malware_report_times_);
585}
586
587int ClientSideDetectionService::GetPhishingNumReports() {
588  return GetNumReports(&phishing_report_times_);
589}
590
591int ClientSideDetectionService::GetNumReports(
592    std::queue<base::Time>* report_times) {
593  base::Time cutoff =
594      base::Time::Now() - base::TimeDelta::FromDays(kReportsIntervalDays);
595
596  // Erase items older than cutoff because we will never care about them again.
597  while (!report_times->empty() &&
598         report_times->front() < cutoff) {
599    report_times->pop();
600  }
601
602  // Return the number of elements that are above the cutoff.
603  return report_times->size();
604}
605
606bool ClientSideDetectionService::InitializePrivateNetworks() {
607  static const char* const kPrivateNetworks[] = {
608    "10.0.0.0/8",
609    "127.0.0.0/8",
610    "172.16.0.0/12",
611    "192.168.0.0/16",
612    // IPv6 address ranges
613    "fc00::/7",
614    "fec0::/10",
615    "::1/128",
616  };
617
618  for (size_t i = 0; i < arraysize(kPrivateNetworks); ++i) {
619    net::IPAddressNumber ip_number;
620    size_t prefix_length;
621    if (net::ParseCIDRBlock(kPrivateNetworks[i], &ip_number, &prefix_length)) {
622      private_networks_.push_back(std::make_pair(ip_number, prefix_length));
623    } else {
624      DLOG(FATAL) << "Unable to parse IP address range: "
625                  << kPrivateNetworks[i];
626      return false;
627    }
628  }
629  return true;
630}
631
632// static
633void ClientSideDetectionService::SetBadSubnets(const ClientSideModel& model,
634                                               BadSubnetMap* bad_subnets) {
635  bad_subnets->clear();
636  for (int i = 0; i < model.bad_subnet_size(); ++i) {
637    int size = model.bad_subnet(i).size();
638    if (size < 0 || size > static_cast<int>(net::kIPv6AddressSize) * 8) {
639      DLOG(ERROR) << "Invalid bad subnet size: " << size;
640      continue;
641    }
642    if (model.bad_subnet(i).prefix().size() != crypto::kSHA256Length) {
643      DLOG(ERROR) << "Invalid bad subnet prefix length: "
644                  << model.bad_subnet(i).prefix().size();
645      continue;
646    }
647    // We precompute the mask for the given subnet size to speed up lookups.
648    // Basically we need to create a 16B long string which has the highest
649    // |size| bits sets to one.
650    std::string mask(net::kIPv6AddressSize, '\x00');
651    mask.replace(0, size / 8, size / 8, '\xFF');
652    if (size % 8) {
653      mask[size / 8] = 0xFF << (8 - (size % 8));
654    }
655    (*bad_subnets)[mask].insert(model.bad_subnet(i).prefix());
656  }
657}
658
659// static
660bool ClientSideDetectionService::ModelHasValidHashIds(
661    const ClientSideModel& model) {
662  const int max_index = model.hashes_size() - 1;
663  for (int i = 0; i < model.rule_size(); ++i) {
664    for (int j = 0; j < model.rule(i).feature_size(); ++j) {
665      if (model.rule(i).feature(j) < 0 ||
666          model.rule(i).feature(j) > max_index) {
667        return false;
668      }
669    }
670  }
671  for (int i = 0; i < model.page_term_size(); ++i) {
672    if (model.page_term(i) < 0 || model.page_term(i) > max_index) {
673      return false;
674    }
675  }
676  return true;
677}
678
679// static
680GURL ClientSideDetectionService::GetClientReportUrl(
681    const std::string& report_url) {
682  GURL url(report_url);
683  std::string api_key = google_apis::GetAPIKey();
684  if (!api_key.empty())
685    url = url.Resolve("?key=" + net::EscapeQueryParamValue(api_key, true));
686
687  return url;
688}
689}  // namespace safe_browsing
690