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