Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(233)

Side by Side Diff: net/base/sdch_manager.cc

Issue 423813002: Sdch view for net-internals (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Change comments and netlog field name Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
18 //------------------------------------------------------------------------------ 19 //------------------------------------------------------------------------------
19 // static 20 // static
20 21
21 // Adjust SDCH limits downwards for mobile. 22 // Adjust SDCH limits downwards for mobile.
22 #if defined(OS_ANDROID) || defined(OS_IOS) 23 #if defined(OS_ANDROID) || defined(OS_IOS)
23 // static 24 // static
24 const size_t SdchManager::kMaxDictionaryCount = 1; 25 const size_t SdchManager::kMaxDictionaryCount = 1;
25 const size_t SdchManager::kMaxDictionarySize = 150 * 1000; 26 const size_t SdchManager::kMaxDictionarySize = 150 * 1000;
26 #else 27 #else
27 // static 28 // static
28 const size_t SdchManager::kMaxDictionaryCount = 20; 29 const size_t SdchManager::kMaxDictionaryCount = 20;
29 const size_t SdchManager::kMaxDictionarySize = 1000 * 1000; 30 const size_t SdchManager::kMaxDictionarySize = 1000 * 1000;
30 #endif 31 #endif
31 32
32 // static 33 // static
33 bool SdchManager::g_sdch_enabled_ = true; 34 bool SdchManager::g_sdch_enabled_ = true;
34 35
35 // static 36 // static
36 bool SdchManager::g_secure_scheme_supported_ = false; 37 bool SdchManager::g_secure_scheme_supported_ = false;
37 38
38 //------------------------------------------------------------------------------ 39 //------------------------------------------------------------------------------
40
41 SdchManager::AddResult::AddResult() : added(false), problem(PROBLEM_CODE_OK) {
42 }
43
39 SdchManager::Dictionary::Dictionary(const std::string& dictionary_text, 44 SdchManager::Dictionary::Dictionary(const std::string& dictionary_text,
40 size_t offset, 45 size_t offset,
41 const std::string& client_hash, 46 const std::string& client_hash,
42 const GURL& gurl, 47 const GURL& gurl,
43 const std::string& domain, 48 const std::string& domain,
44 const std::string& path, 49 const std::string& path,
45 const base::Time& expiration, 50 const base::Time& expiration,
46 const std::set<int>& ports) 51 const std::set<int>& ports)
47 : text_(dictionary_text, offset), 52 : text_(dictionary_text, offset),
48 client_hash_(client_hash), 53 client_hash_(client_hash),
49 url_(gurl), 54 url_(gurl),
50 domain_(domain), 55 domain_(domain),
51 path_(path), 56 path_(path),
52 expiration_(expiration), 57 expiration_(expiration),
53 ports_(ports) { 58 ports_(ports) {
54 } 59 }
55 60
56 SdchManager::Dictionary::~Dictionary() { 61 SdchManager::Dictionary::~Dictionary() {
57 } 62 }
58 63
59 bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) { 64 bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) const {
60 /* The specific rules of when a dictionary should be advertised in an 65 /* 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 66 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 67 terms "domain-match" and "pathmatch" are defined in RFC 2965 [6]. A
63 dictionary may be advertised in the Avail-Dictionaries header exactly when 68 dictionary may be advertised in the Avail-Dictionaries header exactly when
64 all of the following are true: 69 all of the following are true:
65 1. The server's effective host name domain-matches the Domain attribute of 70 1. The server's effective host name domain-matches the Domain attribute of
66 the dictionary. 71 the dictionary.
67 2. If the dictionary has a Port attribute, the request port is one of the 72 2. If the dictionary has a Port attribute, the request port is one of the
68 ports listed in the Port attribute. 73 ports listed in the Port attribute.
69 3. The request URI path-matches the path header of the dictionary. 74 3. The request URI path-matches the path header of the dictionary.
(...skipping 10 matching lines...) Expand all
80 return false; 85 return false;
81 if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure()) 86 if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure())
82 return false; 87 return false;
83 if (target_url.SchemeIsSecure() != url_.SchemeIsSecure()) 88 if (target_url.SchemeIsSecure() != url_.SchemeIsSecure())
84 return false; 89 return false;
85 if (base::Time::Now() > expiration_) 90 if (base::Time::Now() > expiration_)
86 return false; 91 return false;
87 return true; 92 return true;
88 } 93 }
89 94
95 base::DictionaryValue* SdchManager::Dictionary::DictionaryInfoToValue() const {
96 base::DictionaryValue* value = new base::DictionaryValue();
97 value->SetString("url", url_.spec());
98 value->SetString("client_hash", client_hash_);
99 value->SetString("domain", domain_);
100 value->SetString("path", path_);
101 value->SetString("expiration",
102 base::Int64ToString(expiration_.ToInternalValue()));
103 base::ListValue* port_list = new base::ListValue();
104 for (std::set<int>::const_iterator it = ports_.begin(); it != ports_.end();
105 ++it) {
106 port_list->AppendInteger(*it);
107 }
108 value->Set("ports", port_list);
109 return value;
110 }
111
90 //------------------------------------------------------------------------------ 112 //------------------------------------------------------------------------------
91 // Security functions restricting loads and use of dictionaries. 113 // Security functions restricting loads and use of dictionaries.
92 114
93 // static 115 // static
94 bool SdchManager::Dictionary::CanSet(const std::string& domain, 116 SdchManager::ProblemCodes SdchManager::Dictionary::CanSet(
95 const std::string& path, 117 const std::string& domain,
96 const std::set<int>& ports, 118 const std::string& path,
97 const GURL& dictionary_url) { 119 const std::set<int>& ports,
120 const GURL& dictionary_url) {
98 /* 121 /*
99 A dictionary is invalid and must not be stored if any of the following are 122 A dictionary is invalid and must not be stored if any of the following are
100 true: 123 true:
101 1. The dictionary has no Domain attribute. 124 1. The dictionary has no Domain attribute.
102 2. The effective host name that derives from the referer URL host name does 125 2. The effective host name that derives from the referer URL host name does
103 not domain-match the Domain attribute. 126 not domain-match the Domain attribute.
104 3. The Domain attribute is a top level domain. 127 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 128 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 129 form HD, where D is the value of the Domain attribute, and H is a string
107 that contains one or more dots. 130 that contains one or more dots.
108 5. If the dictionary has a Port attribute and the referer URL's port was not 131 5. If the dictionary has a Port attribute and the referer URL's port was not
109 in the list. 132 in the list.
110 */ 133 */
111 134
112 // TODO(jar): Redirects in dictionary fetches might plausibly be problematic, 135 // TODO(jar): Redirects in dictionary fetches might plausibly be problematic,
113 // and hence the conservative approach is to not allow any redirects (if there 136 // 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). 137 // were any... then don't allow the dictionary to be set).
115 138
116 if (domain.empty()) { 139 if (domain.empty())
117 SdchErrorRecovery(DICTIONARY_MISSING_DOMAIN_SPECIFIER); 140 return DICTIONARY_MISSING_DOMAIN_SPECIFIER; // Domain is required.
118 return false; // Domain is required. 141
119 }
120 if (registry_controlled_domains::GetDomainAndRegistry( 142 if (registry_controlled_domains::GetDomainAndRegistry(
121 domain, 143 domain, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)
122 registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES).empty()) { 144 .empty())
123 SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN); 145 return DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN; // domain was a TLD.
124 return false; // domain was a TLD. 146
125 } 147 if (!Dictionary::DomainMatch(dictionary_url, domain))
126 if (!Dictionary::DomainMatch(dictionary_url, domain)) { 148 return DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL;
127 SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL);
128 return false;
129 }
130 149
131 std::string referrer_url_host = dictionary_url.host(); 150 std::string referrer_url_host = dictionary_url.host();
132 size_t postfix_domain_index = referrer_url_host.rfind(domain); 151 size_t postfix_domain_index = referrer_url_host.rfind(domain);
133 // See if it is indeed a postfix, or just an internal string. 152 // See if it is indeed a postfix, or just an internal string.
134 if (referrer_url_host.size() == postfix_domain_index + domain.size()) { 153 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. 154 // 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('.'); 155 size_t end_of_host_index = referrer_url_host.find_first_of('.');
137 if (referrer_url_host.npos != end_of_host_index && 156 if (referrer_url_host.npos != end_of_host_index &&
138 end_of_host_index < postfix_domain_index) { 157 end_of_host_index < postfix_domain_index)
139 SdchErrorRecovery(DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX); 158 return DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX;
140 return false;
141 }
142 } 159 }
143 160
144 if (!ports.empty() 161 if (!ports.empty() && 0 == ports.count(dictionary_url.EffectiveIntPort()))
145 && 0 == ports.count(dictionary_url.EffectiveIntPort())) { 162 return DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL;
146 SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL); 163
147 return false; 164 return PROBLEM_CODE_OK;
148 }
149 return true;
150 } 165 }
151 166
152 // static 167 SdchManager::ProblemCodes SdchManager::Dictionary::CanUse(
153 bool SdchManager::Dictionary::CanUse(const GURL& referring_url) { 168 const GURL& referring_url) const {
154 /* 169 /*
155 1. The request URL's host name domain-matches the Domain attribute of the 170 1. The request URL's host name domain-matches the Domain attribute of the
156 dictionary. 171 dictionary.
157 2. If the dictionary has a Port attribute, the request port is one of the 172 2. If the dictionary has a Port attribute, the request port is one of the
158 ports listed in the Port attribute. 173 ports listed in the Port attribute.
159 3. The request URL path-matches the path attribute of the dictionary. 174 3. The request URL path-matches the path attribute of the dictionary.
160 4. The request is not an HTTPS request. 175 4. The request is not an HTTPS request.
161 We can override (ignore) item (4) only when we have explicitly enabled 176 We can override (ignore) item (4) only when we have explicitly enabled
162 HTTPS support AND the dictionary acquisition scheme matches the target 177 HTTPS support AND the dictionary acquisition scheme matches the target
163 url scheme. 178 url scheme.
164 */ 179 */
165 if (!DomainMatch(referring_url, domain_)) { 180 if (!DomainMatch(referring_url, domain_))
166 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_DOMAIN); 181 return DICTIONARY_FOUND_HAS_WRONG_DOMAIN;
167 return false; 182
168 } 183 if (!ports_.empty() && 0 == ports_.count(referring_url.EffectiveIntPort()))
169 if (!ports_.empty() 184 return DICTIONARY_FOUND_HAS_WRONG_PORT_LIST;
170 && 0 == ports_.count(referring_url.EffectiveIntPort())) { 185
171 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PORT_LIST); 186 if (path_.size() && !PathMatch(referring_url.path(), path_))
172 return false; 187 return DICTIONARY_FOUND_HAS_WRONG_PATH;
173 } 188
174 if (path_.size() && !PathMatch(referring_url.path(), path_)) { 189 if (!SdchManager::secure_scheme_supported() && referring_url.SchemeIsSecure())
175 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PATH); 190 return DICTIONARY_FOUND_HAS_WRONG_SCHEME;
176 return false; 191
177 } 192 if (referring_url.SchemeIsSecure() != url_.SchemeIsSecure())
178 if (!SdchManager::secure_scheme_supported() && 193 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 194
188 // TODO(jar): Remove overly restrictive failsafe test (added per security 195 // TODO(jar): Remove overly restrictive failsafe test (added per security
189 // review) when we have a need to be more general. 196 // review) when we have a need to be more general.
190 if (!referring_url.SchemeIsHTTPOrHTTPS()) { 197 if (!referring_url.SchemeIsHTTPOrHTTPS())
191 SdchErrorRecovery(ATTEMPT_TO_DECODE_NON_HTTP_DATA); 198 return ATTEMPT_TO_DECODE_NON_HTTP_DATA;
192 return false;
193 }
194 199
195 return true; 200 return PROBLEM_CODE_OK;
196 } 201 }
197 202
203 // static
198 bool SdchManager::Dictionary::PathMatch(const std::string& path, 204 bool SdchManager::Dictionary::PathMatch(const std::string& path,
199 const std::string& restriction) { 205 const std::string& restriction) {
200 /* Must be either: 206 /* Must be either:
201 1. P2 is equal to P1 207 1. P2 is equal to P1
202 2. P2 is a prefix of P1 and either the final character in P2 is "/" or the 208 2. P2 is a prefix of P1 and either the final character in P2 is "/" or the
203 character following P2 in P1 is "/". 209 character following P2 in P1 is "/".
204 */ 210 */
205 if (path == restriction) 211 if (path == restriction)
206 return true; 212 return true;
207 size_t prefix_length = restriction.size(); 213 size_t prefix_length = restriction.size();
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
308 314
309 int SdchManager::BlacklistDomainExponential(const std::string& domain) { 315 int SdchManager::BlacklistDomainExponential(const std::string& domain) {
310 if (exponential_blacklist_count_.end() == 316 if (exponential_blacklist_count_.end() ==
311 exponential_blacklist_count_.find(domain)) 317 exponential_blacklist_count_.find(domain))
312 return 0; 318 return 0;
313 return exponential_blacklist_count_[StringToLowerASCII(domain)]; 319 return exponential_blacklist_count_[StringToLowerASCII(domain)];
314 } 320 }
315 321
316 bool SdchManager::IsInSupportedDomain(const GURL& url) { 322 bool SdchManager::IsInSupportedDomain(const GURL& url) {
317 DCHECK(CalledOnValidThread()); 323 DCHECK(CalledOnValidThread());
324 if (!IsSdchEnabledForUrl(url))
baranovich 2014/08/02 21:17:24 I've split this method into two for 2 reasons: 1)
325 return false;
326
327 if (IsInBlacklistedDomain(url)) {
328 SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET);
329 return false;
330 }
331 return true;
332 }
333
334 // static
335 bool SdchManager::IsSdchEnabledForUrl(const GURL& url) {
318 if (!g_sdch_enabled_ ) 336 if (!g_sdch_enabled_ )
319 return false; 337 return false;
320 338
321 if (!secure_scheme_supported() && url.SchemeIsSecure()) 339 if (!secure_scheme_supported() && url.SchemeIsSecure())
322 return false; 340 return false;
323 341
342 return true;
343 }
344
345 bool SdchManager::IsInBlacklistedDomain(const GURL& url) {
324 if (blacklisted_domains_.empty()) 346 if (blacklisted_domains_.empty())
325 return true; 347 return false;
326 348
327 std::string domain(StringToLowerASCII(url.host())); 349 std::string domain(StringToLowerASCII(url.host()));
328 DomainCounter::iterator it = blacklisted_domains_.find(domain); 350 DomainCounter::iterator it = blacklisted_domains_.find(domain);
329 if (blacklisted_domains_.end() == it) 351 if (blacklisted_domains_.end() == it)
330 return true; 352 return false;
331 353
332 int count = it->second - 1; 354 int count = it->second - 1;
333 if (count > 0) 355 if (count > 0)
334 blacklisted_domains_[domain] = count; 356 blacklisted_domains_[domain] = count;
335 else 357 else
336 blacklisted_domains_.erase(domain); 358 blacklisted_domains_.erase(domain);
337 SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET); 359
338 return false; 360 return true;
339 } 361 }
340 362
341 void SdchManager::FetchDictionary(const GURL& request_url, 363 SdchManager::ProblemCodes SdchManager::FetchDictionary(
342 const GURL& dictionary_url) { 364 const GURL& request_url,
365 const GURL& dictionary_url) {
343 DCHECK(CalledOnValidThread()); 366 DCHECK(CalledOnValidThread());
344 if (CanFetchDictionary(request_url, dictionary_url) && fetcher_.get()) 367 ProblemCodes rv = CanFetchDictionary(request_url, dictionary_url);
368 if (rv != PROBLEM_CODE_OK)
369 return rv;
370
371 if (fetcher_.get())
345 fetcher_->Schedule(dictionary_url); 372 fetcher_->Schedule(dictionary_url);
373
374 return PROBLEM_CODE_OK;
346 } 375 }
347 376
348 bool SdchManager::CanFetchDictionary(const GURL& referring_url, 377 SdchManager::ProblemCodes SdchManager::CanFetchDictionary(
349 const GURL& dictionary_url) const { 378 const GURL& referring_url,
379 const GURL& dictionary_url) const {
350 DCHECK(CalledOnValidThread()); 380 DCHECK(CalledOnValidThread());
351 /* The user agent may retrieve a dictionary from the dictionary URL if all of 381 /* The user agent may retrieve a dictionary from the dictionary URL if all of
352 the following are true: 382 the following are true:
353 1 The dictionary URL host name matches the referrer URL host name and 383 1 The dictionary URL host name matches the referrer URL host name and
354 scheme. 384 scheme.
355 2 The dictionary URL host name domain matches the parent domain of the 385 2 The dictionary URL host name domain matches the parent domain of the
356 referrer URL host name 386 referrer URL host name
357 3 The parent domain of the referrer URL host name is not a top level 387 3 The parent domain of the referrer URL host name is not a top level
358 domain 388 domain
359 4 The dictionary URL is not an HTTPS URL. 389 4 The dictionary URL is not an HTTPS URL.
360 */ 390 */
361 // Item (1) above implies item (2). Spec should be updated. 391 // Item (1) above implies item (2). Spec should be updated.
362 // I take "host name match" to be "is identical to" 392 // I take "host name match" to be "is identical to"
363 if (referring_url.host() != dictionary_url.host() || 393 if (referring_url.host() != dictionary_url.host() ||
364 referring_url.scheme() != dictionary_url.scheme()) { 394 referring_url.scheme() != dictionary_url.scheme())
365 SdchErrorRecovery(DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST); 395 return DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST;
366 return false; 396
367 } 397 if (!secure_scheme_supported() && referring_url.SchemeIsSecure())
368 if (!secure_scheme_supported() && referring_url.SchemeIsSecure()) { 398 return DICTIONARY_SELECTED_FOR_SSL;
369 SdchErrorRecovery(DICTIONARY_SELECTED_FOR_SSL);
370 return false;
371 }
372 399
373 // TODO(jar): Remove this failsafe conservative hack which is more restrictive 400 // TODO(jar): Remove this failsafe conservative hack which is more restrictive
374 // than current SDCH spec when needed, and justified by security audit. 401 // than current SDCH spec when needed, and justified by security audit.
375 if (!referring_url.SchemeIsHTTPOrHTTPS()) { 402 if (!referring_url.SchemeIsHTTPOrHTTPS())
376 SdchErrorRecovery(DICTIONARY_SELECTED_FROM_NON_HTTP); 403 return DICTIONARY_SELECTED_FROM_NON_HTTP;
377 return false;
378 }
379 404
380 return true; 405 return PROBLEM_CODE_OK;
381 } 406 }
382 407
383 bool SdchManager::AddSdchDictionary(const std::string& dictionary_text, 408 SdchManager::AddResult SdchManager::AddSdchDictionary(
409 const std::string& dictionary_text,
384 const GURL& dictionary_url) { 410 const GURL& dictionary_url) {
385 DCHECK(CalledOnValidThread()); 411 DCHECK(CalledOnValidThread());
386 std::string client_hash; 412 std::string client_hash;
387 std::string server_hash; 413 std::string server_hash;
388 GenerateHash(dictionary_text, &client_hash, &server_hash); 414 GenerateHash(dictionary_text, &client_hash, &server_hash);
415 AddResult rv;
389 if (dictionaries_.find(server_hash) != dictionaries_.end()) { 416 if (dictionaries_.find(server_hash) != dictionaries_.end()) {
390 SdchErrorRecovery(DICTIONARY_ALREADY_LOADED); 417 rv.problem = DICTIONARY_ALREADY_LOADED; // Already loaded.
391 return false; // Already loaded. 418 return rv;
392 } 419 }
393 420
394 std::string domain, path; 421 std::string domain, path;
395 std::set<int> ports; 422 std::set<int> ports;
396 base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30)); 423 base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30));
397 424
398 if (dictionary_text.empty()) { 425 if (dictionary_text.empty()) {
399 SdchErrorRecovery(DICTIONARY_HAS_NO_TEXT); 426 rv.problem = DICTIONARY_HAS_NO_TEXT; // Empty response.
400 return false; // Missing header. 427 return rv;
401 } 428 }
402 429
403 size_t header_end = dictionary_text.find("\n\n"); 430 size_t header_end = dictionary_text.find("\n\n");
404 if (std::string::npos == header_end) { 431 if (std::string::npos == header_end) {
405 SdchErrorRecovery(DICTIONARY_HAS_NO_HEADER); 432 rv.problem = DICTIONARY_HAS_NO_HEADER; // Missing header.
406 return false; // Missing header. 433 return rv;
407 } 434 }
435
408 size_t line_start = 0; // Start of line being parsed. 436 size_t line_start = 0; // Start of line being parsed.
409 while (1) { 437 while (1) {
410 size_t line_end = dictionary_text.find('\n', line_start); 438 size_t line_end = dictionary_text.find('\n', line_start);
411 DCHECK(std::string::npos != line_end); 439 DCHECK(std::string::npos != line_end);
412 DCHECK_LE(line_end, header_end); 440 DCHECK_LE(line_end, header_end);
413 441
414 size_t colon_index = dictionary_text.find(':', line_start); 442 size_t colon_index = dictionary_text.find(':', line_start);
415 if (std::string::npos == colon_index) { 443 if (std::string::npos == colon_index) {
416 SdchErrorRecovery(DICTIONARY_HEADER_LINE_MISSING_COLON); 444 // Illegal line missing a colon.
417 return false; // Illegal line missing a colon. 445 rv.problem = DICTIONARY_HEADER_LINE_MISSING_COLON;
446 return rv;
418 } 447 }
419 448
420 if (colon_index > line_end) 449 if (colon_index > line_end)
421 break; 450 break;
422 451
423 size_t value_start = dictionary_text.find_first_not_of(" \t", 452 size_t value_start = dictionary_text.find_first_not_of(" \t",
424 colon_index + 1); 453 colon_index + 1);
425 if (std::string::npos != value_start) { 454 if (std::string::npos != value_start) {
426 if (value_start >= line_end) 455 if (value_start >= line_end)
427 break; 456 break;
428 std::string name(dictionary_text, line_start, colon_index - line_start); 457 std::string name(dictionary_text, line_start, colon_index - line_start);
429 std::string value(dictionary_text, value_start, line_end - value_start); 458 std::string value(dictionary_text, value_start, line_end - value_start);
430 name = StringToLowerASCII(name); 459 name = StringToLowerASCII(name);
431 if (name == "domain") { 460 if (name == "domain") {
432 domain = value; 461 domain = value;
433 } else if (name == "path") { 462 } else if (name == "path") {
434 path = value; 463 path = value;
435 } else if (name == "format-version") { 464 } else if (name == "format-version") {
436 if (value != "1.0") 465 if (value != "1.0") {
437 return false; 466 // TODO(baranovich): add problem code to track unsupported versions?
467 return rv;
468 }
438 } else if (name == "max-age") { 469 } else if (name == "max-age") {
439 int64 seconds; 470 int64 seconds;
440 base::StringToInt64(value, &seconds); 471 base::StringToInt64(value, &seconds);
441 expiration = base::Time::Now() + base::TimeDelta::FromSeconds(seconds); 472 expiration = base::Time::Now() + base::TimeDelta::FromSeconds(seconds);
442 } else if (name == "port") { 473 } else if (name == "port") {
443 int port; 474 int port;
444 base::StringToInt(value, &port); 475 base::StringToInt(value, &port);
445 if (port >= 0) 476 if (port >= 0)
446 ports.insert(port); 477 ports.insert(port);
447 } 478 }
448 } 479 }
449 480
450 if (line_end >= header_end) 481 if (line_end >= header_end)
451 break; 482 break;
452 line_start = line_end + 1; 483 line_start = line_end + 1;
453 } 484 }
454 485
455 if (!IsInSupportedDomain(dictionary_url)) 486 if (!IsSdchEnabledForUrl(dictionary_url))
456 return false; 487 return rv;
457 488
458 if (!Dictionary::CanSet(domain, path, ports, dictionary_url)) 489 if (IsInBlacklistedDomain(dictionary_url)) {
459 return false; 490 rv.problem = DOMAIN_BLACKLIST_INCLUDES_TARGET;
491 return rv;
492 }
493
494 ProblemCodes err = Dictionary::CanSet(domain, path, ports, dictionary_url);
495 if (err != PROBLEM_CODE_OK) {
496 rv.problem = err;
497 return rv;
498 }
460 499
461 // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of 500 // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of
462 // useless dictionaries. We should probably have a cache eviction plan, 501 // useless dictionaries. We should probably have a cache eviction plan,
463 // instead of just blocking additions. For now, with the spec in flux, it 502 // instead of just blocking additions. For now, with the spec in flux, it
464 // is probably not worth doing eviction handling. 503 // is probably not worth doing eviction handling.
465 if (kMaxDictionarySize < dictionary_text.size()) { 504 if (kMaxDictionarySize < dictionary_text.size()) {
466 SdchErrorRecovery(DICTIONARY_IS_TOO_LARGE); 505 rv.problem = DICTIONARY_IS_TOO_LARGE;
467 return false; 506 return rv;
468 } 507 }
469 if (kMaxDictionaryCount <= dictionaries_.size()) { 508 if (kMaxDictionaryCount <= dictionaries_.size()) {
470 SdchErrorRecovery(DICTIONARY_COUNT_EXCEEDED); 509 rv.problem = DICTIONARY_COUNT_EXCEEDED;
471 return false; 510 return rv;
472 } 511 }
473 512
474 UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size()); 513 UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size());
475 DVLOG(1) << "Loaded dictionary with client hash " << client_hash 514 DVLOG(1) << "Loaded dictionary with client hash " << client_hash
476 << " and server hash " << server_hash; 515 << " and server hash " << server_hash;
477 Dictionary* dictionary = 516 Dictionary* dictionary =
478 new Dictionary(dictionary_text, header_end + 2, client_hash, 517 new Dictionary(dictionary_text, header_end + 2, client_hash,
479 dictionary_url, domain, path, expiration, ports); 518 dictionary_url, domain, path, expiration, ports);
480 dictionaries_[server_hash] = dictionary; 519 dictionaries_[server_hash] = dictionary;
481 return true; 520
521 rv.added = true;
522 return rv;
482 } 523 }
483 524
484 void SdchManager::GetVcdiffDictionary( 525 SdchManager::ProblemCodes SdchManager::GetVcdiffDictionary(
485 const std::string& server_hash, 526 const std::string& server_hash,
486 const GURL& referring_url, 527 const GURL& referring_url,
487 scoped_refptr<Dictionary>* dictionary) { 528 scoped_refptr<Dictionary>* dictionary) {
488 DCHECK(CalledOnValidThread()); 529 DCHECK(CalledOnValidThread());
489 *dictionary = NULL; 530 *dictionary = NULL;
490 DictionaryMap::iterator it = dictionaries_.find(server_hash); 531 DictionaryMap::iterator it = dictionaries_.find(server_hash);
491 if (it == dictionaries_.end()) { 532 if (it == dictionaries_.end())
492 return; 533 return PROBLEM_CODE_OK; // not a problem. Just no dictionary.
493 } 534
494 scoped_refptr<Dictionary> matching_dictionary = it->second; 535 scoped_refptr<Dictionary> matching_dictionary = it->second;
495 if (!IsInSupportedDomain(referring_url)) 536 if (!IsSdchEnabledForUrl(referring_url))
496 return; 537 return PROBLEM_CODE_OK; // not a problem. SDCH is disabled.
497 if (!matching_dictionary->CanUse(referring_url)) 538
498 return; 539 if (IsInBlacklistedDomain(referring_url))
499 *dictionary = matching_dictionary; 540 return DOMAIN_BLACKLIST_INCLUDES_TARGET;
541
542 ProblemCodes rv = matching_dictionary->CanUse(referring_url);
543 if (rv == PROBLEM_CODE_OK)
544 *dictionary = matching_dictionary;
545 return rv;
500 } 546 }
501 547
502 // TODO(jar): If we have evictions from the dictionaries_, then we need to 548 // 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 549 // change this interface to return a list of reference counted Dictionary
504 // instances that can be used if/when a server specifies one. 550 // instances that can be used if/when a server specifies one.
505 void SdchManager::GetAvailDictionaryList(const GURL& target_url, 551 void SdchManager::GetAvailDictionaryList(const GURL& target_url,
506 std::string* list) { 552 std::string* list) {
507 DCHECK(CalledOnValidThread()); 553 DCHECK(CalledOnValidThread());
508 int count = 0; 554 int count = 0;
509 for (DictionaryMap::iterator it = dictionaries_.begin(); 555 for (DictionaryMap::iterator it = dictionaries_.begin();
510 it != dictionaries_.end(); ++it) { 556 it != dictionaries_.end(); ++it) {
511 if (!IsInSupportedDomain(target_url)) 557 if (!IsInSupportedDomain(target_url)) {
512 continue; 558 continue;
559 }
513 if (!it->second->CanAdvertise(target_url)) 560 if (!it->second->CanAdvertise(target_url))
514 continue; 561 continue;
515 ++count; 562 ++count;
516 if (!list->empty()) 563 if (!list->empty())
517 list->append(","); 564 list->append(",");
518 list->append(it->second->client_hash()); 565 list->append(it->second->client_hash());
519 } 566 }
520 // Watch to see if we have corrupt or numerous dictionaries. 567 // Watch to see if we have corrupt or numerous dictionaries.
521 if (count > 0) 568 if (count > 0)
522 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); 569 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count);
523 } 570 }
524 571
525 // static 572 // static
526 void SdchManager::GenerateHash(const std::string& dictionary_text, 573 void SdchManager::GenerateHash(const std::string& dictionary_text,
527 std::string* client_hash, std::string* server_hash) { 574 std::string* client_hash, std::string* server_hash) {
528 char binary_hash[32]; 575 char binary_hash[32];
529 crypto::SHA256HashString(dictionary_text, binary_hash, sizeof(binary_hash)); 576 crypto::SHA256HashString(dictionary_text, binary_hash, sizeof(binary_hash));
530 577
531 std::string first_48_bits(&binary_hash[0], 6); 578 std::string first_48_bits(&binary_hash[0], 6);
532 std::string second_48_bits(&binary_hash[6], 6); 579 std::string second_48_bits(&binary_hash[6], 6);
533 UrlSafeBase64Encode(first_48_bits, client_hash); 580 UrlSafeBase64Encode(first_48_bits, client_hash);
534 UrlSafeBase64Encode(second_48_bits, server_hash); 581 UrlSafeBase64Encode(second_48_bits, server_hash);
535 582
536 DCHECK_EQ(server_hash->length(), 8u); 583 DCHECK_EQ(server_hash->length(), 8u);
537 DCHECK_EQ(client_hash->length(), 8u); 584 DCHECK_EQ(client_hash->length(), 8u);
538 } 585 }
539 586
587 base::Value* SdchManager::SdchInfoToValue() const {
588 base::DictionaryValue* value = new base::DictionaryValue();
589
590 value->SetBoolean("sdch_enabled", sdch_enabled());
591 value->SetBoolean("secure_scheme_support", secure_scheme_supported());
592
593 base::ListValue* entry_list = new base::ListValue();
594 for (DictionaryMap::const_iterator it = dictionaries_.begin();
595 it != dictionaries_.end();
596 ++it) {
597 base::DictionaryValue* entry_dict = it->second->DictionaryInfoToValue();
598 entry_dict->SetString("server_hash", it->first);
599 entry_list->Append(entry_dict);
600 }
601 value->Set("dictionaries", entry_list);
602
603 entry_list = new base::ListValue();
604 for (DomainCounter::const_iterator it = blacklisted_domains_.begin();
605 it != blacklisted_domains_.end();
606 ++it) {
607 base::DictionaryValue* entry_dict = new base::DictionaryValue();
608 entry_dict->SetString("domain", it->first);
609 if (it->second != INT_MAX)
610 entry_dict->SetInteger("tries", it->second);
611 entry_list->Append(entry_dict);
612 }
613 value->Set("blacklisted", entry_list);
614
615 return value;
616 }
617
540 //------------------------------------------------------------------------------ 618 //------------------------------------------------------------------------------
541 // Methods for supporting latency experiments. 619 // Methods for supporting latency experiments.
542 620
543 bool SdchManager::AllowLatencyExperiment(const GURL& url) const { 621 bool SdchManager::AllowLatencyExperiment(const GURL& url) const {
544 DCHECK(CalledOnValidThread()); 622 DCHECK(CalledOnValidThread());
545 return allow_latency_experiment_.end() != 623 return allow_latency_experiment_.end() !=
546 allow_latency_experiment_.find(url.host()); 624 allow_latency_experiment_.find(url.host());
547 } 625 }
548 626
549 void SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) { 627 void SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) {
550 DCHECK(CalledOnValidThread()); 628 DCHECK(CalledOnValidThread());
551 if (enable) { 629 if (enable) {
552 allow_latency_experiment_.insert(url.host()); 630 allow_latency_experiment_.insert(url.host());
553 return; 631 return;
554 } 632 }
555 ExperimentSet::iterator it = allow_latency_experiment_.find(url.host()); 633 ExperimentSet::iterator it = allow_latency_experiment_.find(url.host());
556 if (allow_latency_experiment_.end() == it) 634 if (allow_latency_experiment_.end() == it)
557 return; // It was already erased, or never allowed. 635 return; // It was already erased, or never allowed.
636
637 // Do not send error up. It's internal stuff, I guess.
558 SdchErrorRecovery(LATENCY_TEST_DISALLOWED); 638 SdchErrorRecovery(LATENCY_TEST_DISALLOWED);
559 allow_latency_experiment_.erase(it); 639 allow_latency_experiment_.erase(it);
560 } 640 }
561 641
562 // static 642 // static
563 void SdchManager::UrlSafeBase64Encode(const std::string& input, 643 void SdchManager::UrlSafeBase64Encode(const std::string& input,
564 std::string* output) { 644 std::string* output) {
565 // Since this is only done during a dictionary load, and hashes are only 8 645 // 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. 646 // characters, we just do the simple fixup, rather than rewriting the encoder.
567 base::Base64Encode(input, output); 647 base::Base64Encode(input, output);
568 for (size_t i = 0; i < output->size(); ++i) { 648 for (size_t i = 0; i < output->size(); ++i) {
569 switch (output->data()[i]) { 649 switch (output->data()[i]) {
570 case '+': 650 case '+':
571 (*output)[i] = '-'; 651 (*output)[i] = '-';
572 continue; 652 continue;
573 case '/': 653 case '/':
574 (*output)[i] = '_'; 654 (*output)[i] = '_';
575 continue; 655 continue;
576 default: 656 default:
577 continue; 657 continue;
578 } 658 }
579 } 659 }
580 } 660 }
581 661
582 } // namespace net 662 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698