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 |