Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/base/sdch_manager.h" | 5 #include "net/base/sdch_manager.h" |
| 6 | 6 |
| 7 #include "base/base64.h" | 7 #include "base/base64.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
| 10 #include "base/strings/string_number_conversions.h" | 10 #include "base/strings/string_number_conversions.h" |
| 11 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
| 12 #include "crypto/sha2.h" | 12 #include "crypto/sha2.h" |
| 13 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | 13 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
| 14 #include "net/url_request/url_request_http_job.h" | 14 #include "net/url_request/url_request_http_job.h" |
| 15 | 15 |
| 16 namespace { | |
| 17 | |
| 18 // Spec collision warning: URLs may have domain names which have a | |
| 19 // trailing "." (e.g. "http://this.is.a.domain.com./file.txt"). The | |
| 20 // SDCH spec was written to parallel RFC 2965 (an old version of the | |
| 21 // cookie spec, which didn't precisely address the issue of trailing "."s. | |
| 22 // The newest version of the cookie spec, RFC 6265, specifically prohibits | |
| 23 // trailing "."s from cookie domains. So we prohibit trailing "."s in | |
| 24 // dictionary domains, and strip any trailing "." from URL domains. | |
| 25 bool StripTrailingDot(std::string* host) { | |
| 26 if (host->size() == 0) | |
|
Ryan Sleevi
2014/09/22 20:30:37
host->empty()
(a nit I hold on to from the days o
Randy Smith (Not in Mondays)
2014/09/23 19:52:58
Done.
| |
| 27 return false; | |
| 28 | |
| 29 if ((*host)[host->size() - 1] != '.') | |
|
Ryan Sleevi
2014/09/22 20:30:36
if (*host->rbegin() != '.')
return false;
Lazin
Randy Smith (Not in Mondays)
2014/09/23 19:52:58
Agreed :-}. Done.
| |
| 30 return false; | |
| 31 | |
| 32 host->resize(host->size() - 1); | |
| 33 return true; | |
| 34 } | |
| 35 | |
| 36 void StripTrailingDotURL(GURL* gurl) { | |
|
Ryan Sleevi
2014/09/22 20:30:36
naming wise, this felt a little weird, although I
Randy Smith (Not in Mondays)
2014/09/23 19:52:58
I don't want "NormalizeToDictionary" because to me
| |
| 37 std::string host(gurl->host()); | |
| 38 | |
| 39 if (!StripTrailingDot(&host)) | |
| 40 return; | |
|
Ryan Sleevi
2014/09/22 20:30:36
Just inline this? You never use "StripTrailingDot"
Randy Smith (Not in Mondays)
2014/09/23 19:52:58
Done.
| |
| 41 | |
| 42 GURL::Replacements replacements; | |
| 43 replacements.SetHostStr(host); | |
| 44 *gurl = gurl->ReplaceComponents(replacements); | |
| 45 return; | |
| 46 } | |
| 47 | |
| 48 } // namespace | |
| 49 | |
| 16 namespace net { | 50 namespace net { |
| 17 | 51 |
| 18 //------------------------------------------------------------------------------ | 52 //------------------------------------------------------------------------------ |
| 19 // static | 53 // static |
| 20 | 54 |
| 21 // Adjust SDCH limits downwards for mobile. | 55 // Adjust SDCH limits downwards for mobile. |
| 22 #if defined(OS_ANDROID) || defined(OS_IOS) | 56 #if defined(OS_ANDROID) || defined(OS_IOS) |
| 23 // static | 57 // static |
| 24 const size_t SdchManager::kMaxDictionaryCount = 1; | 58 const size_t SdchManager::kMaxDictionaryCount = 1; |
| 25 const size_t SdchManager::kMaxDictionarySize = 500 * 1000; | 59 const size_t SdchManager::kMaxDictionarySize = 500 * 1000; |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 122 registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES).empty()) { | 156 registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES).empty()) { |
| 123 SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN); | 157 SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN); |
| 124 return false; // domain was a TLD. | 158 return false; // domain was a TLD. |
| 125 } | 159 } |
| 126 if (!Dictionary::DomainMatch(dictionary_url, domain)) { | 160 if (!Dictionary::DomainMatch(dictionary_url, domain)) { |
| 127 SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL); | 161 SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL); |
| 128 return false; | 162 return false; |
| 129 } | 163 } |
| 130 | 164 |
| 131 std::string referrer_url_host = dictionary_url.host(); | 165 std::string referrer_url_host = dictionary_url.host(); |
| 132 size_t postfix_domain_index = referrer_url_host.rfind(domain); | 166 size_t postfix_domain_index = referrer_url_host.rfind(domain); |
|
Ryan Sleevi
2014/09/22 22:49:42
BUG: Check your npos results! Bad things happen he
Randy Smith (Not in Mondays)
2014/09/23 19:52:58
Ignoring all comments on untouched code for this C
| |
| 133 // See if it is indeed a postfix, or just an internal string. | 167 // See if it is indeed a postfix, or just an internal string. |
| 134 if (referrer_url_host.size() == postfix_domain_index + domain.size()) { | 168 if (referrer_url_host.size() == postfix_domain_index + domain.size()) { |
|
Ryan Sleevi
2014/09/22 22:49:42
I find this logic really, really weird.
Why not j
| |
| 135 // It is a postfix... so check to see if there's a dot in the prefix. | 169 // It is a postfix... so check to see if there's a dot in the prefix. |
| 136 size_t end_of_host_index = referrer_url_host.find_first_of('.'); | 170 size_t end_of_host_index = referrer_url_host.find_first_of('.'); |
| 137 if (referrer_url_host.npos != end_of_host_index && | 171 if (referrer_url_host.npos != end_of_host_index && |
| 138 end_of_host_index < postfix_domain_index) { | 172 end_of_host_index < postfix_domain_index) { |
| 139 SdchErrorRecovery(DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX); | 173 SdchErrorRecovery(DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX); |
| 140 return false; | 174 return false; |
| 141 } | 175 } |
| 142 } | 176 } |
| 143 | 177 |
| 144 if (!ports.empty() | 178 if (!ports.empty() |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 398 } | 432 } |
| 399 | 433 |
| 400 return true; | 434 return true; |
| 401 } | 435 } |
| 402 | 436 |
| 403 void SdchManager::GetVcdiffDictionary( | 437 void SdchManager::GetVcdiffDictionary( |
| 404 const std::string& server_hash, | 438 const std::string& server_hash, |
| 405 const GURL& referring_url, | 439 const GURL& referring_url, |
| 406 scoped_refptr<Dictionary>* dictionary) { | 440 scoped_refptr<Dictionary>* dictionary) { |
| 407 DCHECK(CalledOnValidThread()); | 441 DCHECK(CalledOnValidThread()); |
| 442 | |
| 443 GURL referring_url_hdn(referring_url); | |
| 444 StripTrailingDotURL(&referring_url_hdn); | |
| 445 | |
| 408 *dictionary = NULL; | 446 *dictionary = NULL; |
| 409 DictionaryMap::iterator it = dictionaries_.find(server_hash); | 447 DictionaryMap::iterator it = dictionaries_.find(server_hash); |
| 410 if (it == dictionaries_.end()) { | 448 if (it == dictionaries_.end()) { |
| 411 return; | 449 return; |
| 412 } | 450 } |
| 413 scoped_refptr<Dictionary> matching_dictionary = it->second; | 451 scoped_refptr<Dictionary> matching_dictionary = it->second; |
| 414 if (!IsInSupportedDomain(referring_url)) | 452 if (!IsInSupportedDomain(referring_url_hdn)) |
| 415 return; | 453 return; |
| 416 if (!matching_dictionary->CanUse(referring_url)) | 454 if (!matching_dictionary->CanUse(referring_url_hdn)) |
|
Ryan Sleevi
2014/09/22 20:30:37
OK, I'm fairly confused now why it's necessary to
Randy Smith (Not in Mondays)
2014/09/22 20:43:51
This isn't necessary; it's the checks in AddSdchDi
| |
| 417 return; | 455 return; |
| 418 *dictionary = matching_dictionary; | 456 *dictionary = matching_dictionary; |
| 419 } | 457 } |
| 420 | 458 |
| 421 // TODO(jar): If we have evictions from the dictionaries_, then we need to | 459 // TODO(jar): If we have evictions from the dictionaries_, then we need to |
| 422 // change this interface to return a list of reference counted Dictionary | 460 // change this interface to return a list of reference counted Dictionary |
| 423 // instances that can be used if/when a server specifies one. | 461 // instances that can be used if/when a server specifies one. |
| 424 void SdchManager::GetAvailDictionaryList(const GURL& target_url, | 462 void SdchManager::GetAvailDictionaryList(const GURL& target_url, |
| 425 std::string* list) { | 463 std::string* list) { |
| 426 DCHECK(CalledOnValidThread()); | 464 DCHECK(CalledOnValidThread()); |
| 465 | |
| 466 GURL target_url_hdn(target_url); | |
| 467 StripTrailingDotURL(&target_url_hdn); | |
| 468 | |
| 427 int count = 0; | 469 int count = 0; |
| 428 for (DictionaryMap::iterator it = dictionaries_.begin(); | 470 for (DictionaryMap::iterator it = dictionaries_.begin(); |
| 429 it != dictionaries_.end(); ++it) { | 471 it != dictionaries_.end(); ++it) { |
| 430 if (!IsInSupportedDomain(target_url)) | 472 if (!IsInSupportedDomain(target_url_hdn)) |
| 431 continue; | 473 continue; |
| 432 if (!it->second->CanAdvertise(target_url)) | 474 if (!it->second->CanAdvertise(target_url_hdn)) |
| 433 continue; | 475 continue; |
| 434 ++count; | 476 ++count; |
| 435 if (!list->empty()) | 477 if (!list->empty()) |
| 436 list->append(","); | 478 list->append(","); |
| 437 list->append(it->second->client_hash()); | 479 list->append(it->second->client_hash()); |
| 438 } | 480 } |
| 439 // Watch to see if we have corrupt or numerous dictionaries. | 481 // Watch to see if we have corrupt or numerous dictionaries. |
| 440 if (count > 0) | 482 if (count > 0) |
| 441 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); | 483 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); |
| 442 } | 484 } |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 471 allow_latency_experiment_.insert(url.host()); | 513 allow_latency_experiment_.insert(url.host()); |
| 472 return; | 514 return; |
| 473 } | 515 } |
| 474 ExperimentSet::iterator it = allow_latency_experiment_.find(url.host()); | 516 ExperimentSet::iterator it = allow_latency_experiment_.find(url.host()); |
| 475 if (allow_latency_experiment_.end() == it) | 517 if (allow_latency_experiment_.end() == it) |
| 476 return; // It was already erased, or never allowed. | 518 return; // It was already erased, or never allowed. |
| 477 SdchErrorRecovery(LATENCY_TEST_DISALLOWED); | 519 SdchErrorRecovery(LATENCY_TEST_DISALLOWED); |
| 478 allow_latency_experiment_.erase(it); | 520 allow_latency_experiment_.erase(it); |
| 479 } | 521 } |
| 480 | 522 |
| 481 void SdchManager::AddSdchDictionary(const std::string& dictionary_text, | 523 void SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
|
Ryan Sleevi
2014/09/22 20:55:26
style nits: Perhaps for a future CL, but this shou
| |
| 482 const GURL& dictionary_url) { | 524 const GURL& dictionary_url_const) { |
|
Ryan Sleevi
2014/09/22 20:55:26
pedantry naming: dictionary_url here, and normaliz
Randy Smith (Not in Mondays)
2014/09/23 19:52:58
Done.
| |
| 483 DCHECK(CalledOnValidThread()); | 525 DCHECK(CalledOnValidThread()); |
| 484 std::string client_hash; | 526 std::string client_hash; |
| 485 std::string server_hash; | 527 std::string server_hash; |
| 486 GenerateHash(dictionary_text, &client_hash, &server_hash); | 528 GenerateHash(dictionary_text, &client_hash, &server_hash); |
| 487 if (dictionaries_.find(server_hash) != dictionaries_.end()) { | 529 if (dictionaries_.find(server_hash) != dictionaries_.end()) { |
| 488 SdchErrorRecovery(DICTIONARY_ALREADY_LOADED); | 530 SdchErrorRecovery(DICTIONARY_ALREADY_LOADED); |
| 489 return; // Already loaded. | 531 return; // Already loaded. |
| 490 } | 532 } |
| 491 | 533 |
| 492 std::string domain, path; | 534 std::string domain, path; |
| 493 std::set<int> ports; | 535 std::set<int> ports; |
| 494 base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30)); | 536 base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30)); |
| 495 | 537 |
| 496 if (dictionary_text.empty()) { | 538 if (dictionary_text.empty()) { |
| 497 SdchErrorRecovery(DICTIONARY_HAS_NO_TEXT); | 539 SdchErrorRecovery(DICTIONARY_HAS_NO_TEXT); |
| 498 return; // Missing header. | 540 return; // Missing header. |
| 499 } | 541 } |
| 500 | 542 |
| 501 size_t header_end = dictionary_text.find("\n\n"); | 543 size_t header_end = dictionary_text.find("\n\n"); |
| 502 if (std::string::npos == header_end) { | 544 if (std::string::npos == header_end) { |
| 503 SdchErrorRecovery(DICTIONARY_HAS_NO_HEADER); | 545 SdchErrorRecovery(DICTIONARY_HAS_NO_HEADER); |
| 504 return; // Missing header. | 546 return; // Missing header. |
| 505 } | 547 } |
| 506 size_t line_start = 0; // Start of line being parsed. | 548 size_t line_start = 0; // Start of line being parsed. |
| 507 while (1) { | 549 while (1) { |
| 508 size_t line_end = dictionary_text.find('\n', line_start); | 550 size_t line_end = dictionary_text.find('\n', line_start); |
| 509 DCHECK(std::string::npos != line_end); | 551 DCHECK(std::string::npos != line_end); |
| 510 DCHECK_LE(line_end, header_end); | 552 DCHECK_LE(line_end, header_end); |
|
Ryan Sleevi
2014/09/22 20:55:26
This is really unnerving. Shouldn't these be non-C
| |
| 511 | 553 |
| 512 size_t colon_index = dictionary_text.find(':', line_start); | 554 size_t colon_index = dictionary_text.find(':', line_start); |
| 513 if (std::string::npos == colon_index) { | 555 if (std::string::npos == colon_index) { |
| 514 SdchErrorRecovery(DICTIONARY_HEADER_LINE_MISSING_COLON); | 556 SdchErrorRecovery(DICTIONARY_HEADER_LINE_MISSING_COLON); |
| 515 return; // Illegal line missing a colon. | 557 return; // Illegal line missing a colon. |
| 516 } | 558 } |
| 517 | 559 |
| 518 if (colon_index > line_end) | 560 if (colon_index > line_end) |
|
Ryan Sleevi
2014/09/22 20:55:26
if line_end == npos, this will always be false.
| |
| 519 break; | 561 break; |
| 520 | 562 |
| 521 size_t value_start = dictionary_text.find_first_not_of(" \t", | 563 size_t value_start = dictionary_text.find_first_not_of(" \t", |
| 522 colon_index + 1); | 564 colon_index + 1); |
| 523 if (std::string::npos != value_start) { | 565 if (std::string::npos != value_start) { |
| 524 if (value_start >= line_end) | 566 if (value_start >= line_end) |
| 525 break; | 567 break; |
| 526 std::string name(dictionary_text, line_start, colon_index - line_start); | 568 std::string name(dictionary_text, line_start, colon_index - line_start); |
| 527 std::string value(dictionary_text, value_start, line_end - value_start); | 569 std::string value(dictionary_text, value_start, line_end - value_start); |
| 528 name = base::StringToLowerASCII(name); | 570 name = base::StringToLowerASCII(name); |
|
Ryan Sleevi
2014/09/22 20:55:26
Such wasteful copying :'(
name == could be LowerC
| |
| 529 if (name == "domain") { | 571 if (name == "domain") { |
| 530 domain = value; | 572 domain = value; |
| 531 } else if (name == "path") { | 573 } else if (name == "path") { |
| 532 path = value; | 574 path = value; |
| 533 } else if (name == "format-version") { | 575 } else if (name == "format-version") { |
| 534 if (value != "1.0") | 576 if (value != "1.0") |
| 535 return; | 577 return; |
| 536 } else if (name == "max-age") { | 578 } else if (name == "max-age") { |
| 537 int64 seconds; | 579 int64 seconds; |
| 538 base::StringToInt64(value, &seconds); | 580 base::StringToInt64(value, &seconds); |
|
Ryan Sleevi
2014/09/22 20:55:26
Unchecked return value? Killing me smalls, KILLING
| |
| 539 expiration = base::Time::Now() + base::TimeDelta::FromSeconds(seconds); | 581 expiration = base::Time::Now() + base::TimeDelta::FromSeconds(seconds); |
| 540 } else if (name == "port") { | 582 } else if (name == "port") { |
| 541 int port; | 583 int port; |
| 542 base::StringToInt(value, &port); | 584 base::StringToInt(value, &port); |
|
Ryan Sleevi
2014/09/22 20:55:26
This too!
| |
| 543 if (port >= 0) | 585 if (port >= 0) |
| 544 ports.insert(port); | 586 ports.insert(port); |
| 545 } | 587 } |
| 546 } | 588 } |
| 547 | 589 |
| 548 if (line_end >= header_end) | 590 if (line_end >= header_end) |
| 549 break; | 591 break; |
| 550 line_start = line_end + 1; | 592 line_start = line_end + 1; |
|
Ryan Sleevi
2014/09/22 20:55:27
if line_end == npos, this will wrap around to 0 (s
| |
| 551 } | 593 } |
| 552 | 594 |
| 595 // Forbid dictionary with trailing dots; the SDCH spec is explicitly | |
| 596 // patterned after an old cookie spec (RFC 2695) which is silent on | |
| 597 // the issue of trailing dots, and the newer cookie spec (RFC 6265) | |
| 598 // explicitly forbids them. | |
| 599 if (domain.size() != 0 && domain[domain.size() - 1] == '.') { | |
| 600 SdchErrorRecovery(DICTIONARY_DOMAIN_HAS_TRAILING_PERIOD); | |
| 601 return; | |
|
Ryan Sleevi
2014/09/22 20:55:27
From your reply, it _seems_ that this is the ONLY
Randy Smith (Not in Mondays)
2014/09/22 21:09:56
I don't believe so; I think the stripping of the d
Ryan Sleevi
2014/09/22 22:49:42
I tried to work through why this is. That is, I'm
| |
| 602 } | |
| 603 | |
| 604 // Strip trailing dot from the dictionary url before comparison. | |
| 605 GURL dictionary_url(dictionary_url_const); | |
| 606 StripTrailingDotURL(&dictionary_url); | |
|
Randy Smith (Not in Mondays)
2014/09/23 17:36:43
>> I believe this is the only change required to s
| |
| 607 | |
| 553 if (!IsInSupportedDomain(dictionary_url)) | 608 if (!IsInSupportedDomain(dictionary_url)) |
| 554 return; | 609 return; |
| 555 | 610 |
| 556 if (!Dictionary::CanSet(domain, path, ports, dictionary_url)) | 611 if (!Dictionary::CanSet(domain, path, ports, dictionary_url)) |
| 557 return; | 612 return; |
| 558 | 613 |
| 559 // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of | 614 // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of |
| 560 // useless dictionaries. We should probably have a cache eviction plan, | 615 // useless dictionaries. We should probably have a cache eviction plan, |
| 561 // instead of just blocking additions. For now, with the spec in flux, it | 616 // instead of just blocking additions. For now, with the spec in flux, it |
| 562 // is probably not worth doing eviction handling. | 617 // is probably not worth doing eviction handling. |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 583 void SdchManager::UrlSafeBase64Encode(const std::string& input, | 638 void SdchManager::UrlSafeBase64Encode(const std::string& input, |
| 584 std::string* output) { | 639 std::string* output) { |
| 585 // Since this is only done during a dictionary load, and hashes are only 8 | 640 // Since this is only done during a dictionary load, and hashes are only 8 |
| 586 // characters, we just do the simple fixup, rather than rewriting the encoder. | 641 // characters, we just do the simple fixup, rather than rewriting the encoder. |
| 587 base::Base64Encode(input, output); | 642 base::Base64Encode(input, output); |
| 588 std::replace(output->begin(), output->end(), '+', '-'); | 643 std::replace(output->begin(), output->end(), '+', '-'); |
| 589 std::replace(output->begin(), output->end(), '/', '_'); | 644 std::replace(output->begin(), output->end(), '/', '_'); |
| 590 } | 645 } |
| 591 | 646 |
| 592 } // namespace net | 647 } // namespace net |
| OLD | NEW |