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 "crypto/sha2.h" | 12 #include "crypto/sha2.h" |
13 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | 13 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
14 #include "net/url_request/url_request_http_job.h" | 14 #include "net/url_request/url_request_http_job.h" |
15 | 15 |
16 namespace net { | 16 namespace net { |
17 | 17 |
18 //------------------------------------------------------------------------------ | 18 //------------------------------------------------------------------------------ |
19 // static | 19 // static |
20 const size_t SdchManager::kMaxDictionarySize = 1000000; | 20 const size_t SdchManager::kMaxDictionarySize = 1000000; |
21 | 21 |
22 // static | 22 // static |
23 const size_t SdchManager::kMaxDictionaryCount = 20; | 23 const size_t SdchManager::kMaxDictionaryCount = 20; |
24 | 24 |
25 // static | 25 // static |
26 SdchManager* SdchManager::global_ = NULL; | |
27 | |
28 // static | |
29 bool SdchManager::g_sdch_enabled_ = true; | 26 bool SdchManager::g_sdch_enabled_ = true; |
30 | 27 |
31 // static | 28 // static |
32 bool SdchManager::g_secure_scheme_supported_ = false; | 29 bool SdchManager::g_secure_scheme_supported_ = false; |
33 | 30 |
34 //------------------------------------------------------------------------------ | 31 //------------------------------------------------------------------------------ |
35 SdchManager::Dictionary::Dictionary(const std::string& dictionary_text, | 32 SdchManager::Dictionary::Dictionary(const std::string& dictionary_text, |
36 size_t offset, | 33 size_t offset, |
37 const std::string& client_hash, | 34 const std::string& client_hash, |
38 const GURL& gurl, | 35 const GURL& gurl, |
39 const std::string& domain, | 36 const std::string& domain, |
40 const std::string& path, | 37 const std::string& path, |
41 const base::Time& expiration, | 38 const base::Time& expiration, |
42 const std::set<int>& ports) | 39 const std::set<int>& ports) |
43 : text_(dictionary_text, offset), | 40 : text_(dictionary_text, offset), |
44 client_hash_(client_hash), | 41 client_hash_(client_hash), |
45 url_(gurl), | 42 url_(gurl), |
46 domain_(domain), | 43 domain_(domain), |
47 path_(path), | 44 path_(path), |
48 expiration_(expiration), | 45 expiration_(expiration), |
49 ports_(ports) { | 46 ports_(ports) { |
50 } | 47 } |
51 | 48 |
52 SdchManager::Dictionary::~Dictionary() { | 49 SdchManager::Dictionary::~Dictionary() { |
53 } | 50 } |
54 | 51 |
55 bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) { | 52 bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) { |
56 if (!SdchManager::Global()->IsInSupportedDomain(target_url)) | |
57 return false; | |
58 /* The specific rules of when a dictionary should be advertised in an | 53 /* The specific rules of when a dictionary should be advertised in an |
59 Avail-Dictionary header are modeled after the rules for cookie scoping. The | 54 Avail-Dictionary header are modeled after the rules for cookie scoping. The |
60 terms "domain-match" and "pathmatch" are defined in RFC 2965 [6]. A | 55 terms "domain-match" and "pathmatch" are defined in RFC 2965 [6]. A |
61 dictionary may be advertised in the Avail-Dictionaries header exactly when | 56 dictionary may be advertised in the Avail-Dictionaries header exactly when |
62 all of the following are true: | 57 all of the following are true: |
63 1. The server's effective host name domain-matches the Domain attribute of | 58 1. The server's effective host name domain-matches the Domain attribute of |
64 the dictionary. | 59 the dictionary. |
65 2. If the dictionary has a Port attribute, the request port is one of the | 60 2. If the dictionary has a Port attribute, the request port is one of the |
66 ports listed in the Port attribute. | 61 ports listed in the Port attribute. |
67 3. The request URI path-matches the path header of the dictionary. | 62 3. The request URI path-matches the path header of the dictionary. |
(...skipping 17 matching lines...) Expand all Loading... |
85 } | 80 } |
86 | 81 |
87 //------------------------------------------------------------------------------ | 82 //------------------------------------------------------------------------------ |
88 // Security functions restricting loads and use of dictionaries. | 83 // Security functions restricting loads and use of dictionaries. |
89 | 84 |
90 // static | 85 // static |
91 bool SdchManager::Dictionary::CanSet(const std::string& domain, | 86 bool SdchManager::Dictionary::CanSet(const std::string& domain, |
92 const std::string& path, | 87 const std::string& path, |
93 const std::set<int>& ports, | 88 const std::set<int>& ports, |
94 const GURL& dictionary_url) { | 89 const GURL& dictionary_url) { |
95 if (!SdchManager::Global()->IsInSupportedDomain(dictionary_url)) | |
96 return false; | |
97 /* | 90 /* |
98 A dictionary is invalid and must not be stored if any of the following are | 91 A dictionary is invalid and must not be stored if any of the following are |
99 true: | 92 true: |
100 1. The dictionary has no Domain attribute. | 93 1. The dictionary has no Domain attribute. |
101 2. The effective host name that derives from the referer URL host name does | 94 2. The effective host name that derives from the referer URL host name does |
102 not domain-match the Domain attribute. | 95 not domain-match the Domain attribute. |
103 3. The Domain attribute is a top level domain. | 96 3. The Domain attribute is a top level domain. |
104 4. The referer URL host is a host domain name (not IP address) and has the | 97 4. The referer URL host is a host domain name (not IP address) and has the |
105 form HD, where D is the value of the Domain attribute, and H is a string | 98 form HD, where D is the value of the Domain attribute, and H is a string |
106 that contains one or more dots. | 99 that contains one or more dots. |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
143 if (!ports.empty() | 136 if (!ports.empty() |
144 && 0 == ports.count(dictionary_url.EffectiveIntPort())) { | 137 && 0 == ports.count(dictionary_url.EffectiveIntPort())) { |
145 SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL); | 138 SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL); |
146 return false; | 139 return false; |
147 } | 140 } |
148 return true; | 141 return true; |
149 } | 142 } |
150 | 143 |
151 // static | 144 // static |
152 bool SdchManager::Dictionary::CanUse(const GURL& referring_url) { | 145 bool SdchManager::Dictionary::CanUse(const GURL& referring_url) { |
153 if (!SdchManager::Global()->IsInSupportedDomain(referring_url)) | |
154 return false; | |
155 /* | 146 /* |
156 1. The request URL's host name domain-matches the Domain attribute of the | 147 1. The request URL's host name domain-matches the Domain attribute of the |
157 dictionary. | 148 dictionary. |
158 2. If the dictionary has a Port attribute, the request port is one of the | 149 2. If the dictionary has a Port attribute, the request port is one of the |
159 ports listed in the Port attribute. | 150 ports listed in the Port attribute. |
160 3. The request URL path-matches the path attribute of the dictionary. | 151 3. The request URL path-matches the path attribute of the dictionary. |
161 4. The request is not an HTTPS request. | 152 4. The request is not an HTTPS request. |
162 We can override (ignore) item (4) only when we have explicitly enabled | 153 We can override (ignore) item (4) only when we have explicitly enabled |
163 HTTPS support AND dictionary has been acquired over HTTPS. | 154 HTTPS support AND dictionary has been acquired over HTTPS. |
164 */ | 155 */ |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
214 | 205 |
215 // static | 206 // static |
216 bool SdchManager::Dictionary::DomainMatch(const GURL& gurl, | 207 bool SdchManager::Dictionary::DomainMatch(const GURL& gurl, |
217 const std::string& restriction) { | 208 const std::string& restriction) { |
218 // TODO(jar): This is not precisely a domain match definition. | 209 // TODO(jar): This is not precisely a domain match definition. |
219 return gurl.DomainIs(restriction.data(), restriction.size()); | 210 return gurl.DomainIs(restriction.data(), restriction.size()); |
220 } | 211 } |
221 | 212 |
222 //------------------------------------------------------------------------------ | 213 //------------------------------------------------------------------------------ |
223 SdchManager::SdchManager() { | 214 SdchManager::SdchManager() { |
224 DCHECK(!global_); | |
225 DCHECK(CalledOnValidThread()); | 215 DCHECK(CalledOnValidThread()); |
226 global_ = this; | |
227 } | 216 } |
228 | 217 |
229 SdchManager::~SdchManager() { | 218 SdchManager::~SdchManager() { |
230 DCHECK_EQ(this, global_); | |
231 DCHECK(CalledOnValidThread()); | 219 DCHECK(CalledOnValidThread()); |
232 while (!dictionaries_.empty()) { | 220 while (!dictionaries_.empty()) { |
233 DictionaryMap::iterator it = dictionaries_.begin(); | 221 DictionaryMap::iterator it = dictionaries_.begin(); |
234 it->second->Release(); | 222 it->second->Release(); |
235 dictionaries_.erase(it->first); | 223 dictionaries_.erase(it->first); |
236 } | 224 } |
237 global_ = NULL; | |
238 } | 225 } |
239 | 226 |
240 // static | 227 // static |
241 void SdchManager::Shutdown() { | |
242 EnableSdchSupport(false); | |
243 if (!global_ ) | |
244 return; | |
245 global_->set_sdch_fetcher(NULL); | |
246 } | |
247 | |
248 // static | |
249 SdchManager* SdchManager::Global() { | |
250 return global_; | |
251 } | |
252 | |
253 // static | |
254 void SdchManager::SdchErrorRecovery(ProblemCodes problem) { | 228 void SdchManager::SdchErrorRecovery(ProblemCodes problem) { |
255 UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE); | 229 UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE); |
256 } | 230 } |
257 | 231 |
258 void SdchManager::set_sdch_fetcher(SdchFetcher* fetcher) { | 232 void SdchManager::set_sdch_fetcher(SdchFetcher* fetcher) { |
259 DCHECK(CalledOnValidThread()); | 233 DCHECK(CalledOnValidThread()); |
260 fetcher_.reset(fetcher); | 234 fetcher_.reset(fetcher); |
261 } | 235 } |
262 | 236 |
263 // static | 237 // static |
264 void SdchManager::EnableSdchSupport(bool enabled) { | 238 void SdchManager::EnableSdchSupport(bool enabled) { |
265 g_sdch_enabled_ = enabled; | 239 g_sdch_enabled_ = enabled; |
266 } | 240 } |
267 | 241 |
268 // static | 242 // static |
269 void SdchManager::EnableSecureSchemeSupport(bool enabled) { | 243 void SdchManager::EnableSecureSchemeSupport(bool enabled) { |
270 g_secure_scheme_supported_ = enabled; | 244 g_secure_scheme_supported_ = enabled; |
271 } | 245 } |
272 | 246 |
273 // static | |
274 void SdchManager::BlacklistDomain(const GURL& url) { | 247 void SdchManager::BlacklistDomain(const GURL& url) { |
275 if (!global_ ) | 248 SetAllowLatencyExperiment(url, false); |
276 return; | |
277 global_->SetAllowLatencyExperiment(url, false); | |
278 | 249 |
279 std::string domain(StringToLowerASCII(url.host())); | 250 std::string domain(StringToLowerASCII(url.host())); |
280 int count = global_->blacklisted_domains_[domain]; | 251 int count = blacklisted_domains_[domain]; |
281 if (count > 0) | 252 if (count > 0) |
282 return; // Domain is already blacklisted. | 253 return; // Domain is already blacklisted. |
283 | 254 |
284 count = 1 + 2 * global_->exponential_blacklist_count[domain]; | 255 count = 1 + 2 * exponential_blacklist_count[domain]; |
285 if (count > 0) | 256 if (count > 0) |
286 global_->exponential_blacklist_count[domain] = count; | 257 exponential_blacklist_count[domain] = count; |
287 else | 258 else |
288 count = INT_MAX; | 259 count = INT_MAX; |
289 | 260 |
290 global_->blacklisted_domains_[domain] = count; | 261 blacklisted_domains_[domain] = count; |
291 } | 262 } |
292 | 263 |
293 // static | |
294 void SdchManager::BlacklistDomainForever(const GURL& url) { | 264 void SdchManager::BlacklistDomainForever(const GURL& url) { |
295 if (!global_ ) | 265 SetAllowLatencyExperiment(url, false); |
296 return; | |
297 global_->SetAllowLatencyExperiment(url, false); | |
298 | 266 |
299 std::string domain(StringToLowerASCII(url.host())); | 267 std::string domain(StringToLowerASCII(url.host())); |
300 global_->exponential_blacklist_count[domain] = INT_MAX; | 268 exponential_blacklist_count[domain] = INT_MAX; |
301 global_->blacklisted_domains_[domain] = INT_MAX; | 269 blacklisted_domains_[domain] = INT_MAX; |
302 } | 270 } |
303 | 271 |
304 // static | |
305 void SdchManager::ClearBlacklistings() { | 272 void SdchManager::ClearBlacklistings() { |
306 Global()->blacklisted_domains_.clear(); | 273 blacklisted_domains_.clear(); |
307 Global()->exponential_blacklist_count.clear(); | 274 exponential_blacklist_count.clear(); |
308 } | 275 } |
309 | 276 |
310 // static | |
311 void SdchManager::ClearDomainBlacklisting(const std::string& domain) { | 277 void SdchManager::ClearDomainBlacklisting(const std::string& domain) { |
312 Global()->blacklisted_domains_.erase(StringToLowerASCII(domain)); | 278 blacklisted_domains_.erase(StringToLowerASCII(domain)); |
313 } | 279 } |
314 | 280 |
315 // static | |
316 int SdchManager::BlackListDomainCount(const std::string& domain) { | 281 int SdchManager::BlackListDomainCount(const std::string& domain) { |
317 if (Global()->blacklisted_domains_.end() == | 282 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain)) |
318 Global()->blacklisted_domains_.find(domain)) | |
319 return 0; | 283 return 0; |
320 return Global()->blacklisted_domains_[StringToLowerASCII(domain)]; | 284 return blacklisted_domains_[StringToLowerASCII(domain)]; |
321 } | 285 } |
322 | 286 |
323 // static | |
324 int SdchManager::BlacklistDomainExponential(const std::string& domain) { | 287 int SdchManager::BlacklistDomainExponential(const std::string& domain) { |
325 if (Global()->exponential_blacklist_count.end() == | 288 if (exponential_blacklist_count.end() == |
326 Global()->exponential_blacklist_count.find(domain)) | 289 exponential_blacklist_count.find(domain)) |
327 return 0; | 290 return 0; |
328 return Global()->exponential_blacklist_count[StringToLowerASCII(domain)]; | 291 return exponential_blacklist_count[StringToLowerASCII(domain)]; |
329 } | 292 } |
330 | 293 |
331 bool SdchManager::IsInSupportedDomain(const GURL& url) { | 294 bool SdchManager::IsInSupportedDomain(const GURL& url) { |
332 DCHECK(CalledOnValidThread()); | 295 DCHECK(CalledOnValidThread()); |
333 if (!g_sdch_enabled_ ) | 296 if (!g_sdch_enabled_ ) |
334 return false; | 297 return false; |
335 | 298 |
336 if (blacklisted_domains_.empty()) | 299 if (blacklisted_domains_.empty()) |
337 return true; | 300 return true; |
338 | 301 |
339 std::string domain(StringToLowerASCII(url.host())); | 302 std::string domain(StringToLowerASCII(url.host())); |
340 DomainCounter::iterator it = blacklisted_domains_.find(domain); | 303 DomainCounter::iterator it = blacklisted_domains_.find(domain); |
341 if (blacklisted_domains_.end() == it) | 304 if (blacklisted_domains_.end() == it) |
342 return true; | 305 return true; |
343 | 306 |
344 int count = it->second - 1; | 307 int count = it->second - 1; |
345 if (count > 0) | 308 if (count > 0) |
346 blacklisted_domains_[domain] = count; | 309 blacklisted_domains_[domain] = count; |
347 else | 310 else |
348 blacklisted_domains_.erase(domain); | 311 blacklisted_domains_.erase(domain); |
349 SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET); | 312 SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET); |
350 return false; | 313 return false; |
351 } | 314 } |
352 | 315 |
353 void SdchManager::FetchDictionary(const GURL& request_url, | 316 void SdchManager::FetchDictionary(const GURL& request_url, |
354 const GURL& dictionary_url) { | 317 const GURL& dictionary_url) { |
355 DCHECK(CalledOnValidThread()); | 318 DCHECK(CalledOnValidThread()); |
356 if (SdchManager::Global()->CanFetchDictionary(request_url, dictionary_url) && | 319 if (CanFetchDictionary(request_url, dictionary_url) && fetcher_.get()) |
357 fetcher_.get()) | |
358 fetcher_->Schedule(dictionary_url); | 320 fetcher_->Schedule(dictionary_url); |
359 } | 321 } |
360 | 322 |
361 bool SdchManager::CanFetchDictionary(const GURL& referring_url, | 323 bool SdchManager::CanFetchDictionary(const GURL& referring_url, |
362 const GURL& dictionary_url) const { | 324 const GURL& dictionary_url) const { |
363 DCHECK(CalledOnValidThread()); | 325 DCHECK(CalledOnValidThread()); |
364 /* The user agent may retrieve a dictionary from the dictionary URL if all of | 326 /* The user agent may retrieve a dictionary from the dictionary URL if all of |
365 the following are true: | 327 the following are true: |
366 1 The dictionary URL host name matches the referrer URL host name and | 328 1 The dictionary URL host name matches the referrer URL host name and |
367 scheme. | 329 scheme. |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
458 if (port >= 0) | 420 if (port >= 0) |
459 ports.insert(port); | 421 ports.insert(port); |
460 } | 422 } |
461 } | 423 } |
462 | 424 |
463 if (line_end >= header_end) | 425 if (line_end >= header_end) |
464 break; | 426 break; |
465 line_start = line_end + 1; | 427 line_start = line_end + 1; |
466 } | 428 } |
467 | 429 |
| 430 if (!IsInSupportedDomain(dictionary_url)) |
| 431 return false; |
| 432 |
468 if (!Dictionary::CanSet(domain, path, ports, dictionary_url)) | 433 if (!Dictionary::CanSet(domain, path, ports, dictionary_url)) |
469 return false; | 434 return false; |
470 | 435 |
471 // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of | 436 // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of |
472 // useless dictionaries. We should probably have a cache eviction plan, | 437 // useless dictionaries. We should probably have a cache eviction plan, |
473 // instead of just blocking additions. For now, with the spec in flux, it | 438 // instead of just blocking additions. For now, with the spec in flux, it |
474 // is probably not worth doing eviction handling. | 439 // is probably not worth doing eviction handling. |
475 if (kMaxDictionarySize < dictionary_text.size()) { | 440 if (kMaxDictionarySize < dictionary_text.size()) { |
476 SdchErrorRecovery(DICTIONARY_IS_TOO_LARGE); | 441 SdchErrorRecovery(DICTIONARY_IS_TOO_LARGE); |
477 return false; | 442 return false; |
(...skipping 16 matching lines...) Expand all Loading... |
494 | 459 |
495 void SdchManager::GetVcdiffDictionary(const std::string& server_hash, | 460 void SdchManager::GetVcdiffDictionary(const std::string& server_hash, |
496 const GURL& referring_url, Dictionary** dictionary) { | 461 const GURL& referring_url, Dictionary** dictionary) { |
497 DCHECK(CalledOnValidThread()); | 462 DCHECK(CalledOnValidThread()); |
498 *dictionary = NULL; | 463 *dictionary = NULL; |
499 DictionaryMap::iterator it = dictionaries_.find(server_hash); | 464 DictionaryMap::iterator it = dictionaries_.find(server_hash); |
500 if (it == dictionaries_.end()) { | 465 if (it == dictionaries_.end()) { |
501 return; | 466 return; |
502 } | 467 } |
503 Dictionary* matching_dictionary = it->second; | 468 Dictionary* matching_dictionary = it->second; |
| 469 if (!IsInSupportedDomain(referring_url)) |
| 470 return; |
504 if (!matching_dictionary->CanUse(referring_url)) | 471 if (!matching_dictionary->CanUse(referring_url)) |
505 return; | 472 return; |
506 *dictionary = matching_dictionary; | 473 *dictionary = matching_dictionary; |
507 } | 474 } |
508 | 475 |
509 // TODO(jar): If we have evictions from the dictionaries_, then we need to | 476 // TODO(jar): If we have evictions from the dictionaries_, then we need to |
510 // change this interface to return a list of reference counted Dictionary | 477 // change this interface to return a list of reference counted Dictionary |
511 // instances that can be used if/when a server specifies one. | 478 // instances that can be used if/when a server specifies one. |
512 void SdchManager::GetAvailDictionaryList(const GURL& target_url, | 479 void SdchManager::GetAvailDictionaryList(const GURL& target_url, |
513 std::string* list) { | 480 std::string* list) { |
514 DCHECK(CalledOnValidThread()); | 481 DCHECK(CalledOnValidThread()); |
515 int count = 0; | 482 int count = 0; |
516 for (DictionaryMap::iterator it = dictionaries_.begin(); | 483 for (DictionaryMap::iterator it = dictionaries_.begin(); |
517 it != dictionaries_.end(); ++it) { | 484 it != dictionaries_.end(); ++it) { |
| 485 if (!IsInSupportedDomain(target_url)) |
| 486 continue; |
518 if (!it->second->CanAdvertise(target_url)) | 487 if (!it->second->CanAdvertise(target_url)) |
519 continue; | 488 continue; |
520 ++count; | 489 ++count; |
521 if (!list->empty()) | 490 if (!list->empty()) |
522 list->append(","); | 491 list->append(","); |
523 list->append(it->second->client_hash()); | 492 list->append(it->second->client_hash()); |
524 } | 493 } |
525 // Watch to see if we have corrupt or numerous dictionaries. | 494 // Watch to see if we have corrupt or numerous dictionaries. |
526 if (count > 0) | 495 if (count > 0) |
527 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); | 496 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
578 case '/': | 547 case '/': |
579 (*output)[i] = '_'; | 548 (*output)[i] = '_'; |
580 continue; | 549 continue; |
581 default: | 550 default: |
582 continue; | 551 continue; |
583 } | 552 } |
584 } | 553 } |
585 } | 554 } |
586 | 555 |
587 } // namespace net | 556 } // namespace net |
OLD | NEW |