Index: net/base/sdch_manager.cc |
diff --git a/net/base/sdch_manager.cc b/net/base/sdch_manager.cc |
index fee75e58f2fd11170213447a1fc911547e261ab4..42b5cd19e4fc655aa7226d08b649796961a632fe 100644 |
--- a/net/base/sdch_manager.cc |
+++ b/net/base/sdch_manager.cc |
@@ -9,12 +9,22 @@ |
#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/url_request/url_request_http_job.h" |
namespace net { |
+namespace { |
+ |
+bool IsDictionaryProblem(SdchManager::ProblemCodes code) { |
+ return code >= SdchManager::DICTIONARY_HAS_NO_HEADER && |
+ code <= SdchManager::DICTIONARY_ALREADY_TRIED_TO_DOWNLOAD; |
+} |
+ |
+} |
+ |
//------------------------------------------------------------------------------ |
// static |
@@ -87,6 +97,24 @@ bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) { |
return true; |
} |
+base::DictionaryValue* |
+SdchManager::Dictionary::DictionaryInfoToValue() const { |
+ base::DictionaryValue* value = new base::DictionaryValue(); |
+ value->SetString("url", url_.spec()); |
+ value->SetString("client_hash", client_hash_); |
+ value->SetString("domain", domain_); |
+ value->SetString("path", path_); |
+ value->SetString("expiration", |
+ base::Int64ToString(expiration_.ToInternalValue())); |
+ base::ListValue* port_list = new base::ListValue(); |
+ for (std::set<int>::const_iterator it = ports_.begin(); |
+ it != ports_.end(); ++it) { |
+ port_list->AppendInteger(*it); |
+ } |
+ value->Set("ports", port_list); |
+ return value; |
+} |
+ |
//------------------------------------------------------------------------------ |
// Security functions restricting loads and use of dictionaries. |
@@ -94,7 +122,8 @@ bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) { |
bool SdchManager::Dictionary::CanSet(const std::string& domain, |
const std::string& path, |
const std::set<int>& ports, |
- const GURL& dictionary_url) { |
+ const GURL& dictionary_url, |
+ SdchManager* sdch_manager) { |
/* |
A dictionary is invalid and must not be stored if any of the following are |
true: |
@@ -114,17 +143,20 @@ bool SdchManager::Dictionary::CanSet(const std::string& domain, |
// were any... then don't allow the dictionary to be set). |
if (domain.empty()) { |
- SdchErrorRecovery(DICTIONARY_MISSING_DOMAIN_SPECIFIER); |
+ sdch_manager->SdchErrorRecovery( |
+ DICTIONARY_MISSING_DOMAIN_SPECIFIER, dictionary_url); |
return false; // Domain is required. |
} |
if (registry_controlled_domains::GetDomainAndRegistry( |
domain, |
registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES).empty()) { |
- SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN); |
+ sdch_manager->SdchErrorRecovery( |
+ DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN, dictionary_url); |
return false; // domain was a TLD. |
} |
if (!Dictionary::DomainMatch(dictionary_url, domain)) { |
- SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL); |
+ sdch_manager->SdchErrorRecovery( |
+ DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL, dictionary_url); |
return false; |
} |
@@ -136,21 +168,23 @@ bool SdchManager::Dictionary::CanSet(const std::string& domain, |
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); |
+ sdch_manager->SdchErrorRecovery( |
+ DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX, dictionary_url); |
return false; |
} |
} |
if (!ports.empty() |
&& 0 == ports.count(dictionary_url.EffectiveIntPort())) { |
- SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL); |
+ sdch_manager->SdchErrorRecovery( |
+ DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL, dictionary_url); |
return false; |
} |
return true; |
} |
-// static |
-bool SdchManager::Dictionary::CanUse(const GURL& referring_url) { |
+bool SdchManager::Dictionary::CanUse( |
+ const GURL& referring_url, SdchManager* sdch_manager) { |
/* |
1. The request URL's host name domain-matches the Domain attribute of the |
dictionary. |
@@ -163,32 +197,38 @@ bool SdchManager::Dictionary::CanUse(const GURL& referring_url) { |
url scheme. |
*/ |
if (!DomainMatch(referring_url, domain_)) { |
- SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_DOMAIN); |
+ sdch_manager->SdchErrorRecovery( |
+ DICTIONARY_FOUND_HAS_WRONG_DOMAIN, referring_url); |
return false; |
} |
if (!ports_.empty() |
&& 0 == ports_.count(referring_url.EffectiveIntPort())) { |
- SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PORT_LIST); |
+ sdch_manager->SdchErrorRecovery( |
+ DICTIONARY_FOUND_HAS_WRONG_PORT_LIST, referring_url); |
return false; |
} |
if (path_.size() && !PathMatch(referring_url.path(), path_)) { |
- SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PATH); |
+ sdch_manager->SdchErrorRecovery( |
+ DICTIONARY_FOUND_HAS_WRONG_PATH, referring_url); |
return false; |
} |
if (!SdchManager::secure_scheme_supported() && |
referring_url.SchemeIsSecure()) { |
- SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME); |
+ sdch_manager->SdchErrorRecovery( |
+ DICTIONARY_FOUND_HAS_WRONG_SCHEME, referring_url); |
return false; |
} |
if (referring_url.SchemeIsSecure() != url_.SchemeIsSecure()) { |
- SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME); |
+ sdch_manager->SdchErrorRecovery( |
+ DICTIONARY_FOUND_HAS_WRONG_SCHEME, referring_url); |
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.SchemeIsHTTPOrHTTPS()) { |
- SdchErrorRecovery(ATTEMPT_TO_DECODE_NON_HTTP_DATA); |
+ sdch_manager->SdchErrorRecovery( |
+ ATTEMPT_TO_DECODE_NON_HTTP_DATA, referring_url); |
return false; |
} |
@@ -247,8 +287,10 @@ void SdchManager::ClearData() { |
} |
// static |
-void SdchManager::SdchErrorRecovery(ProblemCodes problem) { |
+void SdchManager::SdchErrorRecovery( |
+ ProblemCodes problem, const GURL& url) const { |
UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE); |
+ problems_.push_back(std::make_pair(url.spec(), problem)); |
} |
void SdchManager::set_sdch_fetcher(SdchFetcher* fetcher) { |
@@ -334,7 +376,7 @@ bool SdchManager::IsInSupportedDomain(const GURL& url) { |
blacklisted_domains_[domain] = count; |
else |
blacklisted_domains_.erase(domain); |
- SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET); |
+ SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET, url); |
return false; |
} |
@@ -362,18 +404,19 @@ bool SdchManager::CanFetchDictionary(const GURL& referring_url, |
// 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); |
+ SdchErrorRecovery( |
+ DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST, dictionary_url); |
return false; |
} |
if (!secure_scheme_supported() && referring_url.SchemeIsSecure()) { |
- SdchErrorRecovery(DICTIONARY_SELECTED_FOR_SSL); |
+ SdchErrorRecovery(DICTIONARY_SELECTED_FOR_SSL, dictionary_url); |
return false; |
} |
// 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); |
+ SdchErrorRecovery(DICTIONARY_SELECTED_FROM_NON_HTTP, dictionary_url); |
return false; |
} |
@@ -387,7 +430,7 @@ bool SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
std::string server_hash; |
GenerateHash(dictionary_text, &client_hash, &server_hash); |
if (dictionaries_.find(server_hash) != dictionaries_.end()) { |
- SdchErrorRecovery(DICTIONARY_ALREADY_LOADED); |
+ SdchErrorRecovery(DICTIONARY_ALREADY_LOADED, dictionary_url); |
return false; // Already loaded. |
} |
@@ -396,13 +439,13 @@ bool SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30)); |
if (dictionary_text.empty()) { |
- SdchErrorRecovery(DICTIONARY_HAS_NO_TEXT); |
+ SdchErrorRecovery(DICTIONARY_HAS_NO_TEXT, dictionary_url); |
return false; // Missing header. |
} |
size_t header_end = dictionary_text.find("\n\n"); |
if (std::string::npos == header_end) { |
- SdchErrorRecovery(DICTIONARY_HAS_NO_HEADER); |
+ SdchErrorRecovery(DICTIONARY_HAS_NO_HEADER, dictionary_url); |
return false; // Missing header. |
} |
size_t line_start = 0; // Start of line being parsed. |
@@ -413,7 +456,8 @@ bool SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
size_t colon_index = dictionary_text.find(':', line_start); |
if (std::string::npos == colon_index) { |
- SdchErrorRecovery(DICTIONARY_HEADER_LINE_MISSING_COLON); |
+ SdchErrorRecovery( |
+ DICTIONARY_HEADER_LINE_MISSING_COLON, dictionary_url); |
return false; // Illegal line missing a colon. |
} |
@@ -455,7 +499,7 @@ bool SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
if (!IsInSupportedDomain(dictionary_url)) |
return false; |
- if (!Dictionary::CanSet(domain, path, ports, dictionary_url)) |
+ if (!Dictionary::CanSet(domain, path, ports, dictionary_url, this)) |
return false; |
// TODO(jar): Remove these hacks to preclude a DOS attack involving piles of |
@@ -463,11 +507,11 @@ bool SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
// 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); |
+ SdchErrorRecovery(DICTIONARY_IS_TOO_LARGE, dictionary_url); |
return false; |
} |
if (kMaxDictionaryCount <= dictionaries_.size()) { |
- SdchErrorRecovery(DICTIONARY_COUNT_EXCEEDED); |
+ SdchErrorRecovery(DICTIONARY_COUNT_EXCEEDED, dictionary_url); |
return false; |
} |
@@ -494,7 +538,7 @@ void SdchManager::GetVcdiffDictionary( |
scoped_refptr<Dictionary> matching_dictionary = it->second; |
if (!IsInSupportedDomain(referring_url)) |
return; |
- if (!matching_dictionary->CanUse(referring_url)) |
+ if (!matching_dictionary->CanUse(referring_url, this)) |
return; |
*dictionary = matching_dictionary; |
} |
@@ -537,6 +581,51 @@ void SdchManager::GenerateHash(const std::string& dictionary_text, |
DCHECK_EQ(client_hash->length(), 8u); |
} |
+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 = it->second->DictionaryInfoToValue(); |
+ entry_dict->SetString("server_hash", it->first); |
+ entry_list->Append(entry_dict); |
+ } |
+ value->Set("dictionaries", entry_list); |
+ |
+ entry_list = new base::ListValue(); |
+ for (DomainCounter::const_iterator it = blacklisted_domains_.begin(); |
+ it != blacklisted_domains_.end(); ++it) { |
+ base::DictionaryValue* entry_dict = new base::DictionaryValue(); |
+ entry_dict->SetString("domain", it->first); |
+ if (it->second != INT_MAX) |
+ entry_dict->SetInteger("tries", it->second); |
+ entry_list->Append(entry_dict); |
+ } |
+ value->Set("blacklisted", entry_list); |
+ |
+ |
+ base::ListValue* dict_problems_list = new base::ListValue(); |
+ base::ListValue* usage_problems_list = new base::ListValue(); |
+ for (size_t i = 0; i < problems_.size(); ++i) { |
+ base::DictionaryValue* entry = new base::DictionaryValue(); |
+ entry->SetString("url", problems_[i].first); |
+ entry->SetInteger("error", problems_[i].second); |
+ if (IsDictionaryProblem(problems_[i].second)) { |
+ dict_problems_list->Append(entry); |
+ } else { |
+ usage_problems_list->Append(entry); |
+ } |
+ } |
+ value->Set("dict_errors", dict_problems_list); |
+ value->Set("usage_errors", usage_problems_list); |
+ |
+ return value; |
+} |
+ |
//------------------------------------------------------------------------------ |
// Methods for supporting latency experiments. |
@@ -555,7 +644,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(LATENCY_TEST_DISALLOWED, url); |
allow_latency_experiment_.erase(it); |
} |