| Index: net/base/sdch_manager.cc
|
| diff --git a/net/base/sdch_manager.cc b/net/base/sdch_manager.cc
|
| index 5aca923545241c7b8bf276c2fb0eb4ebe6c71560..18976c9e22e838204ce0f2af9c44db8c7fdf90f7 100644
|
| --- a/net/base/sdch_manager.cc
|
| +++ b/net/base/sdch_manager.cc
|
| @@ -26,41 +26,176 @@ const size_t SdchManager::kMaxDictionaryCount = 20;
|
| // static
|
| SdchManager* SdchManager::global_;
|
|
|
| -// static
|
| -SdchManager* SdchManager::Global() {
|
| - return global_;
|
| +//------------------------------------------------------------------------------
|
| +SdchManager::Dictionary::Dictionary(const std::string& dictionary_text,
|
| + size_t offset, const std::string& client_hash, const GURL& gurl,
|
| + const std::string& domain, const std::string& path, const Time& expiration,
|
| + const std::set<int> ports)
|
| + : text_(dictionary_text, offset),
|
| + client_hash_(client_hash),
|
| + url_(gurl),
|
| + domain_(domain),
|
| + path_(path),
|
| + expiration_(expiration),
|
| + ports_(ports) {
|
| }
|
|
|
| -// static
|
| -void SdchManager::SdchErrorRecovery(ProblemCodes problem) {
|
| - UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE);
|
| +SdchManager::Dictionary::~Dictionary() {
|
| }
|
|
|
| -// static
|
| -void SdchManager::ClearBlacklistings() {
|
| - Global()->blacklisted_domains_.clear();
|
| - Global()->exponential_blacklist_count.clear();
|
| +bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) {
|
| + if (!SdchManager::Global()->IsInSupportedDomain(target_url))
|
| + return false;
|
| + /* The specific rules of when a dictionary should be advertised in an
|
| + Avail-Dictionary header are modeled after the rules for cookie scoping. The
|
| + terms "domain-match" and "pathmatch" are defined in RFC 2965 [6]. A
|
| + dictionary may be advertised in the Avail-Dictionaries header exactly when
|
| + all of the following are true:
|
| + 1. The server's effective host name domain-matches the Domain attribute of
|
| + the dictionary.
|
| + 2. If the dictionary has a Port attribute, the request port is one of the
|
| + ports listed in the Port attribute.
|
| + 3. The request URI path-matches the path header of the dictionary.
|
| + 4. The request is not an HTTPS request.
|
| + */
|
| + if (!DomainMatch(target_url, domain_))
|
| + return false;
|
| + if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort()))
|
| + return false;
|
| + if (path_.size() && !PathMatch(target_url.path(), path_))
|
| + return false;
|
| + if (target_url.SchemeIsSecure())
|
| + return false;
|
| + if (Time::Now() > expiration_)
|
| + return false;
|
| + return true;
|
| }
|
|
|
| +//------------------------------------------------------------------------------
|
| +// Security functions restricting loads and use of dictionaries.
|
| +
|
| // static
|
| -void SdchManager::ClearDomainBlacklisting(const std::string& domain) {
|
| - Global()->blacklisted_domains_.erase(StringToLowerASCII(domain));
|
| +bool SdchManager::Dictionary::CanSet(const std::string& domain,
|
| + const std::string& path,
|
| + const std::set<int> ports,
|
| + const GURL& dictionary_url) {
|
| + if (!SdchManager::Global()->IsInSupportedDomain(dictionary_url))
|
| + return false;
|
| + /*
|
| + A dictionary is invalid and must not be stored if any of the following are
|
| + true:
|
| + 1. The dictionary has no Domain attribute.
|
| + 2. The effective host name that derives from the referer URL host name does
|
| + not domain-match the Domain attribute.
|
| + 3. The Domain attribute is a top level domain.
|
| + 4. The referer URL host is a host domain name (not IP address) and has the
|
| + form HD, where D is the value of the Domain attribute, and H is a string
|
| + that contains one or more dots.
|
| + 5. If the dictionary has a Port attribute and the referer URL's port was not
|
| + in the list.
|
| + */
|
| +
|
| + // TODO(jar): Redirects in dictionary fetches might plausibly be problematic,
|
| + // and hence the conservative approach is to not allow any redirects (if there
|
| + // were any... then don't allow the dictionary to be set).
|
| +
|
| + if (domain.empty()) {
|
| + SdchErrorRecovery(DICTIONARY_MISSING_DOMAIN_SPECIFIER);
|
| + return false; // Domain is required.
|
| + }
|
| + if (net::RegistryControlledDomainService::GetDomainAndRegistry(domain).size()
|
| + == 0) {
|
| + SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN);
|
| + return false; // domain was a TLD.
|
| + }
|
| + if (!Dictionary::DomainMatch(dictionary_url, domain)) {
|
| + SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL);
|
| + return false;
|
| + }
|
| +
|
| + std::string referrer_url_host = dictionary_url.host();
|
| + size_t postfix_domain_index = referrer_url_host.rfind(domain);
|
| + // See if it is indeed a postfix, or just an internal string.
|
| + if (referrer_url_host.size() == postfix_domain_index + domain.size()) {
|
| + // It is a postfix... so check to see if there's a dot in the prefix.
|
| + size_t end_of_host_index = referrer_url_host.find_first_of('.');
|
| + if (referrer_url_host.npos != end_of_host_index &&
|
| + end_of_host_index < postfix_domain_index) {
|
| + SdchErrorRecovery(DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX);
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + if (!ports.empty()
|
| + && 0 == ports.count(dictionary_url.EffectiveIntPort())) {
|
| + SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL);
|
| + return false;
|
| + }
|
| + return true;
|
| }
|
|
|
| // static
|
| -int SdchManager::BlackListDomainCount(const std::string& domain) {
|
| - if (Global()->blacklisted_domains_.end() ==
|
| - Global()->blacklisted_domains_.find(domain))
|
| - return 0;
|
| - return Global()->blacklisted_domains_[StringToLowerASCII(domain)];
|
| +bool SdchManager::Dictionary::CanUse(const GURL& referring_url) {
|
| + if (!SdchManager::Global()->IsInSupportedDomain(referring_url))
|
| + return false;
|
| + /*
|
| + 1. The request URL's host name domain-matches the Domain attribute of the
|
| + dictionary.
|
| + 2. If the dictionary has a Port attribute, the request port is one of the
|
| + ports listed in the Port attribute.
|
| + 3. The request URL path-matches the path attribute of the dictionary.
|
| + 4. The request is not an HTTPS request.
|
| +*/
|
| + if (!DomainMatch(referring_url, domain_)) {
|
| + SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_DOMAIN);
|
| + return false;
|
| + }
|
| + if (!ports_.empty()
|
| + && 0 == ports_.count(referring_url.EffectiveIntPort())) {
|
| + SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PORT_LIST);
|
| + return false;
|
| + }
|
| + if (path_.size() && !PathMatch(referring_url.path(), path_)) {
|
| + SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PATH);
|
| + return false;
|
| + }
|
| + if (referring_url.SchemeIsSecure()) {
|
| + SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME);
|
| + return false;
|
| + }
|
| +
|
| + // TODO(jar): Remove overly restrictive failsafe test (added per security
|
| + // review) when we have a need to be more general.
|
| + if (!referring_url.SchemeIs("http")) {
|
| + SdchErrorRecovery(ATTEMPT_TO_DECODE_NON_HTTP_DATA);
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool SdchManager::Dictionary::PathMatch(const std::string& path,
|
| + const std::string& restriction) {
|
| + /* Must be either:
|
| + 1. P2 is equal to P1
|
| + 2. P2 is a prefix of P1 and either the final character in P2 is "/" or the
|
| + character following P2 in P1 is "/".
|
| + */
|
| + if (path == restriction)
|
| + return true;
|
| + size_t prefix_length = restriction.size();
|
| + if (prefix_length > path.size())
|
| + return false; // Can't be a prefix.
|
| + if (0 != path.compare(0, prefix_length, restriction))
|
| + return false;
|
| + return restriction[prefix_length - 1] == '/' || path[prefix_length] == '/';
|
| }
|
|
|
| // static
|
| -int SdchManager::BlacklistDomainExponential(const std::string& domain) {
|
| - if (Global()->exponential_blacklist_count.end() ==
|
| - Global()->exponential_blacklist_count.find(domain))
|
| - return 0;
|
| - return Global()->exponential_blacklist_count[StringToLowerASCII(domain)];
|
| +bool SdchManager::Dictionary::DomainMatch(const GURL& gurl,
|
| + const std::string& restriction) {
|
| + // TODO(jar): This is not precisely a domain match definition.
|
| + return gurl.DomainIs(restriction.data(), restriction.size());
|
| }
|
|
|
| //------------------------------------------------------------------------------
|
| @@ -87,6 +222,22 @@ void SdchManager::Shutdown() {
|
| }
|
|
|
| // static
|
| +SdchManager* SdchManager::Global() {
|
| + return global_;
|
| +}
|
| +
|
| +// static
|
| +void SdchManager::SdchErrorRecovery(ProblemCodes problem) {
|
| + UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE);
|
| +}
|
| +
|
| +void SdchManager::EnableSdchSupport(const std::string& domain) {
|
| + // We presume that there is a SDCH manager instance.
|
| + global_->supported_domain_ = domain;
|
| + global_->sdch_enabled_ = true;
|
| +}
|
| +
|
| +// static
|
| void SdchManager::BlacklistDomain(const GURL& url) {
|
| if (!global_ )
|
| return;
|
| @@ -117,10 +268,31 @@ void SdchManager::BlacklistDomainForever(const GURL& url) {
|
| global_->blacklisted_domains_[domain] = INT_MAX;
|
| }
|
|
|
| -void SdchManager::EnableSdchSupport(const std::string& domain) {
|
| - // We presume that there is a SDCH manager instance.
|
| - global_->supported_domain_ = domain;
|
| - global_->sdch_enabled_ = true;
|
| +// static
|
| +void SdchManager::ClearBlacklistings() {
|
| + Global()->blacklisted_domains_.clear();
|
| + Global()->exponential_blacklist_count.clear();
|
| +}
|
| +
|
| +// static
|
| +void SdchManager::ClearDomainBlacklisting(const std::string& domain) {
|
| + Global()->blacklisted_domains_.erase(StringToLowerASCII(domain));
|
| +}
|
| +
|
| +// static
|
| +int SdchManager::BlackListDomainCount(const std::string& domain) {
|
| + if (Global()->blacklisted_domains_.end() ==
|
| + Global()->blacklisted_domains_.find(domain))
|
| + return 0;
|
| + return Global()->blacklisted_domains_[StringToLowerASCII(domain)];
|
| +}
|
| +
|
| +// static
|
| +int SdchManager::BlacklistDomainExponential(const std::string& domain) {
|
| + if (Global()->exponential_blacklist_count.end() ==
|
| + Global()->exponential_blacklist_count.find(domain))
|
| + return 0;
|
| + return Global()->exponential_blacklist_count[StringToLowerASCII(domain)];
|
| }
|
|
|
| bool SdchManager::IsInSupportedDomain(const GURL& url) {
|
| @@ -147,6 +319,13 @@ bool SdchManager::IsInSupportedDomain(const GURL& url) {
|
| return false;
|
| }
|
|
|
| +void SdchManager::FetchDictionary(const GURL& request_url,
|
| + const GURL& dictionary_url) {
|
| + if (SdchManager::Global()->CanFetchDictionary(request_url, dictionary_url) &&
|
| + fetcher_.get())
|
| + fetcher_->Schedule(dictionary_url);
|
| +}
|
| +
|
| bool SdchManager::CanFetchDictionary(const GURL& referring_url,
|
| const GURL& dictionary_url) const {
|
| /* The user agent may retrieve a dictionary from the dictionary URL if all of
|
| @@ -179,13 +358,6 @@ bool SdchManager::CanFetchDictionary(const GURL& referring_url,
|
| return true;
|
| }
|
|
|
| -void SdchManager::FetchDictionary(const GURL& request_url,
|
| - const GURL& dictionary_url) {
|
| - if (SdchManager::Global()->CanFetchDictionary(request_url, dictionary_url) &&
|
| - fetcher_.get())
|
| - fetcher_->Schedule(dictionary_url);
|
| -}
|
| -
|
| bool SdchManager::AddSdchDictionary(const std::string& dictionary_text,
|
| const GURL& dictionary_url) {
|
| std::string client_hash;
|
| @@ -317,22 +489,6 @@ void SdchManager::GetAvailDictionaryList(const GURL& target_url,
|
| UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count);
|
| }
|
|
|
| -SdchManager::Dictionary::Dictionary(const std::string& dictionary_text,
|
| - size_t offset, const std::string& client_hash, const GURL& gurl,
|
| - const std::string& domain, const std::string& path, const Time& expiration,
|
| - const std::set<int> ports)
|
| - : text_(dictionary_text, offset),
|
| - client_hash_(client_hash),
|
| - url_(gurl),
|
| - domain_(domain),
|
| - path_(path),
|
| - expiration_(expiration),
|
| - ports_(ports) {
|
| -}
|
| -
|
| -SdchManager::Dictionary::~Dictionary() {
|
| -}
|
| -
|
| // static
|
| void SdchManager::GenerateHash(const std::string& dictionary_text,
|
| std::string* client_hash, std::string* server_hash) {
|
| @@ -348,181 +504,6 @@ void SdchManager::GenerateHash(const std::string& dictionary_text,
|
| DCHECK_EQ(client_hash->length(), 8u);
|
| }
|
|
|
| -// static
|
| -void SdchManager::UrlSafeBase64Encode(const std::string& input,
|
| - std::string* output) {
|
| - // Since this is only done during a dictionary load, and hashes are only 8
|
| - // characters, we just do the simple fixup, rather than rewriting the encoder.
|
| - base::Base64Encode(input, output);
|
| - for (size_t i = 0; i < output->size(); ++i) {
|
| - switch (output->data()[i]) {
|
| - case '+':
|
| - (*output)[i] = '-';
|
| - continue;
|
| - case '/':
|
| - (*output)[i] = '_';
|
| - continue;
|
| - default:
|
| - continue;
|
| - }
|
| - }
|
| -}
|
| -
|
| -//------------------------------------------------------------------------------
|
| -// Security functions restricting loads and use of dictionaries.
|
| -
|
| -// static
|
| -bool SdchManager::Dictionary::CanSet(const std::string& domain,
|
| - const std::string& path,
|
| - const std::set<int> ports,
|
| - const GURL& dictionary_url) {
|
| - if (!SdchManager::Global()->IsInSupportedDomain(dictionary_url))
|
| - return false;
|
| - /*
|
| - A dictionary is invalid and must not be stored if any of the following are
|
| - true:
|
| - 1. The dictionary has no Domain attribute.
|
| - 2. The effective host name that derives from the referer URL host name does
|
| - not domain-match the Domain attribute.
|
| - 3. The Domain attribute is a top level domain.
|
| - 4. The referer URL host is a host domain name (not IP address) and has the
|
| - form HD, where D is the value of the Domain attribute, and H is a string
|
| - that contains one or more dots.
|
| - 5. If the dictionary has a Port attribute and the referer URL's port was not
|
| - in the list.
|
| - */
|
| -
|
| - // TODO(jar): Redirects in dictionary fetches might plausibly be problematic,
|
| - // and hence the conservative approach is to not allow any redirects (if there
|
| - // were any... then don't allow the dictionary to be set).
|
| -
|
| - if (domain.empty()) {
|
| - SdchErrorRecovery(DICTIONARY_MISSING_DOMAIN_SPECIFIER);
|
| - return false; // Domain is required.
|
| - }
|
| - if (net::RegistryControlledDomainService::GetDomainAndRegistry(domain).size()
|
| - == 0) {
|
| - SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN);
|
| - return false; // domain was a TLD.
|
| - }
|
| - if (!Dictionary::DomainMatch(dictionary_url, domain)) {
|
| - SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL);
|
| - return false;
|
| - }
|
| -
|
| - std::string referrer_url_host = dictionary_url.host();
|
| - size_t postfix_domain_index = referrer_url_host.rfind(domain);
|
| - // See if it is indeed a postfix, or just an internal string.
|
| - if (referrer_url_host.size() == postfix_domain_index + domain.size()) {
|
| - // It is a postfix... so check to see if there's a dot in the prefix.
|
| - size_t end_of_host_index = referrer_url_host.find_first_of('.');
|
| - if (referrer_url_host.npos != end_of_host_index &&
|
| - end_of_host_index < postfix_domain_index) {
|
| - SdchErrorRecovery(DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX);
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - if (!ports.empty()
|
| - && 0 == ports.count(dictionary_url.EffectiveIntPort())) {
|
| - SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL);
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -// static
|
| -bool SdchManager::Dictionary::CanUse(const GURL& referring_url) {
|
| - if (!SdchManager::Global()->IsInSupportedDomain(referring_url))
|
| - return false;
|
| - /*
|
| - 1. The request URL's host name domain-matches the Domain attribute of the
|
| - dictionary.
|
| - 2. If the dictionary has a Port attribute, the request port is one of the
|
| - ports listed in the Port attribute.
|
| - 3. The request URL path-matches the path attribute of the dictionary.
|
| - 4. The request is not an HTTPS request.
|
| -*/
|
| - if (!DomainMatch(referring_url, domain_)) {
|
| - SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_DOMAIN);
|
| - return false;
|
| - }
|
| - if (!ports_.empty()
|
| - && 0 == ports_.count(referring_url.EffectiveIntPort())) {
|
| - SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PORT_LIST);
|
| - return false;
|
| - }
|
| - if (path_.size() && !PathMatch(referring_url.path(), path_)) {
|
| - SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PATH);
|
| - return false;
|
| - }
|
| - if (referring_url.SchemeIsSecure()) {
|
| - SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME);
|
| - return false;
|
| - }
|
| -
|
| - // TODO(jar): Remove overly restrictive failsafe test (added per security
|
| - // review) when we have a need to be more general.
|
| - if (!referring_url.SchemeIs("http")) {
|
| - SdchErrorRecovery(ATTEMPT_TO_DECODE_NON_HTTP_DATA);
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) {
|
| - if (!SdchManager::Global()->IsInSupportedDomain(target_url))
|
| - return false;
|
| - /* The specific rules of when a dictionary should be advertised in an
|
| - Avail-Dictionary header are modeled after the rules for cookie scoping. The
|
| - terms "domain-match" and "pathmatch" are defined in RFC 2965 [6]. A
|
| - dictionary may be advertised in the Avail-Dictionaries header exactly when
|
| - all of the following are true:
|
| - 1. The server's effective host name domain-matches the Domain attribute of
|
| - the dictionary.
|
| - 2. If the dictionary has a Port attribute, the request port is one of the
|
| - ports listed in the Port attribute.
|
| - 3. The request URI path-matches the path header of the dictionary.
|
| - 4. The request is not an HTTPS request.
|
| - */
|
| - if (!DomainMatch(target_url, domain_))
|
| - return false;
|
| - if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort()))
|
| - return false;
|
| - if (path_.size() && !PathMatch(target_url.path(), path_))
|
| - return false;
|
| - if (target_url.SchemeIsSecure())
|
| - return false;
|
| - if (Time::Now() > expiration_)
|
| - return false;
|
| - return true;
|
| -}
|
| -
|
| -bool SdchManager::Dictionary::PathMatch(const std::string& path,
|
| - const std::string& restriction) {
|
| - /* Must be either:
|
| - 1. P2 is equal to P1
|
| - 2. P2 is a prefix of P1 and either the final character in P2 is "/" or the
|
| - character following P2 in P1 is "/".
|
| - */
|
| - if (path == restriction)
|
| - return true;
|
| - size_t prefix_length = restriction.size();
|
| - if (prefix_length > path.size())
|
| - return false; // Can't be a prefix.
|
| - if (0 != path.compare(0, prefix_length, restriction))
|
| - return false;
|
| - return restriction[prefix_length - 1] == '/' || path[prefix_length] == '/';
|
| -}
|
| -
|
| -// static
|
| -bool SdchManager::Dictionary::DomainMatch(const GURL& gurl,
|
| - const std::string& restriction) {
|
| - // TODO(jar): This is not precisely a domain match definition.
|
| - return gurl.DomainIs(restriction.data(), restriction.size());
|
| -}
|
| -
|
| //------------------------------------------------------------------------------
|
| // Methods for supporting latency experiments.
|
|
|
| @@ -542,3 +523,23 @@ void SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) {
|
| SdchErrorRecovery(LATENCY_TEST_DISALLOWED);
|
| allow_latency_experiment_.erase(it);
|
| }
|
| +
|
| +// static
|
| +void SdchManager::UrlSafeBase64Encode(const std::string& input,
|
| + std::string* output) {
|
| + // Since this is only done during a dictionary load, and hashes are only 8
|
| + // characters, we just do the simple fixup, rather than rewriting the encoder.
|
| + base::Base64Encode(input, output);
|
| + for (size_t i = 0; i < output->size(); ++i) {
|
| + switch (output->data()[i]) {
|
| + case '+':
|
| + (*output)[i] = '-';
|
| + continue;
|
| + case '/':
|
| + (*output)[i] = '_';
|
| + continue;
|
| + default:
|
| + continue;
|
| + }
|
| + }
|
| +}
|
|
|