Chromium Code Reviews| 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" |
| 15 #include "net/base/sdch_net_log_params.h" | |
| 14 #include "net/url_request/url_request_http_job.h" | 16 #include "net/url_request/url_request_http_job.h" |
| 15 | 17 |
| 16 namespace net { | 18 namespace net { |
| 17 | 19 |
| 18 //------------------------------------------------------------------------------ | 20 //------------------------------------------------------------------------------ |
| 19 // static | 21 // static |
| 20 | 22 |
| 21 // Adjust SDCH limits downwards for mobile. | 23 // Adjust SDCH limits downwards for mobile. |
| 22 #if defined(OS_ANDROID) || defined(OS_IOS) | 24 #if defined(OS_ANDROID) || defined(OS_IOS) |
| 23 // static | 25 // static |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 49 url_(gurl), | 51 url_(gurl), |
| 50 domain_(domain), | 52 domain_(domain), |
| 51 path_(path), | 53 path_(path), |
| 52 expiration_(expiration), | 54 expiration_(expiration), |
| 53 ports_(ports) { | 55 ports_(ports) { |
| 54 } | 56 } |
| 55 | 57 |
| 56 SdchManager::Dictionary::~Dictionary() { | 58 SdchManager::Dictionary::~Dictionary() { |
| 57 } | 59 } |
| 58 | 60 |
| 59 bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) { | 61 SdchManager::ProblemCodes SdchManager::Dictionary::CanAdvertise( |
| 62 const GURL& target_url) const { | |
| 60 /* The specific rules of when a dictionary should be advertised in an | 63 /* The specific rules of when a dictionary should be advertised in an |
| 61 Avail-Dictionary header are modeled after the rules for cookie scoping. The | 64 Avail-Dictionary header are modeled after the rules for cookie scoping. The |
| 62 terms "domain-match" and "pathmatch" are defined in RFC 2965 [6]. A | 65 terms "domain-match" and "pathmatch" are defined in RFC 2965 [6]. A |
| 63 dictionary may be advertised in the Avail-Dictionaries header exactly when | 66 dictionary may be advertised in the Avail-Dictionaries header exactly when |
| 64 all of the following are true: | 67 all of the following are true: |
| 65 1. The server's effective host name domain-matches the Domain attribute of | 68 1. The server's effective host name domain-matches the Domain attribute of |
| 66 the dictionary. | 69 the dictionary. |
| 67 2. If the dictionary has a Port attribute, the request port is one of the | 70 2. If the dictionary has a Port attribute, the request port is one of the |
| 68 ports listed in the Port attribute. | 71 ports listed in the Port attribute. |
| 69 3. The request URI path-matches the path header of the dictionary. | 72 3. The request URI path-matches the path header of the dictionary. |
| 70 4. The request is not an HTTPS request. | 73 4. The request is not an HTTPS request. |
| 71 We can override (ignore) item (4) only when we have explicitly enabled | 74 We can override (ignore) item (4) only when we have explicitly enabled |
| 72 HTTPS support AND the dictionary acquisition scheme matches the target | 75 HTTPS support AND the dictionary acquisition scheme matches the target |
| 73 url scheme. | 76 url scheme. |
| 74 */ | 77 */ |
| 75 if (!DomainMatch(target_url, domain_)) | 78 if (!DomainMatch(target_url, domain_)) |
| 76 return false; | 79 return DICTIONARY_FOUND_HAS_WRONG_DOMAIN; |
| 77 if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort())) | 80 if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort())) |
| 78 return false; | 81 return DICTIONARY_FOUND_HAS_WRONG_PORT_LIST; |
| 79 if (path_.size() && !PathMatch(target_url.path(), path_)) | 82 if (path_.size() && !PathMatch(target_url.path(), path_)) |
| 80 return false; | 83 return DICTIONARY_FOUND_HAS_WRONG_PATH; |
| 81 if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure()) | 84 if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure()) |
| 82 return false; | 85 return DICTIONARY_FOUND_HAS_WRONG_SCHEME; |
| 83 if (target_url.SchemeIsSecure() != url_.SchemeIsSecure()) | 86 if (target_url.SchemeIsSecure() != url_.SchemeIsSecure()) |
| 84 return false; | 87 return DICTIONARY_FOUND_HAS_WRONG_SCHEME; |
| 85 if (base::Time::Now() > expiration_) | 88 if (base::Time::Now() > expiration_) |
| 86 return false; | 89 return DICTIONARY_FOUND_EXPIRED; |
| 87 return true; | 90 return PROBLEM_CODE_OK; |
| 88 } | 91 } |
| 89 | 92 |
| 90 //------------------------------------------------------------------------------ | 93 //------------------------------------------------------------------------------ |
| 91 // Security functions restricting loads and use of dictionaries. | 94 // Security functions restricting loads and use of dictionaries. |
| 92 | 95 |
| 93 // static | 96 // static |
| 94 bool SdchManager::Dictionary::CanSet(const std::string& domain, | 97 SdchManager::ProblemCodes SdchManager::Dictionary::CanSet( |
| 95 const std::string& path, | 98 const std::string& domain, |
| 96 const std::set<int>& ports, | 99 const std::string& path, |
| 97 const GURL& dictionary_url) { | 100 const std::set<int>& ports, |
| 101 const GURL& dictionary_url) { | |
| 98 /* | 102 /* |
| 99 A dictionary is invalid and must not be stored if any of the following are | 103 A dictionary is invalid and must not be stored if any of the following are |
| 100 true: | 104 true: |
| 101 1. The dictionary has no Domain attribute. | 105 1. The dictionary has no Domain attribute. |
| 102 2. The effective host name that derives from the referer URL host name does | 106 2. The effective host name that derives from the referer URL host name does |
| 103 not domain-match the Domain attribute. | 107 not domain-match the Domain attribute. |
| 104 3. The Domain attribute is a top level domain. | 108 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 | 109 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 | 110 form HD, where D is the value of the Domain attribute, and H is a string |
| 107 that contains one or more dots. | 111 that contains one or more dots. |
| 108 5. If the dictionary has a Port attribute and the referer URL's port was not | 112 5. If the dictionary has a Port attribute and the referer URL's port was not |
| 109 in the list. | 113 in the list. |
| 110 */ | 114 */ |
| 111 | 115 |
| 112 // TODO(jar): Redirects in dictionary fetches might plausibly be problematic, | 116 // TODO(jar): Redirects in dictionary fetches might plausibly be problematic, |
| 113 // and hence the conservative approach is to not allow any redirects (if there | 117 // 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). | 118 // were any... then don't allow the dictionary to be set). |
| 115 | 119 |
| 116 if (domain.empty()) { | 120 if (domain.empty()) |
| 117 SdchErrorRecovery(DICTIONARY_MISSING_DOMAIN_SPECIFIER); | 121 return DICTIONARY_MISSING_DOMAIN_SPECIFIER; // Domain is required. |
| 118 return false; // Domain is required. | 122 |
| 119 } | |
| 120 if (registry_controlled_domains::GetDomainAndRegistry( | 123 if (registry_controlled_domains::GetDomainAndRegistry( |
| 121 domain, | 124 domain, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) |
| 122 registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES).empty()) { | 125 .empty()) |
| 123 SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN); | 126 return DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN; // domain was a TLD. |
| 124 return false; // domain was a TLD. | 127 |
| 125 } | 128 if (!Dictionary::DomainMatch(dictionary_url, domain)) |
| 126 if (!Dictionary::DomainMatch(dictionary_url, domain)) { | 129 return DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL; |
| 127 SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL); | |
| 128 return false; | |
| 129 } | |
| 130 | 130 |
| 131 std::string referrer_url_host = dictionary_url.host(); | 131 std::string referrer_url_host = dictionary_url.host(); |
| 132 size_t postfix_domain_index = referrer_url_host.rfind(domain); | 132 size_t postfix_domain_index = referrer_url_host.rfind(domain); |
| 133 // See if it is indeed a postfix, or just an internal string. | 133 // See if it is indeed a postfix, or just an internal string. |
| 134 if (referrer_url_host.size() == postfix_domain_index + domain.size()) { | 134 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. | 135 // 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('.'); | 136 size_t end_of_host_index = referrer_url_host.find_first_of('.'); |
| 137 if (referrer_url_host.npos != end_of_host_index && | 137 if (referrer_url_host.npos != end_of_host_index && |
| 138 end_of_host_index < postfix_domain_index) { | 138 end_of_host_index < postfix_domain_index) |
| 139 SdchErrorRecovery(DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX); | 139 return DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX; |
| 140 return false; | |
| 141 } | |
| 142 } | 140 } |
| 143 | 141 |
| 144 if (!ports.empty() | 142 if (!ports.empty() && 0 == ports.count(dictionary_url.EffectiveIntPort())) |
| 145 && 0 == ports.count(dictionary_url.EffectiveIntPort())) { | 143 return DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL; |
| 146 SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL); | 144 |
| 147 return false; | 145 return PROBLEM_CODE_OK; |
| 148 } | |
| 149 return true; | |
| 150 } | 146 } |
| 151 | 147 |
| 152 // static | 148 SdchManager::ProblemCodes SdchManager::Dictionary::CanUse( |
| 153 bool SdchManager::Dictionary::CanUse(const GURL& referring_url) { | 149 const GURL& referring_url) const { |
| 154 /* | 150 /* |
| 155 1. The request URL's host name domain-matches the Domain attribute of the | 151 1. The request URL's host name domain-matches the Domain attribute of the |
| 156 dictionary. | 152 dictionary. |
| 157 2. If the dictionary has a Port attribute, the request port is one of the | 153 2. If the dictionary has a Port attribute, the request port is one of the |
| 158 ports listed in the Port attribute. | 154 ports listed in the Port attribute. |
| 159 3. The request URL path-matches the path attribute of the dictionary. | 155 3. The request URL path-matches the path attribute of the dictionary. |
| 160 4. The request is not an HTTPS request. | 156 4. The request is not an HTTPS request. |
| 161 We can override (ignore) item (4) only when we have explicitly enabled | 157 We can override (ignore) item (4) only when we have explicitly enabled |
| 162 HTTPS support AND the dictionary acquisition scheme matches the target | 158 HTTPS support AND the dictionary acquisition scheme matches the target |
| 163 url scheme. | 159 url scheme. |
| 164 */ | 160 */ |
| 165 if (!DomainMatch(referring_url, domain_)) { | 161 if (!DomainMatch(referring_url, domain_)) |
| 166 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_DOMAIN); | 162 return DICTIONARY_FOUND_HAS_WRONG_DOMAIN; |
| 167 return false; | 163 |
| 168 } | 164 if (!ports_.empty() && 0 == ports_.count(referring_url.EffectiveIntPort())) |
| 169 if (!ports_.empty() | 165 return DICTIONARY_FOUND_HAS_WRONG_PORT_LIST; |
| 170 && 0 == ports_.count(referring_url.EffectiveIntPort())) { | 166 |
| 171 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PORT_LIST); | 167 if (path_.size() && !PathMatch(referring_url.path(), path_)) |
| 172 return false; | 168 return DICTIONARY_FOUND_HAS_WRONG_PATH; |
| 173 } | 169 |
| 174 if (path_.size() && !PathMatch(referring_url.path(), path_)) { | 170 if (!SdchManager::secure_scheme_supported() && referring_url.SchemeIsSecure()) |
| 175 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PATH); | 171 return DICTIONARY_FOUND_HAS_WRONG_SCHEME; |
| 176 return false; | 172 |
| 177 } | 173 if (referring_url.SchemeIsSecure() != url_.SchemeIsSecure()) |
| 178 if (!SdchManager::secure_scheme_supported() && | 174 return DICTIONARY_FOUND_HAS_WRONG_SCHEME; |
| 179 referring_url.SchemeIsSecure()) { | |
| 180 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME); | |
| 181 return false; | |
| 182 } | |
| 183 if (referring_url.SchemeIsSecure() != url_.SchemeIsSecure()) { | |
| 184 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME); | |
| 185 return false; | |
| 186 } | |
| 187 | 175 |
| 188 // TODO(jar): Remove overly restrictive failsafe test (added per security | 176 // TODO(jar): Remove overly restrictive failsafe test (added per security |
| 189 // review) when we have a need to be more general. | 177 // review) when we have a need to be more general. |
| 190 if (!referring_url.SchemeIsHTTPOrHTTPS()) { | 178 if (!referring_url.SchemeIsHTTPOrHTTPS()) |
| 191 SdchErrorRecovery(ATTEMPT_TO_DECODE_NON_HTTP_DATA); | 179 return ATTEMPT_TO_DECODE_NON_HTTP_DATA; |
| 192 return false; | |
| 193 } | |
| 194 | 180 |
| 195 return true; | 181 return PROBLEM_CODE_OK; |
| 196 } | 182 } |
| 197 | 183 |
| 184 // static | |
| 198 bool SdchManager::Dictionary::PathMatch(const std::string& path, | 185 bool SdchManager::Dictionary::PathMatch(const std::string& path, |
| 199 const std::string& restriction) { | 186 const std::string& restriction) { |
| 200 /* Must be either: | 187 /* Must be either: |
| 201 1. P2 is equal to P1 | 188 1. P2 is equal to P1 |
| 202 2. P2 is a prefix of P1 and either the final character in P2 is "/" or the | 189 2. P2 is a prefix of P1 and either the final character in P2 is "/" or the |
| 203 character following P2 in P1 is "/". | 190 character following P2 in P1 is "/". |
| 204 */ | 191 */ |
| 205 if (path == restriction) | 192 if (path == restriction) |
| 206 return true; | 193 return true; |
| 207 size_t prefix_length = restriction.size(); | 194 size_t prefix_length = restriction.size(); |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 299 } | 286 } |
| 300 | 287 |
| 301 void SdchManager::ClearBlacklistings() { | 288 void SdchManager::ClearBlacklistings() { |
| 302 blacklisted_domains_.clear(); | 289 blacklisted_domains_.clear(); |
| 303 } | 290 } |
| 304 | 291 |
| 305 void SdchManager::ClearDomainBlacklisting(const std::string& domain) { | 292 void SdchManager::ClearDomainBlacklisting(const std::string& domain) { |
| 306 BlacklistInfo* blacklist_info = &blacklisted_domains_[ | 293 BlacklistInfo* blacklist_info = &blacklisted_domains_[ |
| 307 base::StringToLowerASCII(domain)]; | 294 base::StringToLowerASCII(domain)]; |
| 308 blacklist_info->count = 0; | 295 blacklist_info->count = 0; |
| 309 blacklist_info->reason = MIN_PROBLEM_CODE; | 296 blacklist_info->reason = PROBLEM_CODE_OK; |
| 310 } | 297 } |
| 311 | 298 |
| 312 int SdchManager::BlackListDomainCount(const std::string& domain) { | 299 int SdchManager::BlackListDomainCount(const std::string& domain) { |
| 313 std::string domain_lower(base::StringToLowerASCII(domain)); | 300 std::string domain_lower(base::StringToLowerASCII(domain)); |
| 314 | 301 |
| 315 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) | 302 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) |
| 316 return 0; | 303 return 0; |
| 317 return blacklisted_domains_[domain_lower].count; | 304 return blacklisted_domains_[domain_lower].count; |
| 318 } | 305 } |
| 319 | 306 |
| 320 int SdchManager::BlacklistDomainExponential(const std::string& domain) { | 307 int SdchManager::BlacklistDomainExponential(const std::string& domain) { |
| 321 std::string domain_lower(base::StringToLowerASCII(domain)); | 308 std::string domain_lower(base::StringToLowerASCII(domain)); |
| 322 | 309 |
| 323 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) | 310 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) |
| 324 return 0; | 311 return 0; |
| 325 return blacklisted_domains_[domain_lower].exponential_count; | 312 return blacklisted_domains_[domain_lower].exponential_count; |
| 326 } | 313 } |
| 327 | 314 |
| 328 bool SdchManager::IsInSupportedDomain(const GURL& url) { | 315 SdchManager::ProblemCodes SdchManager::IsInSupportedDomain(const GURL& url) { |
| 329 DCHECK(CalledOnValidThread()); | 316 DCHECK(CalledOnValidThread()); |
| 330 if (!g_sdch_enabled_ ) | 317 if (!g_sdch_enabled_ ) |
| 331 return false; | 318 return SDCH_DISABLED; |
| 332 | 319 |
| 333 if (!secure_scheme_supported() && url.SchemeIsSecure()) | 320 if (!secure_scheme_supported() && url.SchemeIsSecure()) |
| 334 return false; | 321 return SECURE_SCHEME_NOT_SUPPORTED; |
| 335 | 322 |
| 336 if (blacklisted_domains_.empty()) | 323 if (blacklisted_domains_.empty()) |
| 337 return true; | 324 return PROBLEM_CODE_OK; |
| 338 | 325 |
| 339 DomainBlacklistInfo::iterator it = | 326 DomainBlacklistInfo::iterator it = |
| 340 blacklisted_domains_.find(base::StringToLowerASCII(url.host())); | 327 blacklisted_domains_.find(base::StringToLowerASCII(url.host())); |
| 341 if (blacklisted_domains_.end() == it || it->second.count == 0) | 328 if (blacklisted_domains_.end() == it || it->second.count == 0) |
| 342 return true; | 329 return PROBLEM_CODE_OK; |
| 343 | 330 |
| 344 UMA_HISTOGRAM_ENUMERATION("Sdch3.BlacklistReason", it->second.reason, | 331 UMA_HISTOGRAM_ENUMERATION("Sdch3.BlacklistReason", it->second.reason, |
| 345 MAX_PROBLEM_CODE); | 332 MAX_PROBLEM_CODE); |
| 346 SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET); | |
|
Randy Smith (Not in Mondays)
2014/09/18 20:55:51
Just to summarize what I think the refactor of thi
baranovich
2014/09/19 12:42:44
Got your point, although GetVcdiffDictionary/AddSd
baranovich
2014/09/30 13:16:51
Done. I've restored the frequency if the blacklist
| |
| 347 | 333 |
| 348 int count = it->second.count - 1; | 334 int count = it->second.count - 1; |
| 349 if (count > 0) { | 335 if (count > 0) { |
| 350 it->second.count = count; | 336 it->second.count = count; |
| 351 } else { | 337 } else { |
| 352 it->second.count = 0; | 338 it->second.count = 0; |
| 353 it->second.reason = MIN_PROBLEM_CODE; | 339 it->second.reason = PROBLEM_CODE_OK; |
| 354 } | 340 } |
| 355 | 341 |
| 356 return false; | 342 return DOMAIN_BLACKLIST_INCLUDES_TARGET; |
| 357 } | 343 } |
| 358 | 344 |
| 359 void SdchManager::FetchDictionary(const GURL& request_url, | 345 SdchManager::ProblemCodes SdchManager::FetchDictionary( |
| 360 const GURL& dictionary_url) { | 346 const GURL& request_url, |
| 347 const GURL& dictionary_url) { | |
| 361 DCHECK(CalledOnValidThread()); | 348 DCHECK(CalledOnValidThread()); |
| 362 if (CanFetchDictionary(request_url, dictionary_url) && fetcher_.get()) { | 349 ProblemCodes rv = CanFetchDictionary(request_url, dictionary_url); |
| 350 if (rv != PROBLEM_CODE_OK) | |
| 351 return rv; | |
| 352 | |
| 353 if (fetcher_.get()) { | |
| 363 ++fetches_count_for_testing_; | 354 ++fetches_count_for_testing_; |
| 364 fetcher_->Schedule(dictionary_url); | 355 SdchFetcher::ScheduleResult result = fetcher_->Schedule(dictionary_url); |
| 356 switch (result) { | |
| 357 case SdchFetcher::ALREADY_SCHEDULED: | |
| 358 rv = DICTIONARY_ALREADY_SCHEDULED_TO_DOWNLOAD; | |
| 359 break; | |
| 360 case SdchFetcher::ALREADY_TRIED: | |
| 361 rv = DICTIONARY_ALREADY_TRIED_TO_DOWNLOAD; | |
| 362 break; | |
| 363 default: | |
| 364 DCHECK_EQ(SdchFetcher::SCHEDULE_OK, result); | |
| 365 break; | |
| 366 } | |
| 365 } | 367 } |
| 368 | |
| 369 return rv; | |
| 366 } | 370 } |
| 367 | 371 |
| 368 bool SdchManager::CanFetchDictionary(const GURL& referring_url, | 372 SdchManager::ProblemCodes SdchManager::CanFetchDictionary( |
| 369 const GURL& dictionary_url) const { | 373 const GURL& referring_url, |
| 374 const GURL& dictionary_url) const { | |
| 370 DCHECK(CalledOnValidThread()); | 375 DCHECK(CalledOnValidThread()); |
| 371 /* The user agent may retrieve a dictionary from the dictionary URL if all of | 376 /* The user agent may retrieve a dictionary from the dictionary URL if all of |
| 372 the following are true: | 377 the following are true: |
| 373 1 The dictionary URL host name matches the referrer URL host name and | 378 1 The dictionary URL host name matches the referrer URL host name and |
| 374 scheme. | 379 scheme. |
| 375 2 The dictionary URL host name domain matches the parent domain of the | 380 2 The dictionary URL host name domain matches the parent domain of the |
| 376 referrer URL host name | 381 referrer URL host name |
| 377 3 The parent domain of the referrer URL host name is not a top level | 382 3 The parent domain of the referrer URL host name is not a top level |
| 378 domain | 383 domain |
| 379 4 The dictionary URL is not an HTTPS URL. | 384 4 The dictionary URL is not an HTTPS URL. |
| 380 */ | 385 */ |
| 381 // Item (1) above implies item (2). Spec should be updated. | 386 // Item (1) above implies item (2). Spec should be updated. |
| 382 // I take "host name match" to be "is identical to" | 387 // I take "host name match" to be "is identical to" |
| 383 if (referring_url.host() != dictionary_url.host() || | 388 if (referring_url.host() != dictionary_url.host() || |
| 384 referring_url.scheme() != dictionary_url.scheme()) { | 389 referring_url.scheme() != dictionary_url.scheme()) |
| 385 SdchErrorRecovery(DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST); | 390 return DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST; |
| 386 return false; | 391 |
| 387 } | 392 if (!secure_scheme_supported() && referring_url.SchemeIsSecure()) |
| 388 if (!secure_scheme_supported() && referring_url.SchemeIsSecure()) { | 393 return DICTIONARY_SELECTED_FOR_SSL; |
| 389 SdchErrorRecovery(DICTIONARY_SELECTED_FOR_SSL); | |
| 390 return false; | |
| 391 } | |
| 392 | 394 |
| 393 // TODO(jar): Remove this failsafe conservative hack which is more restrictive | 395 // TODO(jar): Remove this failsafe conservative hack which is more restrictive |
| 394 // than current SDCH spec when needed, and justified by security audit. | 396 // than current SDCH spec when needed, and justified by security audit. |
| 395 if (!referring_url.SchemeIsHTTPOrHTTPS()) { | 397 if (!referring_url.SchemeIsHTTPOrHTTPS()) |
| 396 SdchErrorRecovery(DICTIONARY_SELECTED_FROM_NON_HTTP); | 398 return DICTIONARY_SELECTED_FROM_NON_HTTP; |
| 397 return false; | |
| 398 } | |
| 399 | 399 |
| 400 return true; | 400 return PROBLEM_CODE_OK; |
| 401 } | 401 } |
| 402 | 402 |
| 403 void SdchManager::GetVcdiffDictionary( | 403 SdchManager::ProblemCodes SdchManager::GetVcdiffDictionary( |
| 404 const std::string& server_hash, | 404 const std::string& server_hash, |
| 405 const GURL& referring_url, | 405 const GURL& referring_url, |
| 406 scoped_refptr<Dictionary>* dictionary) { | 406 scoped_refptr<Dictionary>* dictionary) { |
| 407 DCHECK(CalledOnValidThread()); | 407 DCHECK(CalledOnValidThread()); |
| 408 *dictionary = NULL; | 408 *dictionary = NULL; |
| 409 DictionaryMap::iterator it = dictionaries_.find(server_hash); | 409 DictionaryMap::iterator it = dictionaries_.find(server_hash); |
| 410 if (it == dictionaries_.end()) { | 410 if (it == dictionaries_.end()) |
| 411 return; | 411 return PROBLEM_CODE_OK; // not a problem. Just no dictionary. |
| 412 } | 412 |
| 413 scoped_refptr<Dictionary> matching_dictionary = it->second; | 413 scoped_refptr<Dictionary> matching_dictionary = it->second; |
| 414 if (!IsInSupportedDomain(referring_url)) | 414 |
| 415 return; | 415 ProblemCodes rv = IsInSupportedDomain(referring_url); |
| 416 if (!matching_dictionary->CanUse(referring_url)) | 416 if (rv != PROBLEM_CODE_OK) |
| 417 return; | 417 return rv; |
| 418 *dictionary = matching_dictionary; | 418 |
| 419 rv = matching_dictionary->CanUse(referring_url); | |
| 420 if (rv == PROBLEM_CODE_OK) | |
| 421 *dictionary = matching_dictionary; | |
| 422 return rv; | |
| 419 } | 423 } |
| 420 | 424 |
| 421 // TODO(jar): If we have evictions from the dictionaries_, then we need to | 425 // 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 | 426 // change this interface to return a list of reference counted Dictionary |
| 423 // instances that can be used if/when a server specifies one. | 427 // instances that can be used if/when a server specifies one. |
| 424 void SdchManager::GetAvailDictionaryList(const GURL& target_url, | 428 void SdchManager::GetAvailDictionaryList(const GURL& target_url, |
| 425 std::string* list) { | 429 std::string* list) { |
| 426 DCHECK(CalledOnValidThread()); | 430 DCHECK(CalledOnValidThread()); |
| 427 int count = 0; | 431 int count = 0; |
| 428 for (DictionaryMap::iterator it = dictionaries_.begin(); | 432 for (DictionaryMap::iterator it = dictionaries_.begin(); |
| 429 it != dictionaries_.end(); ++it) { | 433 it != dictionaries_.end(); ++it) { |
| 430 if (!IsInSupportedDomain(target_url)) | 434 if (IsInSupportedDomain(target_url) != PROBLEM_CODE_OK) |
| 431 continue; | 435 continue; |
| 432 if (!it->second->CanAdvertise(target_url)) | 436 if (it->second->CanAdvertise(target_url) != PROBLEM_CODE_OK) |
| 433 continue; | 437 continue; |
| 434 ++count; | 438 ++count; |
| 435 if (!list->empty()) | 439 if (!list->empty()) |
| 436 list->append(","); | 440 list->append(","); |
| 437 list->append(it->second->client_hash()); | 441 list->append(it->second->client_hash()); |
| 438 } | 442 } |
| 439 // Watch to see if we have corrupt or numerous dictionaries. | 443 // Watch to see if we have corrupt or numerous dictionaries. |
| 440 if (count > 0) | 444 if (count > 0) |
| 441 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); | 445 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); |
| 442 } | 446 } |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 472 return; | 476 return; |
| 473 } | 477 } |
| 474 ExperimentSet::iterator it = allow_latency_experiment_.find(url.host()); | 478 ExperimentSet::iterator it = allow_latency_experiment_.find(url.host()); |
| 475 if (allow_latency_experiment_.end() == it) | 479 if (allow_latency_experiment_.end() == it) |
| 476 return; // It was already erased, or never allowed. | 480 return; // It was already erased, or never allowed. |
| 477 SdchErrorRecovery(LATENCY_TEST_DISALLOWED); | 481 SdchErrorRecovery(LATENCY_TEST_DISALLOWED); |
| 478 allow_latency_experiment_.erase(it); | 482 allow_latency_experiment_.erase(it); |
| 479 } | 483 } |
| 480 | 484 |
| 481 void SdchManager::AddSdchDictionary(const std::string& dictionary_text, | 485 void SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
| 482 const GURL& dictionary_url) { | 486 const GURL& dictionary_url, |
| 487 const BoundNetLog& net_log) { | |
| 483 DCHECK(CalledOnValidThread()); | 488 DCHECK(CalledOnValidThread()); |
| 484 std::string client_hash; | 489 std::string client_hash; |
| 485 std::string server_hash; | 490 std::string server_hash; |
| 486 GenerateHash(dictionary_text, &client_hash, &server_hash); | 491 GenerateHash(dictionary_text, &client_hash, &server_hash); |
| 487 if (dictionaries_.find(server_hash) != dictionaries_.end()) { | 492 if (dictionaries_.find(server_hash) != dictionaries_.end()) { |
| 488 SdchErrorRecovery(DICTIONARY_ALREADY_LOADED); | 493 LogDictionaryError( |
| 494 DICTIONARY_ALREADY_LOADED, dictionary_url, true, net_log); | |
| 489 return; // Already loaded. | 495 return; // Already loaded. |
| 490 } | 496 } |
| 491 | 497 |
| 492 std::string domain, path; | 498 std::string domain, path; |
| 493 std::set<int> ports; | 499 std::set<int> ports; |
| 494 base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30)); | 500 base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30)); |
| 495 | 501 |
| 496 if (dictionary_text.empty()) { | 502 if (dictionary_text.empty()) { |
| 497 SdchErrorRecovery(DICTIONARY_HAS_NO_TEXT); | 503 LogDictionaryError(DICTIONARY_HAS_NO_TEXT, dictionary_url, true, net_log); |
| 498 return; // Missing header. | 504 return; // Missing header. |
| 499 } | 505 } |
| 500 | 506 |
| 501 size_t header_end = dictionary_text.find("\n\n"); | 507 size_t header_end = dictionary_text.find("\n\n"); |
| 502 if (std::string::npos == header_end) { | 508 if (std::string::npos == header_end) { |
| 503 SdchErrorRecovery(DICTIONARY_HAS_NO_HEADER); | 509 LogDictionaryError(DICTIONARY_HAS_NO_HEADER, dictionary_url, true, net_log); |
| 504 return; // Missing header. | 510 return; // Missing header. |
| 505 } | 511 } |
| 506 size_t line_start = 0; // Start of line being parsed. | 512 size_t line_start = 0; // Start of line being parsed. |
| 507 while (1) { | 513 while (1) { |
| 508 size_t line_end = dictionary_text.find('\n', line_start); | 514 size_t line_end = dictionary_text.find('\n', line_start); |
| 509 DCHECK(std::string::npos != line_end); | 515 DCHECK(std::string::npos != line_end); |
| 510 DCHECK_LE(line_end, header_end); | 516 DCHECK_LE(line_end, header_end); |
| 511 | 517 |
| 512 size_t colon_index = dictionary_text.find(':', line_start); | 518 size_t colon_index = dictionary_text.find(':', line_start); |
| 513 if (std::string::npos == colon_index) { | 519 if (std::string::npos == colon_index) { |
| 514 SdchErrorRecovery(DICTIONARY_HEADER_LINE_MISSING_COLON); | 520 LogDictionaryError( |
| 521 DICTIONARY_HEADER_LINE_MISSING_COLON, dictionary_url, true, net_log); | |
| 515 return; // Illegal line missing a colon. | 522 return; // Illegal line missing a colon. |
| 516 } | 523 } |
| 517 | 524 |
| 518 if (colon_index > line_end) | 525 if (colon_index > line_end) |
| 519 break; | 526 break; |
| 520 | 527 |
| 521 size_t value_start = dictionary_text.find_first_not_of(" \t", | 528 size_t value_start = dictionary_text.find_first_not_of(" \t", |
| 522 colon_index + 1); | 529 colon_index + 1); |
| 523 if (std::string::npos != value_start) { | 530 if (std::string::npos != value_start) { |
| 524 if (value_start >= line_end) | 531 if (value_start >= line_end) |
| 525 break; | 532 break; |
| 526 std::string name(dictionary_text, line_start, colon_index - line_start); | 533 std::string name(dictionary_text, line_start, colon_index - line_start); |
| 527 std::string value(dictionary_text, value_start, line_end - value_start); | 534 std::string value(dictionary_text, value_start, line_end - value_start); |
| 528 name = base::StringToLowerASCII(name); | 535 name = base::StringToLowerASCII(name); |
| 529 if (name == "domain") { | 536 if (name == "domain") { |
| 530 domain = value; | 537 domain = value; |
| 531 } else if (name == "path") { | 538 } else if (name == "path") { |
| 532 path = value; | 539 path = value; |
| 533 } else if (name == "format-version") { | 540 } else if (name == "format-version") { |
| 534 if (value != "1.0") | 541 if (value != "1.0") |
| 542 LogDictionaryError( | |
| 543 UNSUPPORTED_VERSION, dictionary_url, true, net_log); | |
| 535 return; | 544 return; |
| 536 } else if (name == "max-age") { | 545 } else if (name == "max-age") { |
| 537 int64 seconds; | 546 int64 seconds; |
| 538 base::StringToInt64(value, &seconds); | 547 base::StringToInt64(value, &seconds); |
| 539 expiration = base::Time::Now() + base::TimeDelta::FromSeconds(seconds); | 548 expiration = base::Time::Now() + base::TimeDelta::FromSeconds(seconds); |
| 540 } else if (name == "port") { | 549 } else if (name == "port") { |
| 541 int port; | 550 int port; |
| 542 base::StringToInt(value, &port); | 551 base::StringToInt(value, &port); |
| 543 if (port >= 0) | 552 if (port >= 0) |
| 544 ports.insert(port); | 553 ports.insert(port); |
| 545 } | 554 } |
| 546 } | 555 } |
| 547 | 556 |
| 548 if (line_end >= header_end) | 557 if (line_end >= header_end) |
| 549 break; | 558 break; |
| 550 line_start = line_end + 1; | 559 line_start = line_end + 1; |
| 551 } | 560 } |
| 552 | 561 |
| 553 if (!IsInSupportedDomain(dictionary_url)) | 562 ProblemCodes rv = IsInSupportedDomain(dictionary_url); |
| 563 if (rv != PROBLEM_CODE_OK) { | |
| 564 LogDictionaryError(rv, dictionary_url, true, net_log); | |
| 554 return; | 565 return; |
| 566 } | |
| 555 | 567 |
| 556 if (!Dictionary::CanSet(domain, path, ports, dictionary_url)) | 568 rv = Dictionary::CanSet(domain, path, ports, dictionary_url); |
| 569 if (rv != PROBLEM_CODE_OK) { | |
| 570 LogDictionaryError(rv, dictionary_url, true, net_log); | |
| 557 return; | 571 return; |
| 572 } | |
| 558 | 573 |
| 559 // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of | 574 // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of |
| 560 // useless dictionaries. We should probably have a cache eviction plan, | 575 // useless dictionaries. We should probably have a cache eviction plan, |
| 561 // instead of just blocking additions. For now, with the spec in flux, it | 576 // instead of just blocking additions. For now, with the spec in flux, it |
| 562 // is probably not worth doing eviction handling. | 577 // is probably not worth doing eviction handling. |
| 563 if (kMaxDictionarySize < dictionary_text.size()) { | 578 if (kMaxDictionarySize < dictionary_text.size()) { |
| 564 SdchErrorRecovery(DICTIONARY_IS_TOO_LARGE); | 579 LogDictionaryError(DICTIONARY_IS_TOO_LARGE, dictionary_url, true, net_log); |
| 565 return; | 580 return; |
| 566 } | 581 } |
| 567 if (kMaxDictionaryCount <= dictionaries_.size()) { | 582 if (kMaxDictionaryCount <= dictionaries_.size()) { |
| 568 SdchErrorRecovery(DICTIONARY_COUNT_EXCEEDED); | 583 LogDictionaryError( |
| 584 DICTIONARY_COUNT_EXCEEDED, dictionary_url, true, net_log); | |
| 569 return; | 585 return; |
| 570 } | 586 } |
| 571 | 587 |
| 572 UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size()); | 588 UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size()); |
| 573 DVLOG(1) << "Loaded dictionary with client hash " << client_hash | 589 DVLOG(1) << "Loaded dictionary with client hash " << client_hash |
| 574 << " and server hash " << server_hash; | 590 << " and server hash " << server_hash; |
| 575 Dictionary* dictionary = | 591 Dictionary* dictionary = |
| 576 new Dictionary(dictionary_text, header_end + 2, client_hash, | 592 new Dictionary(dictionary_text, header_end + 2, client_hash, |
| 577 dictionary_url, domain, path, expiration, ports); | 593 dictionary_url, domain, path, expiration, ports); |
| 578 dictionaries_[server_hash] = dictionary; | 594 dictionaries_[server_hash] = dictionary; |
| 579 return; | 595 return; |
| 580 } | 596 } |
| 581 | 597 |
| 582 // static | 598 // static |
| 583 void SdchManager::UrlSafeBase64Encode(const std::string& input, | 599 void SdchManager::UrlSafeBase64Encode(const std::string& input, |
| 584 std::string* output) { | 600 std::string* output) { |
| 585 // Since this is only done during a dictionary load, and hashes are only 8 | 601 // 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. | 602 // characters, we just do the simple fixup, rather than rewriting the encoder. |
| 587 base::Base64Encode(input, output); | 603 base::Base64Encode(input, output); |
| 588 std::replace(output->begin(), output->end(), '+', '-'); | 604 std::replace(output->begin(), output->end(), '+', '-'); |
| 589 std::replace(output->begin(), output->end(), '/', '_'); | 605 std::replace(output->begin(), output->end(), '/', '_'); |
| 590 } | 606 } |
| 591 | 607 |
| 608 base::Value* SdchManager::SdchInfoToValue() const { | |
| 609 base::DictionaryValue* value = new base::DictionaryValue(); | |
| 610 | |
| 611 value->SetBoolean("sdch_enabled", sdch_enabled()); | |
| 612 value->SetBoolean("secure_scheme_support", secure_scheme_supported()); | |
| 613 | |
| 614 base::ListValue* entry_list = new base::ListValue(); | |
| 615 for (DictionaryMap::const_iterator it = dictionaries_.begin(); | |
| 616 it != dictionaries_.end(); | |
| 617 ++it) { | |
| 618 base::DictionaryValue* entry_dict = new base::DictionaryValue(); | |
| 619 entry_dict->SetString("url", it->second->url().spec()); | |
| 620 entry_dict->SetString("client_hash", it->second->client_hash()); | |
| 621 entry_dict->SetString("domain", it->second->domain()); | |
| 622 entry_dict->SetString("path", it->second->path()); | |
| 623 base::ListValue* port_list = new base::ListValue(); | |
| 624 for (std::set<int>::const_iterator port_it = it->second->ports().begin(); | |
| 625 port_it != it->second->ports().end(); | |
| 626 ++port_it) { | |
| 627 port_list->AppendInteger(*port_it); | |
| 628 } | |
| 629 entry_dict->Set("ports", port_list); | |
| 630 entry_dict->SetString("server_hash", it->first); | |
| 631 entry_list->Append(entry_dict); | |
| 632 } | |
| 633 value->Set("dictionaries", entry_list); | |
| 634 | |
| 635 entry_list = new base::ListValue(); | |
| 636 for (DomainBlacklistInfo::const_iterator it = blacklisted_domains_.begin(); | |
| 637 it != blacklisted_domains_.end(); | |
| 638 ++it) { | |
| 639 if (it->second.count == 0) | |
| 640 continue; | |
| 641 base::DictionaryValue* entry_dict = new base::DictionaryValue(); | |
| 642 entry_dict->SetString("domain", it->first); | |
| 643 if (it->second.count != INT_MAX) | |
| 644 entry_dict->SetInteger("tries", it->second.count); | |
| 645 entry_dict->SetInteger("reason", it->second.reason); | |
| 646 entry_list->Append(entry_dict); | |
| 647 } | |
| 648 value->Set("blacklisted", entry_list); | |
| 649 | |
| 650 return value; | |
| 651 } | |
| 652 | |
| 653 // static | |
|
Randy Smith (Not in Mondays)
2014/09/18 20:55:51
Why not make this a file static or put it in an an
| |
| 654 void SdchManager::LogDictionaryError(ProblemCodes error, | |
| 655 const GURL& url, | |
| 656 bool is_error, | |
| 657 const BoundNetLog& net_log) { | |
| 658 SdchErrorRecovery(error); | |
| 659 net_log.AddEvent( | |
| 660 NetLog::TYPE_SDCH_DICTIONARY_FETCH_ERROR, | |
| 661 base::Bind( | |
| 662 &NetLogSdchDictionaryFetchProblemCallback, error, &url, is_error)); | |
| 663 } | |
| 664 | |
| 592 } // namespace net | 665 } // namespace net |
| OLD | NEW |