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