Index: net/base/sdch_manager.cc |
diff --git a/net/base/sdch_manager.cc b/net/base/sdch_manager.cc |
index 1285ad9383aa1784d92ebd3481c910f69454b091..af5e6ee3fa7424ff7e9990d9a667940f81907521 100644 |
--- a/net/base/sdch_manager.cc |
+++ b/net/base/sdch_manager.cc |
@@ -9,6 +9,7 @@ |
#include "base/metrics/histogram.h" |
#include "base/strings/string_number_conversions.h" |
#include "base/strings/string_util.h" |
+#include "base/values.h" |
#include "crypto/sha2.h" |
#include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
#include "net/base/sdch_observer.h" |
@@ -78,7 +79,8 @@ SdchManager::Dictionary::Dictionary(const std::string& dictionary_text, |
SdchManager::Dictionary::~Dictionary() { |
} |
-bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) { |
+SdchProblemCode SdchManager::Dictionary::CanAdvertise( |
+ const GURL& target_url) const { |
/* 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 |
@@ -95,28 +97,28 @@ bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) { |
url scheme. |
*/ |
if (!DomainMatch(target_url, domain_)) |
- return false; |
+ return SDCH_DICTIONARY_FOUND_HAS_WRONG_DOMAIN; |
if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort())) |
- return false; |
+ return SDCH_DICTIONARY_FOUND_HAS_WRONG_PORT_LIST; |
if (path_.size() && !PathMatch(target_url.path(), path_)) |
- return false; |
+ return SDCH_DICTIONARY_FOUND_HAS_WRONG_PATH; |
if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure()) |
- return false; |
+ return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME; |
if (target_url.SchemeIsSecure() != url_.SchemeIsSecure()) |
- return false; |
+ return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME; |
if (base::Time::Now() > expiration_) |
- return false; |
- return true; |
+ return SDCH_DICTIONARY_FOUND_EXPIRED; |
+ return SDCH_OK; |
} |
//------------------------------------------------------------------------------ |
// 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) { |
+SdchProblemCode SdchManager::Dictionary::CanSet(const std::string& domain, |
+ const std::string& path, |
+ const std::set<int>& ports, |
+ const GURL& dictionary_url) { |
/* |
A dictionary is invalid and must not be stored if any of the following are |
true: |
@@ -135,44 +137,38 @@ bool SdchManager::Dictionary::CanSet(const std::string& domain, |
// 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 (domain.empty()) |
+ return SDCH_DICTIONARY_MISSING_DOMAIN_SPECIFIER; // Domain is required. |
+ |
if (registry_controlled_domains::GetDomainAndRegistry( |
- domain, |
- registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES).empty()) { |
- 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; |
+ domain, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) |
+ .empty()) { |
+ return SDCH_DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN; // domain was a TLD. |
} |
+ if (!Dictionary::DomainMatch(dictionary_url, domain)) |
+ return SDCH_DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL; |
+ |
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 && |
+ 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; |
+ return SDCH_DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX; |
} |
} |
- if (!ports.empty() |
- && 0 == ports.count(dictionary_url.EffectiveIntPort())) { |
- SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL); |
- return false; |
- } |
- return true; |
+ if (!ports.empty() && 0 == ports.count(dictionary_url.EffectiveIntPort())) |
+ return SDCH_DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL; |
+ |
+ return SDCH_OK; |
} |
-// static |
-bool SdchManager::Dictionary::CanUse(const GURL& referring_url) { |
+SdchProblemCode SdchManager::Dictionary::CanUse( |
+ const GURL& referring_url) const { |
/* |
1. The request URL's host name domain-matches the Domain attribute of the |
dictionary. |
@@ -184,39 +180,30 @@ bool SdchManager::Dictionary::CanUse(const GURL& referring_url) { |
HTTPS support AND the dictionary acquisition scheme matches the target |
url scheme. |
*/ |
- 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 (!SdchManager::secure_scheme_supported() && |
- referring_url.SchemeIsSecure()) { |
- SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME); |
- return false; |
- } |
- if (referring_url.SchemeIsSecure() != url_.SchemeIsSecure()) { |
- SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME); |
- return false; |
- } |
+ if (!DomainMatch(referring_url, domain_)) |
+ return SDCH_DICTIONARY_FOUND_HAS_WRONG_DOMAIN; |
+ |
+ if (!ports_.empty() && 0 == ports_.count(referring_url.EffectiveIntPort())) |
+ return SDCH_DICTIONARY_FOUND_HAS_WRONG_PORT_LIST; |
+ |
+ if (path_.size() && !PathMatch(referring_url.path(), path_)) |
+ return SDCH_DICTIONARY_FOUND_HAS_WRONG_PATH; |
+ |
+ if (!SdchManager::secure_scheme_supported() && referring_url.SchemeIsSecure()) |
+ return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME; |
+ |
+ if (referring_url.SchemeIsSecure() != url_.SchemeIsSecure()) |
+ return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME; |
// TODO(jar): Remove overly restrictive failsafe test (added per security |
// review) when we have a need to be more general. |
- if (!referring_url.SchemeIsHTTPOrHTTPS()) { |
- SdchErrorRecovery(ATTEMPT_TO_DECODE_NON_HTTP_DATA); |
- return false; |
- } |
+ if (!referring_url.SchemeIsHTTPOrHTTPS()) |
+ return SDCH_ATTEMPT_TO_DECODE_NON_HTTP_DATA; |
- return true; |
+ return SDCH_OK; |
} |
+// static |
bool SdchManager::Dictionary::PathMatch(const std::string& path, |
const std::string& restriction) { |
/* Must be either: |
@@ -268,8 +255,9 @@ void SdchManager::ClearData() { |
} |
// static |
-void SdchManager::SdchErrorRecovery(ProblemCodes problem) { |
- UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE); |
+void SdchManager::SdchErrorRecovery(SdchProblemCode problem) { |
+ UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_5", problem, |
+ SDCH_MAX_PROBLEM_CODE); |
} |
// static |
@@ -283,7 +271,7 @@ void SdchManager::EnableSecureSchemeSupport(bool enabled) { |
} |
void SdchManager::BlacklistDomain(const GURL& url, |
- ProblemCodes blacklist_reason) { |
+ SdchProblemCode blacklist_reason) { |
SetAllowLatencyExperiment(url, false); |
BlacklistInfo* blacklist_info = |
@@ -304,7 +292,7 @@ void SdchManager::BlacklistDomain(const GURL& url, |
} |
void SdchManager::BlacklistDomainForever(const GURL& url, |
- ProblemCodes blacklist_reason) { |
+ SdchProblemCode blacklist_reason) { |
SetAllowLatencyExperiment(url, false); |
BlacklistInfo* blacklist_info = |
@@ -322,7 +310,7 @@ void SdchManager::ClearDomainBlacklisting(const std::string& domain) { |
BlacklistInfo* blacklist_info = &blacklisted_domains_[ |
base::StringToLowerASCII(domain)]; |
blacklist_info->count = 0; |
- blacklist_info->reason = MIN_PROBLEM_CODE; |
+ blacklist_info->reason = SDCH_OK; |
} |
int SdchManager::BlackListDomainCount(const std::string& domain) { |
@@ -341,49 +329,53 @@ int SdchManager::BlacklistDomainExponential(const std::string& domain) { |
return blacklisted_domains_[domain_lower].exponential_count; |
} |
-bool SdchManager::IsInSupportedDomain(const GURL& url) { |
+SdchProblemCode SdchManager::IsInSupportedDomain(const GURL& url) { |
DCHECK(thread_checker_.CalledOnValidThread()); |
if (!g_sdch_enabled_ ) |
- return false; |
+ return SDCH_DISABLED; |
if (!secure_scheme_supported() && url.SchemeIsSecure()) |
- return false; |
+ return SDCH_SECURE_SCHEME_NOT_SUPPORTED; |
if (blacklisted_domains_.empty()) |
- return true; |
+ return SDCH_OK; |
DomainBlacklistInfo::iterator it = |
blacklisted_domains_.find(base::StringToLowerASCII(url.host())); |
if (blacklisted_domains_.end() == it || it->second.count == 0) |
- return true; |
+ return SDCH_OK; |
UMA_HISTOGRAM_ENUMERATION("Sdch3.BlacklistReason", it->second.reason, |
- MAX_PROBLEM_CODE); |
- SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET); |
+ SDCH_MAX_PROBLEM_CODE); |
int count = it->second.count - 1; |
if (count > 0) { |
it->second.count = count; |
} else { |
it->second.count = 0; |
- it->second.reason = MIN_PROBLEM_CODE; |
+ it->second.reason = SDCH_OK; |
} |
- return false; |
+ return SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET; |
} |
-void SdchManager::OnGetDictionary(const GURL& request_url, |
- const GURL& dictionary_url) { |
- if (!CanFetchDictionary(request_url, dictionary_url)) |
- return; |
+SdchProblemCode SdchManager::OnGetDictionary(const GURL& request_url, |
+ const GURL& dictionary_url) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ SdchProblemCode rv = CanFetchDictionary(request_url, dictionary_url); |
+ if (rv != SDCH_OK) |
+ return rv; |
FOR_EACH_OBSERVER(SdchObserver, |
observers_, |
OnGetDictionary(this, request_url, dictionary_url)); |
+ |
+ return SDCH_OK; |
} |
-bool SdchManager::CanFetchDictionary(const GURL& referring_url, |
- const GURL& dictionary_url) const { |
+SdchProblemCode SdchManager::CanFetchDictionary( |
+ const GURL& referring_url, |
+ const GURL& dictionary_url) const { |
DCHECK(thread_checker_.CalledOnValidThread()); |
/* The user agent may retrieve a dictionary from the dictionary URL if all of |
the following are true: |
@@ -397,41 +389,40 @@ bool SdchManager::CanFetchDictionary(const GURL& referring_url, |
// Item (1) above implies item (2). Spec should be updated. |
// I take "host name match" to be "is identical to" |
if (referring_url.host() != dictionary_url.host() || |
- referring_url.scheme() != dictionary_url.scheme()) { |
- SdchErrorRecovery(DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST); |
- return false; |
- } |
- if (!secure_scheme_supported() && referring_url.SchemeIsSecure()) { |
- SdchErrorRecovery(DICTIONARY_SELECTED_FOR_SSL); |
- return false; |
- } |
+ referring_url.scheme() != dictionary_url.scheme()) |
+ return SDCH_DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST; |
+ |
+ if (!secure_scheme_supported() && referring_url.SchemeIsSecure()) |
+ return SDCH_DICTIONARY_SELECTED_FOR_SSL; |
// TODO(jar): Remove this failsafe conservative hack which is more restrictive |
// than current SDCH spec when needed, and justified by security audit. |
- if (!referring_url.SchemeIsHTTPOrHTTPS()) { |
- SdchErrorRecovery(DICTIONARY_SELECTED_FROM_NON_HTTP); |
- return false; |
- } |
+ if (!referring_url.SchemeIsHTTPOrHTTPS()) |
+ return SDCH_DICTIONARY_SELECTED_FROM_NON_HTTP; |
- return true; |
+ return SDCH_OK; |
} |
-void SdchManager::GetVcdiffDictionary( |
+SdchProblemCode SdchManager::GetVcdiffDictionary( |
const std::string& server_hash, |
const GURL& referring_url, |
scoped_refptr<Dictionary>* dictionary) { |
DCHECK(thread_checker_.CalledOnValidThread()); |
*dictionary = NULL; |
DictionaryMap::iterator it = dictionaries_.find(server_hash); |
- if (it == dictionaries_.end()) { |
- return; |
- } |
+ if (it == dictionaries_.end()) |
+ return SDCH_DICTIONARY_HASH_NOT_FOUND; |
+ |
scoped_refptr<Dictionary> matching_dictionary = it->second; |
- if (!IsInSupportedDomain(referring_url)) |
- return; |
- if (!matching_dictionary->CanUse(referring_url)) |
- return; |
- *dictionary = matching_dictionary; |
+ |
+ SdchProblemCode rv = IsInSupportedDomain(referring_url); |
+ if (rv != SDCH_OK) |
+ return rv; |
+ |
+ rv = matching_dictionary->CanUse(referring_url); |
+ if (rv == SDCH_OK) |
+ *dictionary = matching_dictionary; |
+ return rv; |
} |
// TODO(jar): If we have evictions from the dictionaries_, then we need to |
@@ -443,9 +434,11 @@ void SdchManager::GetAvailDictionaryList(const GURL& target_url, |
int count = 0; |
for (DictionaryMap::iterator it = dictionaries_.begin(); |
it != dictionaries_.end(); ++it) { |
- if (!IsInSupportedDomain(target_url)) |
+ SdchProblemCode rv = IsInSupportedDomain(target_url); |
+ if (rv != SDCH_OK) |
continue; |
- if (!it->second->CanAdvertise(target_url)) |
+ |
+ if (it->second->CanAdvertise(target_url) != SDCH_OK) |
continue; |
++count; |
if (!list->empty()) |
@@ -490,7 +483,7 @@ void SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) { |
ExperimentSet::iterator it = allow_latency_experiment_.find(url.host()); |
if (allow_latency_experiment_.end() == it) |
return; // It was already erased, or never allowed. |
- SdchErrorRecovery(LATENCY_TEST_DISALLOWED); |
+ SdchErrorRecovery(SDCH_LATENCY_TEST_DISALLOWED); |
allow_latency_experiment_.erase(it); |
} |
@@ -502,31 +495,27 @@ void SdchManager::RemoveObserver(SdchObserver* observer) { |
observers_.RemoveObserver(observer); |
} |
-void SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
+SdchProblemCode SdchManager::AddSdchDictionary( |
+ const std::string& dictionary_text, |
const GURL& dictionary_url) { |
DCHECK(thread_checker_.CalledOnValidThread()); |
std::string client_hash; |
std::string server_hash; |
GenerateHash(dictionary_text, &client_hash, &server_hash); |
- if (dictionaries_.find(server_hash) != dictionaries_.end()) { |
- SdchErrorRecovery(DICTIONARY_ALREADY_LOADED); |
- return; // Already loaded. |
- } |
+ if (dictionaries_.find(server_hash) != dictionaries_.end()) |
+ return SDCH_DICTIONARY_ALREADY_LOADED; // Already loaded. |
std::string domain, path; |
std::set<int> ports; |
base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30)); |
- if (dictionary_text.empty()) { |
- SdchErrorRecovery(DICTIONARY_HAS_NO_TEXT); |
- return; // Missing header. |
- } |
+ if (dictionary_text.empty()) |
+ return SDCH_DICTIONARY_HAS_NO_TEXT; // Missing header. |
size_t header_end = dictionary_text.find("\n\n"); |
- if (std::string::npos == header_end) { |
- SdchErrorRecovery(DICTIONARY_HAS_NO_HEADER); |
- return; // Missing header. |
- } |
+ if (std::string::npos == header_end) |
+ return SDCH_DICTIONARY_HAS_NO_HEADER; // Missing header. |
+ |
size_t line_start = 0; // Start of line being parsed. |
while (1) { |
size_t line_end = dictionary_text.find('\n', line_start); |
@@ -534,10 +523,9 @@ void SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
DCHECK_LE(line_end, header_end); |
size_t colon_index = dictionary_text.find(':', line_start); |
- if (std::string::npos == colon_index) { |
- SdchErrorRecovery(DICTIONARY_HEADER_LINE_MISSING_COLON); |
- return; // Illegal line missing a colon. |
- } |
+ if (std::string::npos == colon_index) |
+ return SDCH_DICTIONARY_HEADER_LINE_MISSING_COLON; // Illegal line missing |
+ // a colon. |
if (colon_index > line_end) |
break; |
@@ -556,7 +544,7 @@ void SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
path = value; |
} else if (name == "format-version") { |
if (value != "1.0") |
- return; |
+ return SDCH_DICTIONARY_UNSUPPORTED_VERSION; |
} else if (name == "max-age") { |
int64 seconds; |
base::StringToInt64(value, &seconds); |
@@ -578,24 +566,23 @@ void SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
GURL dictionary_url_normalized(dictionary_url); |
StripTrailingDot(&dictionary_url_normalized); |
- if (!IsInSupportedDomain(dictionary_url_normalized)) |
- return; |
+ SdchProblemCode rv = IsInSupportedDomain(dictionary_url_normalized); |
+ if (rv != SDCH_OK) |
+ return rv; |
- if (!Dictionary::CanSet(domain, path, ports, dictionary_url_normalized)) |
- return; |
+ rv = Dictionary::CanSet(domain, path, ports, dictionary_url_normalized); |
+ if (rv != SDCH_OK) |
+ return rv; |
// TODO(jar): Remove these hacks to preclude a DOS attack involving piles of |
// useless dictionaries. We should probably have a cache eviction plan, |
// instead of just blocking additions. For now, with the spec in flux, it |
// is probably not worth doing eviction handling. |
- if (kMaxDictionarySize < dictionary_text.size()) { |
- SdchErrorRecovery(DICTIONARY_IS_TOO_LARGE); |
- return; |
- } |
- if (kMaxDictionaryCount <= dictionaries_.size()) { |
- SdchErrorRecovery(DICTIONARY_COUNT_EXCEEDED); |
- return; |
- } |
+ if (kMaxDictionarySize < dictionary_text.size()) |
+ return SDCH_DICTIONARY_IS_TOO_LARGE; |
+ |
+ if (kMaxDictionaryCount <= dictionaries_.size()) |
+ return SDCH_DICTIONARY_COUNT_EXCEEDED; |
UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size()); |
DVLOG(1) << "Loaded dictionary with client hash " << client_hash |
@@ -605,7 +592,7 @@ void SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
dictionary_url_normalized, domain, |
path, expiration, ports); |
dictionaries_[server_hash] = dictionary; |
- return; |
+ return SDCH_OK; |
} |
// static |
@@ -618,4 +605,46 @@ void SdchManager::UrlSafeBase64Encode(const std::string& input, |
std::replace(output->begin(), output->end(), '/', '_'); |
} |
+base::Value* SdchManager::SdchInfoToValue() const { |
+ base::DictionaryValue* value = new base::DictionaryValue(); |
+ |
+ value->SetBoolean("sdch_enabled", sdch_enabled()); |
+ value->SetBoolean("secure_scheme_support", secure_scheme_supported()); |
+ |
+ base::ListValue* entry_list = new base::ListValue(); |
+ for (DictionaryMap::const_iterator it = dictionaries_.begin(); |
+ it != dictionaries_.end(); ++it) { |
+ base::DictionaryValue* entry_dict = new base::DictionaryValue(); |
+ entry_dict->SetString("url", it->second->url().spec()); |
+ entry_dict->SetString("client_hash", it->second->client_hash()); |
+ entry_dict->SetString("domain", it->second->domain()); |
+ entry_dict->SetString("path", it->second->path()); |
+ base::ListValue* port_list = new base::ListValue(); |
+ for (std::set<int>::const_iterator port_it = it->second->ports().begin(); |
+ port_it != it->second->ports().end(); ++port_it) { |
+ port_list->AppendInteger(*port_it); |
+ } |
+ entry_dict->Set("ports", port_list); |
+ entry_dict->SetString("server_hash", it->first); |
+ entry_list->Append(entry_dict); |
+ } |
+ value->Set("dictionaries", entry_list); |
+ |
+ entry_list = new base::ListValue(); |
+ for (DomainBlacklistInfo::const_iterator it = blacklisted_domains_.begin(); |
+ it != blacklisted_domains_.end(); ++it) { |
+ if (it->second.count == 0) |
+ continue; |
+ base::DictionaryValue* entry_dict = new base::DictionaryValue(); |
+ entry_dict->SetString("domain", it->first); |
+ if (it->second.count != INT_MAX) |
+ entry_dict->SetInteger("tries", it->second.count); |
+ entry_dict->SetInteger("reason", it->second.reason); |
+ entry_list->Append(entry_dict); |
+ } |
+ value->Set("blacklisted", entry_list); |
+ |
+ return value; |
+} |
+ |
} // namespace net |