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