| 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 "base/values.h" |
| 12 #include "crypto/sha2.h" | 13 #include "crypto/sha2.h" |
| 13 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | 14 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
| 14 #include "net/url_request/url_request_http_job.h" | 15 #include "net/url_request/url_request_http_job.h" |
| 15 | 16 |
| 16 namespace net { | 17 namespace net { |
| 17 | 18 |
| 19 namespace { |
| 20 |
| 21 bool IsDictionaryProblem(SdchManager::ProblemCodes code) { |
| 22 return code >= SdchManager::DICTIONARY_HAS_NO_HEADER && |
| 23 code <= SdchManager::DICTIONARY_ALREADY_TRIED_TO_DOWNLOAD; |
| 24 } |
| 25 |
| 26 } |
| 27 |
| 18 //------------------------------------------------------------------------------ | 28 //------------------------------------------------------------------------------ |
| 19 // static | 29 // static |
| 20 | 30 |
| 21 // Adjust SDCH limits downwards for mobile. | 31 // Adjust SDCH limits downwards for mobile. |
| 22 #if defined(OS_ANDROID) || defined(OS_IOS) | 32 #if defined(OS_ANDROID) || defined(OS_IOS) |
| 23 // static | 33 // static |
| 24 const size_t SdchManager::kMaxDictionaryCount = 1; | 34 const size_t SdchManager::kMaxDictionaryCount = 1; |
| 25 const size_t SdchManager::kMaxDictionarySize = 150 * 1000; | 35 const size_t SdchManager::kMaxDictionarySize = 150 * 1000; |
| 26 #else | 36 #else |
| 27 // static | 37 // static |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 80 return false; | 90 return false; |
| 81 if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure()) | 91 if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure()) |
| 82 return false; | 92 return false; |
| 83 if (target_url.SchemeIsSecure() != url_.SchemeIsSecure()) | 93 if (target_url.SchemeIsSecure() != url_.SchemeIsSecure()) |
| 84 return false; | 94 return false; |
| 85 if (base::Time::Now() > expiration_) | 95 if (base::Time::Now() > expiration_) |
| 86 return false; | 96 return false; |
| 87 return true; | 97 return true; |
| 88 } | 98 } |
| 89 | 99 |
| 100 base::DictionaryValue* |
| 101 SdchManager::Dictionary::DictionaryInfoToValue() const { |
| 102 base::DictionaryValue* value = new base::DictionaryValue(); |
| 103 value->SetString("url", url_.spec()); |
| 104 value->SetString("client_hash", client_hash_); |
| 105 value->SetString("domain", domain_); |
| 106 value->SetString("path", path_); |
| 107 value->SetString("expiration", |
| 108 base::Int64ToString(expiration_.ToInternalValue())); |
| 109 base::ListValue* port_list = new base::ListValue(); |
| 110 for (std::set<int>::const_iterator it = ports_.begin(); |
| 111 it != ports_.end(); ++it) { |
| 112 port_list->AppendInteger(*it); |
| 113 } |
| 114 value->Set("ports", port_list); |
| 115 return value; |
| 116 } |
| 117 |
| 90 //------------------------------------------------------------------------------ | 118 //------------------------------------------------------------------------------ |
| 91 // Security functions restricting loads and use of dictionaries. | 119 // Security functions restricting loads and use of dictionaries. |
| 92 | 120 |
| 93 // static | 121 // static |
| 94 bool SdchManager::Dictionary::CanSet(const std::string& domain, | 122 bool SdchManager::Dictionary::CanSet(const std::string& domain, |
| 95 const std::string& path, | 123 const std::string& path, |
| 96 const std::set<int>& ports, | 124 const std::set<int>& ports, |
| 97 const GURL& dictionary_url) { | 125 const GURL& dictionary_url, |
| 126 SdchManager* sdch_manager) { |
| 98 /* | 127 /* |
| 99 A dictionary is invalid and must not be stored if any of the following are | 128 A dictionary is invalid and must not be stored if any of the following are |
| 100 true: | 129 true: |
| 101 1. The dictionary has no Domain attribute. | 130 1. The dictionary has no Domain attribute. |
| 102 2. The effective host name that derives from the referer URL host name does | 131 2. The effective host name that derives from the referer URL host name does |
| 103 not domain-match the Domain attribute. | 132 not domain-match the Domain attribute. |
| 104 3. The Domain attribute is a top level domain. | 133 3. The Domain attribute is a top level domain. |
| 105 4. The referer URL host is a host domain name (not IP address) and has the | 134 4. The referer URL host is a host domain name (not IP address) and has the |
| 106 form HD, where D is the value of the Domain attribute, and H is a string | 135 form HD, where D is the value of the Domain attribute, and H is a string |
| 107 that contains one or more dots. | 136 that contains one or more dots. |
| 108 5. If the dictionary has a Port attribute and the referer URL's port was not | 137 5. If the dictionary has a Port attribute and the referer URL's port was not |
| 109 in the list. | 138 in the list. |
| 110 */ | 139 */ |
| 111 | 140 |
| 112 // TODO(jar): Redirects in dictionary fetches might plausibly be problematic, | 141 // TODO(jar): Redirects in dictionary fetches might plausibly be problematic, |
| 113 // and hence the conservative approach is to not allow any redirects (if there | 142 // and hence the conservative approach is to not allow any redirects (if there |
| 114 // were any... then don't allow the dictionary to be set). | 143 // were any... then don't allow the dictionary to be set). |
| 115 | 144 |
| 116 if (domain.empty()) { | 145 if (domain.empty()) { |
| 117 SdchErrorRecovery(DICTIONARY_MISSING_DOMAIN_SPECIFIER); | 146 sdch_manager->SdchErrorRecovery( |
| 147 DICTIONARY_MISSING_DOMAIN_SPECIFIER, dictionary_url); |
| 118 return false; // Domain is required. | 148 return false; // Domain is required. |
| 119 } | 149 } |
| 120 if (registry_controlled_domains::GetDomainAndRegistry( | 150 if (registry_controlled_domains::GetDomainAndRegistry( |
| 121 domain, | 151 domain, |
| 122 registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES).empty()) { | 152 registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES).empty()) { |
| 123 SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN); | 153 sdch_manager->SdchErrorRecovery( |
| 154 DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN, dictionary_url); |
| 124 return false; // domain was a TLD. | 155 return false; // domain was a TLD. |
| 125 } | 156 } |
| 126 if (!Dictionary::DomainMatch(dictionary_url, domain)) { | 157 if (!Dictionary::DomainMatch(dictionary_url, domain)) { |
| 127 SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL); | 158 sdch_manager->SdchErrorRecovery( |
| 159 DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL, dictionary_url); |
| 128 return false; | 160 return false; |
| 129 } | 161 } |
| 130 | 162 |
| 131 std::string referrer_url_host = dictionary_url.host(); | 163 std::string referrer_url_host = dictionary_url.host(); |
| 132 size_t postfix_domain_index = referrer_url_host.rfind(domain); | 164 size_t postfix_domain_index = referrer_url_host.rfind(domain); |
| 133 // See if it is indeed a postfix, or just an internal string. | 165 // See if it is indeed a postfix, or just an internal string. |
| 134 if (referrer_url_host.size() == postfix_domain_index + domain.size()) { | 166 if (referrer_url_host.size() == postfix_domain_index + domain.size()) { |
| 135 // It is a postfix... so check to see if there's a dot in the prefix. | 167 // 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('.'); | 168 size_t end_of_host_index = referrer_url_host.find_first_of('.'); |
| 137 if (referrer_url_host.npos != end_of_host_index && | 169 if (referrer_url_host.npos != end_of_host_index && |
| 138 end_of_host_index < postfix_domain_index) { | 170 end_of_host_index < postfix_domain_index) { |
| 139 SdchErrorRecovery(DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX); | 171 sdch_manager->SdchErrorRecovery( |
| 172 DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX, dictionary_url); |
| 140 return false; | 173 return false; |
| 141 } | 174 } |
| 142 } | 175 } |
| 143 | 176 |
| 144 if (!ports.empty() | 177 if (!ports.empty() |
| 145 && 0 == ports.count(dictionary_url.EffectiveIntPort())) { | 178 && 0 == ports.count(dictionary_url.EffectiveIntPort())) { |
| 146 SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL); | 179 sdch_manager->SdchErrorRecovery( |
| 180 DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL, dictionary_url); |
| 147 return false; | 181 return false; |
| 148 } | 182 } |
| 149 return true; | 183 return true; |
| 150 } | 184 } |
| 151 | 185 |
| 152 // static | 186 bool SdchManager::Dictionary::CanUse( |
| 153 bool SdchManager::Dictionary::CanUse(const GURL& referring_url) { | 187 const GURL& referring_url, SdchManager* sdch_manager) { |
| 154 /* | 188 /* |
| 155 1. The request URL's host name domain-matches the Domain attribute of the | 189 1. The request URL's host name domain-matches the Domain attribute of the |
| 156 dictionary. | 190 dictionary. |
| 157 2. If the dictionary has a Port attribute, the request port is one of the | 191 2. If the dictionary has a Port attribute, the request port is one of the |
| 158 ports listed in the Port attribute. | 192 ports listed in the Port attribute. |
| 159 3. The request URL path-matches the path attribute of the dictionary. | 193 3. The request URL path-matches the path attribute of the dictionary. |
| 160 4. The request is not an HTTPS request. | 194 4. The request is not an HTTPS request. |
| 161 We can override (ignore) item (4) only when we have explicitly enabled | 195 We can override (ignore) item (4) only when we have explicitly enabled |
| 162 HTTPS support AND the dictionary acquisition scheme matches the target | 196 HTTPS support AND the dictionary acquisition scheme matches the target |
| 163 url scheme. | 197 url scheme. |
| 164 */ | 198 */ |
| 165 if (!DomainMatch(referring_url, domain_)) { | 199 if (!DomainMatch(referring_url, domain_)) { |
| 166 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_DOMAIN); | 200 sdch_manager->SdchErrorRecovery( |
| 201 DICTIONARY_FOUND_HAS_WRONG_DOMAIN, referring_url); |
| 167 return false; | 202 return false; |
| 168 } | 203 } |
| 169 if (!ports_.empty() | 204 if (!ports_.empty() |
| 170 && 0 == ports_.count(referring_url.EffectiveIntPort())) { | 205 && 0 == ports_.count(referring_url.EffectiveIntPort())) { |
| 171 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PORT_LIST); | 206 sdch_manager->SdchErrorRecovery( |
| 207 DICTIONARY_FOUND_HAS_WRONG_PORT_LIST, referring_url); |
| 172 return false; | 208 return false; |
| 173 } | 209 } |
| 174 if (path_.size() && !PathMatch(referring_url.path(), path_)) { | 210 if (path_.size() && !PathMatch(referring_url.path(), path_)) { |
| 175 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PATH); | 211 sdch_manager->SdchErrorRecovery( |
| 212 DICTIONARY_FOUND_HAS_WRONG_PATH, referring_url); |
| 176 return false; | 213 return false; |
| 177 } | 214 } |
| 178 if (!SdchManager::secure_scheme_supported() && | 215 if (!SdchManager::secure_scheme_supported() && |
| 179 referring_url.SchemeIsSecure()) { | 216 referring_url.SchemeIsSecure()) { |
| 180 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME); | 217 sdch_manager->SdchErrorRecovery( |
| 218 DICTIONARY_FOUND_HAS_WRONG_SCHEME, referring_url); |
| 181 return false; | 219 return false; |
| 182 } | 220 } |
| 183 if (referring_url.SchemeIsSecure() != url_.SchemeIsSecure()) { | 221 if (referring_url.SchemeIsSecure() != url_.SchemeIsSecure()) { |
| 184 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME); | 222 sdch_manager->SdchErrorRecovery( |
| 223 DICTIONARY_FOUND_HAS_WRONG_SCHEME, referring_url); |
| 185 return false; | 224 return false; |
| 186 } | 225 } |
| 187 | 226 |
| 188 // TODO(jar): Remove overly restrictive failsafe test (added per security | 227 // TODO(jar): Remove overly restrictive failsafe test (added per security |
| 189 // review) when we have a need to be more general. | 228 // review) when we have a need to be more general. |
| 190 if (!referring_url.SchemeIsHTTPOrHTTPS()) { | 229 if (!referring_url.SchemeIsHTTPOrHTTPS()) { |
| 191 SdchErrorRecovery(ATTEMPT_TO_DECODE_NON_HTTP_DATA); | 230 sdch_manager->SdchErrorRecovery( |
| 231 ATTEMPT_TO_DECODE_NON_HTTP_DATA, referring_url); |
| 192 return false; | 232 return false; |
| 193 } | 233 } |
| 194 | 234 |
| 195 return true; | 235 return true; |
| 196 } | 236 } |
| 197 | 237 |
| 198 bool SdchManager::Dictionary::PathMatch(const std::string& path, | 238 bool SdchManager::Dictionary::PathMatch(const std::string& path, |
| 199 const std::string& restriction) { | 239 const std::string& restriction) { |
| 200 /* Must be either: | 240 /* Must be either: |
| 201 1. P2 is equal to P1 | 241 1. P2 is equal to P1 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 240 fetcher_->Cancel(); | 280 fetcher_->Cancel(); |
| 241 | 281 |
| 242 // Note that this may result in not having dictionaries we've advertised | 282 // Note that this may result in not having dictionaries we've advertised |
| 243 // for incoming responses. The window is relatively small (as ClearData() | 283 // for incoming responses. The window is relatively small (as ClearData() |
| 244 // is not expected to be called frequently), so we rely on meta-refresh | 284 // is not expected to be called frequently), so we rely on meta-refresh |
| 245 // to handle this case. | 285 // to handle this case. |
| 246 dictionaries_.clear(); | 286 dictionaries_.clear(); |
| 247 } | 287 } |
| 248 | 288 |
| 249 // static | 289 // static |
| 250 void SdchManager::SdchErrorRecovery(ProblemCodes problem) { | 290 void SdchManager::SdchErrorRecovery( |
| 291 ProblemCodes problem, const GURL& url) const { |
| 251 UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE); | 292 UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE); |
| 293 problems_.push_back(std::make_pair(url.spec(), problem)); |
| 252 } | 294 } |
| 253 | 295 |
| 254 void SdchManager::set_sdch_fetcher(SdchFetcher* fetcher) { | 296 void SdchManager::set_sdch_fetcher(SdchFetcher* fetcher) { |
| 255 DCHECK(CalledOnValidThread()); | 297 DCHECK(CalledOnValidThread()); |
| 256 fetcher_.reset(fetcher); | 298 fetcher_.reset(fetcher); |
| 257 } | 299 } |
| 258 | 300 |
| 259 // static | 301 // static |
| 260 void SdchManager::EnableSdchSupport(bool enabled) { | 302 void SdchManager::EnableSdchSupport(bool enabled) { |
| 261 g_sdch_enabled_ = enabled; | 303 g_sdch_enabled_ = enabled; |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 327 std::string domain(StringToLowerASCII(url.host())); | 369 std::string domain(StringToLowerASCII(url.host())); |
| 328 DomainCounter::iterator it = blacklisted_domains_.find(domain); | 370 DomainCounter::iterator it = blacklisted_domains_.find(domain); |
| 329 if (blacklisted_domains_.end() == it) | 371 if (blacklisted_domains_.end() == it) |
| 330 return true; | 372 return true; |
| 331 | 373 |
| 332 int count = it->second - 1; | 374 int count = it->second - 1; |
| 333 if (count > 0) | 375 if (count > 0) |
| 334 blacklisted_domains_[domain] = count; | 376 blacklisted_domains_[domain] = count; |
| 335 else | 377 else |
| 336 blacklisted_domains_.erase(domain); | 378 blacklisted_domains_.erase(domain); |
| 337 SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET); | 379 SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET, url); |
| 338 return false; | 380 return false; |
| 339 } | 381 } |
| 340 | 382 |
| 341 void SdchManager::FetchDictionary(const GURL& request_url, | 383 void SdchManager::FetchDictionary(const GURL& request_url, |
| 342 const GURL& dictionary_url) { | 384 const GURL& dictionary_url) { |
| 343 DCHECK(CalledOnValidThread()); | 385 DCHECK(CalledOnValidThread()); |
| 344 if (CanFetchDictionary(request_url, dictionary_url) && fetcher_.get()) | 386 if (CanFetchDictionary(request_url, dictionary_url) && fetcher_.get()) |
| 345 fetcher_->Schedule(dictionary_url); | 387 fetcher_->Schedule(dictionary_url); |
| 346 } | 388 } |
| 347 | 389 |
| 348 bool SdchManager::CanFetchDictionary(const GURL& referring_url, | 390 bool SdchManager::CanFetchDictionary(const GURL& referring_url, |
| 349 const GURL& dictionary_url) const { | 391 const GURL& dictionary_url) const { |
| 350 DCHECK(CalledOnValidThread()); | 392 DCHECK(CalledOnValidThread()); |
| 351 /* The user agent may retrieve a dictionary from the dictionary URL if all of | 393 /* The user agent may retrieve a dictionary from the dictionary URL if all of |
| 352 the following are true: | 394 the following are true: |
| 353 1 The dictionary URL host name matches the referrer URL host name and | 395 1 The dictionary URL host name matches the referrer URL host name and |
| 354 scheme. | 396 scheme. |
| 355 2 The dictionary URL host name domain matches the parent domain of the | 397 2 The dictionary URL host name domain matches the parent domain of the |
| 356 referrer URL host name | 398 referrer URL host name |
| 357 3 The parent domain of the referrer URL host name is not a top level | 399 3 The parent domain of the referrer URL host name is not a top level |
| 358 domain | 400 domain |
| 359 4 The dictionary URL is not an HTTPS URL. | 401 4 The dictionary URL is not an HTTPS URL. |
| 360 */ | 402 */ |
| 361 // Item (1) above implies item (2). Spec should be updated. | 403 // Item (1) above implies item (2). Spec should be updated. |
| 362 // I take "host name match" to be "is identical to" | 404 // I take "host name match" to be "is identical to" |
| 363 if (referring_url.host() != dictionary_url.host() || | 405 if (referring_url.host() != dictionary_url.host() || |
| 364 referring_url.scheme() != dictionary_url.scheme()) { | 406 referring_url.scheme() != dictionary_url.scheme()) { |
| 365 SdchErrorRecovery(DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST); | 407 SdchErrorRecovery( |
| 408 DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST, dictionary_url); |
| 366 return false; | 409 return false; |
| 367 } | 410 } |
| 368 if (!secure_scheme_supported() && referring_url.SchemeIsSecure()) { | 411 if (!secure_scheme_supported() && referring_url.SchemeIsSecure()) { |
| 369 SdchErrorRecovery(DICTIONARY_SELECTED_FOR_SSL); | 412 SdchErrorRecovery(DICTIONARY_SELECTED_FOR_SSL, dictionary_url); |
| 370 return false; | 413 return false; |
| 371 } | 414 } |
| 372 | 415 |
| 373 // TODO(jar): Remove this failsafe conservative hack which is more restrictive | 416 // TODO(jar): Remove this failsafe conservative hack which is more restrictive |
| 374 // than current SDCH spec when needed, and justified by security audit. | 417 // than current SDCH spec when needed, and justified by security audit. |
| 375 if (!referring_url.SchemeIsHTTPOrHTTPS()) { | 418 if (!referring_url.SchemeIsHTTPOrHTTPS()) { |
| 376 SdchErrorRecovery(DICTIONARY_SELECTED_FROM_NON_HTTP); | 419 SdchErrorRecovery(DICTIONARY_SELECTED_FROM_NON_HTTP, dictionary_url); |
| 377 return false; | 420 return false; |
| 378 } | 421 } |
| 379 | 422 |
| 380 return true; | 423 return true; |
| 381 } | 424 } |
| 382 | 425 |
| 383 bool SdchManager::AddSdchDictionary(const std::string& dictionary_text, | 426 bool SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
| 384 const GURL& dictionary_url) { | 427 const GURL& dictionary_url) { |
| 385 DCHECK(CalledOnValidThread()); | 428 DCHECK(CalledOnValidThread()); |
| 386 std::string client_hash; | 429 std::string client_hash; |
| 387 std::string server_hash; | 430 std::string server_hash; |
| 388 GenerateHash(dictionary_text, &client_hash, &server_hash); | 431 GenerateHash(dictionary_text, &client_hash, &server_hash); |
| 389 if (dictionaries_.find(server_hash) != dictionaries_.end()) { | 432 if (dictionaries_.find(server_hash) != dictionaries_.end()) { |
| 390 SdchErrorRecovery(DICTIONARY_ALREADY_LOADED); | 433 SdchErrorRecovery(DICTIONARY_ALREADY_LOADED, dictionary_url); |
| 391 return false; // Already loaded. | 434 return false; // Already loaded. |
| 392 } | 435 } |
| 393 | 436 |
| 394 std::string domain, path; | 437 std::string domain, path; |
| 395 std::set<int> ports; | 438 std::set<int> ports; |
| 396 base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30)); | 439 base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30)); |
| 397 | 440 |
| 398 if (dictionary_text.empty()) { | 441 if (dictionary_text.empty()) { |
| 399 SdchErrorRecovery(DICTIONARY_HAS_NO_TEXT); | 442 SdchErrorRecovery(DICTIONARY_HAS_NO_TEXT, dictionary_url); |
| 400 return false; // Missing header. | 443 return false; // Missing header. |
| 401 } | 444 } |
| 402 | 445 |
| 403 size_t header_end = dictionary_text.find("\n\n"); | 446 size_t header_end = dictionary_text.find("\n\n"); |
| 404 if (std::string::npos == header_end) { | 447 if (std::string::npos == header_end) { |
| 405 SdchErrorRecovery(DICTIONARY_HAS_NO_HEADER); | 448 SdchErrorRecovery(DICTIONARY_HAS_NO_HEADER, dictionary_url); |
| 406 return false; // Missing header. | 449 return false; // Missing header. |
| 407 } | 450 } |
| 408 size_t line_start = 0; // Start of line being parsed. | 451 size_t line_start = 0; // Start of line being parsed. |
| 409 while (1) { | 452 while (1) { |
| 410 size_t line_end = dictionary_text.find('\n', line_start); | 453 size_t line_end = dictionary_text.find('\n', line_start); |
| 411 DCHECK(std::string::npos != line_end); | 454 DCHECK(std::string::npos != line_end); |
| 412 DCHECK_LE(line_end, header_end); | 455 DCHECK_LE(line_end, header_end); |
| 413 | 456 |
| 414 size_t colon_index = dictionary_text.find(':', line_start); | 457 size_t colon_index = dictionary_text.find(':', line_start); |
| 415 if (std::string::npos == colon_index) { | 458 if (std::string::npos == colon_index) { |
| 416 SdchErrorRecovery(DICTIONARY_HEADER_LINE_MISSING_COLON); | 459 SdchErrorRecovery( |
| 460 DICTIONARY_HEADER_LINE_MISSING_COLON, dictionary_url); |
| 417 return false; // Illegal line missing a colon. | 461 return false; // Illegal line missing a colon. |
| 418 } | 462 } |
| 419 | 463 |
| 420 if (colon_index > line_end) | 464 if (colon_index > line_end) |
| 421 break; | 465 break; |
| 422 | 466 |
| 423 size_t value_start = dictionary_text.find_first_not_of(" \t", | 467 size_t value_start = dictionary_text.find_first_not_of(" \t", |
| 424 colon_index + 1); | 468 colon_index + 1); |
| 425 if (std::string::npos != value_start) { | 469 if (std::string::npos != value_start) { |
| 426 if (value_start >= line_end) | 470 if (value_start >= line_end) |
| (...skipping 21 matching lines...) Expand all Loading... |
| 448 } | 492 } |
| 449 | 493 |
| 450 if (line_end >= header_end) | 494 if (line_end >= header_end) |
| 451 break; | 495 break; |
| 452 line_start = line_end + 1; | 496 line_start = line_end + 1; |
| 453 } | 497 } |
| 454 | 498 |
| 455 if (!IsInSupportedDomain(dictionary_url)) | 499 if (!IsInSupportedDomain(dictionary_url)) |
| 456 return false; | 500 return false; |
| 457 | 501 |
| 458 if (!Dictionary::CanSet(domain, path, ports, dictionary_url)) | 502 if (!Dictionary::CanSet(domain, path, ports, dictionary_url, this)) |
| 459 return false; | 503 return false; |
| 460 | 504 |
| 461 // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of | 505 // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of |
| 462 // useless dictionaries. We should probably have a cache eviction plan, | 506 // useless dictionaries. We should probably have a cache eviction plan, |
| 463 // instead of just blocking additions. For now, with the spec in flux, it | 507 // instead of just blocking additions. For now, with the spec in flux, it |
| 464 // is probably not worth doing eviction handling. | 508 // is probably not worth doing eviction handling. |
| 465 if (kMaxDictionarySize < dictionary_text.size()) { | 509 if (kMaxDictionarySize < dictionary_text.size()) { |
| 466 SdchErrorRecovery(DICTIONARY_IS_TOO_LARGE); | 510 SdchErrorRecovery(DICTIONARY_IS_TOO_LARGE, dictionary_url); |
| 467 return false; | 511 return false; |
| 468 } | 512 } |
| 469 if (kMaxDictionaryCount <= dictionaries_.size()) { | 513 if (kMaxDictionaryCount <= dictionaries_.size()) { |
| 470 SdchErrorRecovery(DICTIONARY_COUNT_EXCEEDED); | 514 SdchErrorRecovery(DICTIONARY_COUNT_EXCEEDED, dictionary_url); |
| 471 return false; | 515 return false; |
| 472 } | 516 } |
| 473 | 517 |
| 474 UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size()); | 518 UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size()); |
| 475 DVLOG(1) << "Loaded dictionary with client hash " << client_hash | 519 DVLOG(1) << "Loaded dictionary with client hash " << client_hash |
| 476 << " and server hash " << server_hash; | 520 << " and server hash " << server_hash; |
| 477 Dictionary* dictionary = | 521 Dictionary* dictionary = |
| 478 new Dictionary(dictionary_text, header_end + 2, client_hash, | 522 new Dictionary(dictionary_text, header_end + 2, client_hash, |
| 479 dictionary_url, domain, path, expiration, ports); | 523 dictionary_url, domain, path, expiration, ports); |
| 480 dictionaries_[server_hash] = dictionary; | 524 dictionaries_[server_hash] = dictionary; |
| 481 return true; | 525 return true; |
| 482 } | 526 } |
| 483 | 527 |
| 484 void SdchManager::GetVcdiffDictionary( | 528 void SdchManager::GetVcdiffDictionary( |
| 485 const std::string& server_hash, | 529 const std::string& server_hash, |
| 486 const GURL& referring_url, | 530 const GURL& referring_url, |
| 487 scoped_refptr<Dictionary>* dictionary) { | 531 scoped_refptr<Dictionary>* dictionary) { |
| 488 DCHECK(CalledOnValidThread()); | 532 DCHECK(CalledOnValidThread()); |
| 489 *dictionary = NULL; | 533 *dictionary = NULL; |
| 490 DictionaryMap::iterator it = dictionaries_.find(server_hash); | 534 DictionaryMap::iterator it = dictionaries_.find(server_hash); |
| 491 if (it == dictionaries_.end()) { | 535 if (it == dictionaries_.end()) { |
| 492 return; | 536 return; |
| 493 } | 537 } |
| 494 scoped_refptr<Dictionary> matching_dictionary = it->second; | 538 scoped_refptr<Dictionary> matching_dictionary = it->second; |
| 495 if (!IsInSupportedDomain(referring_url)) | 539 if (!IsInSupportedDomain(referring_url)) |
| 496 return; | 540 return; |
| 497 if (!matching_dictionary->CanUse(referring_url)) | 541 if (!matching_dictionary->CanUse(referring_url, this)) |
| 498 return; | 542 return; |
| 499 *dictionary = matching_dictionary; | 543 *dictionary = matching_dictionary; |
| 500 } | 544 } |
| 501 | 545 |
| 502 // TODO(jar): If we have evictions from the dictionaries_, then we need to | 546 // TODO(jar): If we have evictions from the dictionaries_, then we need to |
| 503 // change this interface to return a list of reference counted Dictionary | 547 // change this interface to return a list of reference counted Dictionary |
| 504 // instances that can be used if/when a server specifies one. | 548 // instances that can be used if/when a server specifies one. |
| 505 void SdchManager::GetAvailDictionaryList(const GURL& target_url, | 549 void SdchManager::GetAvailDictionaryList(const GURL& target_url, |
| 506 std::string* list) { | 550 std::string* list) { |
| 507 DCHECK(CalledOnValidThread()); | 551 DCHECK(CalledOnValidThread()); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 530 | 574 |
| 531 std::string first_48_bits(&binary_hash[0], 6); | 575 std::string first_48_bits(&binary_hash[0], 6); |
| 532 std::string second_48_bits(&binary_hash[6], 6); | 576 std::string second_48_bits(&binary_hash[6], 6); |
| 533 UrlSafeBase64Encode(first_48_bits, client_hash); | 577 UrlSafeBase64Encode(first_48_bits, client_hash); |
| 534 UrlSafeBase64Encode(second_48_bits, server_hash); | 578 UrlSafeBase64Encode(second_48_bits, server_hash); |
| 535 | 579 |
| 536 DCHECK_EQ(server_hash->length(), 8u); | 580 DCHECK_EQ(server_hash->length(), 8u); |
| 537 DCHECK_EQ(client_hash->length(), 8u); | 581 DCHECK_EQ(client_hash->length(), 8u); |
| 538 } | 582 } |
| 539 | 583 |
| 584 base::Value* SdchManager::SdchInfoToValue() const { |
| 585 base::DictionaryValue* value = new base::DictionaryValue(); |
| 586 |
| 587 value->SetBoolean("sdch_enabled", sdch_enabled()); |
| 588 value->SetBoolean("secure_scheme_support", secure_scheme_supported()); |
| 589 |
| 590 base::ListValue* entry_list = new base::ListValue(); |
| 591 for (DictionaryMap::const_iterator it = dictionaries_.begin(); |
| 592 it != dictionaries_.end(); ++it) { |
| 593 base::DictionaryValue* entry_dict = it->second->DictionaryInfoToValue(); |
| 594 entry_dict->SetString("server_hash", it->first); |
| 595 entry_list->Append(entry_dict); |
| 596 } |
| 597 value->Set("dictionaries", entry_list); |
| 598 |
| 599 entry_list = new base::ListValue(); |
| 600 for (DomainCounter::const_iterator it = blacklisted_domains_.begin(); |
| 601 it != blacklisted_domains_.end(); ++it) { |
| 602 base::DictionaryValue* entry_dict = new base::DictionaryValue(); |
| 603 entry_dict->SetString("domain", it->first); |
| 604 if (it->second != INT_MAX) |
| 605 entry_dict->SetInteger("tries", it->second); |
| 606 entry_list->Append(entry_dict); |
| 607 } |
| 608 value->Set("blacklisted", entry_list); |
| 609 |
| 610 |
| 611 base::ListValue* dict_problems_list = new base::ListValue(); |
| 612 base::ListValue* usage_problems_list = new base::ListValue(); |
| 613 for (size_t i = 0; i < problems_.size(); ++i) { |
| 614 base::DictionaryValue* entry = new base::DictionaryValue(); |
| 615 entry->SetString("url", problems_[i].first); |
| 616 entry->SetInteger("error", problems_[i].second); |
| 617 if (IsDictionaryProblem(problems_[i].second)) { |
| 618 dict_problems_list->Append(entry); |
| 619 } else { |
| 620 usage_problems_list->Append(entry); |
| 621 } |
| 622 } |
| 623 value->Set("dict_errors", dict_problems_list); |
| 624 value->Set("usage_errors", usage_problems_list); |
| 625 |
| 626 return value; |
| 627 } |
| 628 |
| 540 //------------------------------------------------------------------------------ | 629 //------------------------------------------------------------------------------ |
| 541 // Methods for supporting latency experiments. | 630 // Methods for supporting latency experiments. |
| 542 | 631 |
| 543 bool SdchManager::AllowLatencyExperiment(const GURL& url) const { | 632 bool SdchManager::AllowLatencyExperiment(const GURL& url) const { |
| 544 DCHECK(CalledOnValidThread()); | 633 DCHECK(CalledOnValidThread()); |
| 545 return allow_latency_experiment_.end() != | 634 return allow_latency_experiment_.end() != |
| 546 allow_latency_experiment_.find(url.host()); | 635 allow_latency_experiment_.find(url.host()); |
| 547 } | 636 } |
| 548 | 637 |
| 549 void SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) { | 638 void SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) { |
| 550 DCHECK(CalledOnValidThread()); | 639 DCHECK(CalledOnValidThread()); |
| 551 if (enable) { | 640 if (enable) { |
| 552 allow_latency_experiment_.insert(url.host()); | 641 allow_latency_experiment_.insert(url.host()); |
| 553 return; | 642 return; |
| 554 } | 643 } |
| 555 ExperimentSet::iterator it = allow_latency_experiment_.find(url.host()); | 644 ExperimentSet::iterator it = allow_latency_experiment_.find(url.host()); |
| 556 if (allow_latency_experiment_.end() == it) | 645 if (allow_latency_experiment_.end() == it) |
| 557 return; // It was already erased, or never allowed. | 646 return; // It was already erased, or never allowed. |
| 558 SdchErrorRecovery(LATENCY_TEST_DISALLOWED); | 647 SdchErrorRecovery(LATENCY_TEST_DISALLOWED, url); |
| 559 allow_latency_experiment_.erase(it); | 648 allow_latency_experiment_.erase(it); |
| 560 } | 649 } |
| 561 | 650 |
| 562 // static | 651 // static |
| 563 void SdchManager::UrlSafeBase64Encode(const std::string& input, | 652 void SdchManager::UrlSafeBase64Encode(const std::string& input, |
| 564 std::string* output) { | 653 std::string* output) { |
| 565 // Since this is only done during a dictionary load, and hashes are only 8 | 654 // Since this is only done during a dictionary load, and hashes are only 8 |
| 566 // characters, we just do the simple fixup, rather than rewriting the encoder. | 655 // characters, we just do the simple fixup, rather than rewriting the encoder. |
| 567 base::Base64Encode(input, output); | 656 base::Base64Encode(input, output); |
| 568 for (size_t i = 0; i < output->size(); ++i) { | 657 for (size_t i = 0; i < output->size(); ++i) { |
| 569 switch (output->data()[i]) { | 658 switch (output->data()[i]) { |
| 570 case '+': | 659 case '+': |
| 571 (*output)[i] = '-'; | 660 (*output)[i] = '-'; |
| 572 continue; | 661 continue; |
| 573 case '/': | 662 case '/': |
| 574 (*output)[i] = '_'; | 663 (*output)[i] = '_'; |
| 575 continue; | 664 continue; |
| 576 default: | 665 default: |
| 577 continue; | 666 continue; |
| 578 } | 667 } |
| 579 } | 668 } |
| 580 } | 669 } |
| 581 | 670 |
| 582 } // namespace net | 671 } // namespace net |
| OLD | NEW |