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" | |
13 #include "crypto/sha2.h" | 12 #include "crypto/sha2.h" |
14 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | 13 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
15 #include "net/url_request/url_request_http_job.h" | 14 #include "net/url_request/url_request_http_job.h" |
16 | 15 |
17 namespace { | 16 namespace { |
18 | 17 |
19 void StripTrailingDot(GURL* gurl) { | 18 void StripTrailingDot(GURL* gurl) { |
20 std::string host(gurl->host()); | 19 std::string host(gurl->host()); |
21 | 20 |
22 if (host.empty()) | 21 if (host.empty()) |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
76 url_(gurl), | 75 url_(gurl), |
77 domain_(domain), | 76 domain_(domain), |
78 path_(path), | 77 path_(path), |
79 expiration_(expiration), | 78 expiration_(expiration), |
80 ports_(ports) { | 79 ports_(ports) { |
81 } | 80 } |
82 | 81 |
83 SdchManager::Dictionary::~Dictionary() { | 82 SdchManager::Dictionary::~Dictionary() { |
84 } | 83 } |
85 | 84 |
86 SdchProblemCode SdchManager::Dictionary::CanAdvertise( | 85 bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) { |
87 const GURL& target_url) const { | |
88 /* The specific rules of when a dictionary should be advertised in an | 86 /* The specific rules of when a dictionary should be advertised in an |
89 Avail-Dictionary header are modeled after the rules for cookie scoping. The | 87 Avail-Dictionary header are modeled after the rules for cookie scoping. The |
90 terms "domain-match" and "pathmatch" are defined in RFC 2965 [6]. A | 88 terms "domain-match" and "pathmatch" are defined in RFC 2965 [6]. A |
91 dictionary may be advertised in the Avail-Dictionaries header exactly when | 89 dictionary may be advertised in the Avail-Dictionaries header exactly when |
92 all of the following are true: | 90 all of the following are true: |
93 1. The server's effective host name domain-matches the Domain attribute of | 91 1. The server's effective host name domain-matches the Domain attribute of |
94 the dictionary. | 92 the dictionary. |
95 2. If the dictionary has a Port attribute, the request port is one of the | 93 2. If the dictionary has a Port attribute, the request port is one of the |
96 ports listed in the Port attribute. | 94 ports listed in the Port attribute. |
97 3. The request URI path-matches the path header of the dictionary. | 95 3. The request URI path-matches the path header of the dictionary. |
98 4. The request is not an HTTPS request. | 96 4. The request is not an HTTPS request. |
99 We can override (ignore) item (4) only when we have explicitly enabled | 97 We can override (ignore) item (4) only when we have explicitly enabled |
100 HTTPS support AND the dictionary acquisition scheme matches the target | 98 HTTPS support AND the dictionary acquisition scheme matches the target |
101 url scheme. | 99 url scheme. |
102 */ | 100 */ |
103 if (!DomainMatch(target_url, domain_)) | 101 if (!DomainMatch(target_url, domain_)) |
104 return SDCH_DICTIONARY_FOUND_HAS_WRONG_DOMAIN; | 102 return false; |
105 if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort())) | 103 if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort())) |
106 return SDCH_DICTIONARY_FOUND_HAS_WRONG_PORT_LIST; | 104 return false; |
107 if (path_.size() && !PathMatch(target_url.path(), path_)) | 105 if (path_.size() && !PathMatch(target_url.path(), path_)) |
108 return SDCH_DICTIONARY_FOUND_HAS_WRONG_PATH; | 106 return false; |
109 if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure()) | 107 if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure()) |
110 return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME; | 108 return false; |
111 if (target_url.SchemeIsSecure() != url_.SchemeIsSecure()) | 109 if (target_url.SchemeIsSecure() != url_.SchemeIsSecure()) |
112 return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME; | 110 return false; |
113 if (base::Time::Now() > expiration_) | 111 if (base::Time::Now() > expiration_) |
114 return SDCH_DICTIONARY_FOUND_EXPIRED; | 112 return false; |
115 return SDCH_OK; | 113 return true; |
116 } | 114 } |
117 | 115 |
118 //------------------------------------------------------------------------------ | 116 //------------------------------------------------------------------------------ |
119 // Security functions restricting loads and use of dictionaries. | 117 // Security functions restricting loads and use of dictionaries. |
120 | 118 |
121 // static | 119 // static |
122 SdchProblemCode SdchManager::Dictionary::CanSet(const std::string& domain, | 120 bool SdchManager::Dictionary::CanSet(const std::string& domain, |
123 const std::string& path, | 121 const std::string& path, |
124 const std::set<int>& ports, | 122 const std::set<int>& ports, |
125 const GURL& dictionary_url) { | 123 const GURL& dictionary_url) { |
126 /* | 124 /* |
127 A dictionary is invalid and must not be stored if any of the following are | 125 A dictionary is invalid and must not be stored if any of the following are |
128 true: | 126 true: |
129 1. The dictionary has no Domain attribute. | 127 1. The dictionary has no Domain attribute. |
130 2. The effective host name that derives from the referer URL host name does | 128 2. The effective host name that derives from the referer URL host name does |
131 not domain-match the Domain attribute. | 129 not domain-match the Domain attribute. |
132 3. The Domain attribute is a top level domain. | 130 3. The Domain attribute is a top level domain. |
133 4. The referer URL host is a host domain name (not IP address) and has the | 131 4. The referer URL host is a host domain name (not IP address) and has the |
134 form HD, where D is the value of the Domain attribute, and H is a string | 132 form HD, where D is the value of the Domain attribute, and H is a string |
135 that contains one or more dots. | 133 that contains one or more dots. |
136 5. If the dictionary has a Port attribute and the referer URL's port was not | 134 5. If the dictionary has a Port attribute and the referer URL's port was not |
137 in the list. | 135 in the list. |
138 */ | 136 */ |
139 | 137 |
140 // TODO(jar): Redirects in dictionary fetches might plausibly be problematic, | 138 // TODO(jar): Redirects in dictionary fetches might plausibly be problematic, |
141 // and hence the conservative approach is to not allow any redirects (if there | 139 // and hence the conservative approach is to not allow any redirects (if there |
142 // were any... then don't allow the dictionary to be set). | 140 // were any... then don't allow the dictionary to be set). |
143 | 141 |
144 if (domain.empty()) | 142 if (domain.empty()) { |
145 return SDCH_DICTIONARY_MISSING_DOMAIN_SPECIFIER; // Domain is required. | 143 SdchErrorRecovery(DICTIONARY_MISSING_DOMAIN_SPECIFIER); |
146 | 144 return false; // Domain is required. |
| 145 } |
147 if (registry_controlled_domains::GetDomainAndRegistry( | 146 if (registry_controlled_domains::GetDomainAndRegistry( |
148 domain, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES) | 147 domain, |
149 .empty()) { | 148 registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES).empty()) { |
150 return SDCH_DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN; // domain was a TLD. | 149 SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN); |
| 150 return false; // domain was a TLD. |
151 } | 151 } |
152 | 152 if (!Dictionary::DomainMatch(dictionary_url, domain)) { |
153 if (!Dictionary::DomainMatch(dictionary_url, domain)) | 153 SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL); |
154 return SDCH_DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL; | 154 return false; |
| 155 } |
155 | 156 |
156 std::string referrer_url_host = dictionary_url.host(); | 157 std::string referrer_url_host = dictionary_url.host(); |
157 size_t postfix_domain_index = referrer_url_host.rfind(domain); | 158 size_t postfix_domain_index = referrer_url_host.rfind(domain); |
158 // See if it is indeed a postfix, or just an internal string. | 159 // See if it is indeed a postfix, or just an internal string. |
159 if (referrer_url_host.size() == postfix_domain_index + domain.size()) { | 160 if (referrer_url_host.size() == postfix_domain_index + domain.size()) { |
160 // It is a postfix... so check to see if there's a dot in the prefix. | 161 // It is a postfix... so check to see if there's a dot in the prefix. |
161 size_t end_of_host_index = referrer_url_host.find_first_of('.'); | 162 size_t end_of_host_index = referrer_url_host.find_first_of('.'); |
162 if (referrer_url_host.npos != end_of_host_index && | 163 if (referrer_url_host.npos != end_of_host_index && |
163 end_of_host_index < postfix_domain_index) { | 164 end_of_host_index < postfix_domain_index) { |
164 return SDCH_DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX; | 165 SdchErrorRecovery(DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX); |
| 166 return false; |
165 } | 167 } |
166 } | 168 } |
167 | 169 |
168 if (!ports.empty() && 0 == ports.count(dictionary_url.EffectiveIntPort())) | 170 if (!ports.empty() |
169 return SDCH_DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL; | 171 && 0 == ports.count(dictionary_url.EffectiveIntPort())) { |
170 | 172 SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL); |
171 return SDCH_OK; | 173 return false; |
| 174 } |
| 175 return true; |
172 } | 176 } |
173 | 177 |
174 SdchProblemCode SdchManager::Dictionary::CanUse( | 178 // static |
175 const GURL& referring_url) const { | 179 bool SdchManager::Dictionary::CanUse(const GURL& referring_url) { |
176 /* | 180 /* |
177 1. The request URL's host name domain-matches the Domain attribute of the | 181 1. The request URL's host name domain-matches the Domain attribute of the |
178 dictionary. | 182 dictionary. |
179 2. If the dictionary has a Port attribute, the request port is one of the | 183 2. If the dictionary has a Port attribute, the request port is one of the |
180 ports listed in the Port attribute. | 184 ports listed in the Port attribute. |
181 3. The request URL path-matches the path attribute of the dictionary. | 185 3. The request URL path-matches the path attribute of the dictionary. |
182 4. The request is not an HTTPS request. | 186 4. The request is not an HTTPS request. |
183 We can override (ignore) item (4) only when we have explicitly enabled | 187 We can override (ignore) item (4) only when we have explicitly enabled |
184 HTTPS support AND the dictionary acquisition scheme matches the target | 188 HTTPS support AND the dictionary acquisition scheme matches the target |
185 url scheme. | 189 url scheme. |
186 */ | 190 */ |
187 if (!DomainMatch(referring_url, domain_)) | 191 if (!DomainMatch(referring_url, domain_)) { |
188 return SDCH_DICTIONARY_FOUND_HAS_WRONG_DOMAIN; | 192 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_DOMAIN); |
189 | 193 return false; |
190 if (!ports_.empty() && 0 == ports_.count(referring_url.EffectiveIntPort())) | 194 } |
191 return SDCH_DICTIONARY_FOUND_HAS_WRONG_PORT_LIST; | 195 if (!ports_.empty() |
192 | 196 && 0 == ports_.count(referring_url.EffectiveIntPort())) { |
193 if (path_.size() && !PathMatch(referring_url.path(), path_)) | 197 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PORT_LIST); |
194 return SDCH_DICTIONARY_FOUND_HAS_WRONG_PATH; | 198 return false; |
195 | 199 } |
196 if (!SdchManager::secure_scheme_supported() && referring_url.SchemeIsSecure()) | 200 if (path_.size() && !PathMatch(referring_url.path(), path_)) { |
197 return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME; | 201 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PATH); |
198 | 202 return false; |
199 if (referring_url.SchemeIsSecure() != url_.SchemeIsSecure()) | 203 } |
200 return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME; | 204 if (!SdchManager::secure_scheme_supported() && |
| 205 referring_url.SchemeIsSecure()) { |
| 206 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME); |
| 207 return false; |
| 208 } |
| 209 if (referring_url.SchemeIsSecure() != url_.SchemeIsSecure()) { |
| 210 SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME); |
| 211 return false; |
| 212 } |
201 | 213 |
202 // TODO(jar): Remove overly restrictive failsafe test (added per security | 214 // TODO(jar): Remove overly restrictive failsafe test (added per security |
203 // review) when we have a need to be more general. | 215 // review) when we have a need to be more general. |
204 if (!referring_url.SchemeIsHTTPOrHTTPS()) | 216 if (!referring_url.SchemeIsHTTPOrHTTPS()) { |
205 return SDCH_ATTEMPT_TO_DECODE_NON_HTTP_DATA; | 217 SdchErrorRecovery(ATTEMPT_TO_DECODE_NON_HTTP_DATA); |
| 218 return false; |
| 219 } |
206 | 220 |
207 return SDCH_OK; | 221 return true; |
208 } | 222 } |
209 | 223 |
210 // static | |
211 bool SdchManager::Dictionary::PathMatch(const std::string& path, | 224 bool SdchManager::Dictionary::PathMatch(const std::string& path, |
212 const std::string& restriction) { | 225 const std::string& restriction) { |
213 /* Must be either: | 226 /* Must be either: |
214 1. P2 is equal to P1 | 227 1. P2 is equal to P1 |
215 2. P2 is a prefix of P1 and either the final character in P2 is "/" or the | 228 2. P2 is a prefix of P1 and either the final character in P2 is "/" or the |
216 character following P2 in P1 is "/". | 229 character following P2 in P1 is "/". |
217 */ | 230 */ |
218 if (path == restriction) | 231 if (path == restriction) |
219 return true; | 232 return true; |
220 size_t prefix_length = restriction.size(); | 233 size_t prefix_length = restriction.size(); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
253 fetcher_->Cancel(); | 266 fetcher_->Cancel(); |
254 | 267 |
255 // Note that this may result in not having dictionaries we've advertised | 268 // Note that this may result in not having dictionaries we've advertised |
256 // for incoming responses. The window is relatively small (as ClearData() | 269 // for incoming responses. The window is relatively small (as ClearData() |
257 // is not expected to be called frequently), so we rely on meta-refresh | 270 // is not expected to be called frequently), so we rely on meta-refresh |
258 // to handle this case. | 271 // to handle this case. |
259 dictionaries_.clear(); | 272 dictionaries_.clear(); |
260 } | 273 } |
261 | 274 |
262 // static | 275 // static |
263 void SdchManager::SdchErrorRecovery(SdchProblemCode problem) { | 276 void SdchManager::SdchErrorRecovery(ProblemCodes problem) { |
264 UMA_HISTOGRAM_ENUMERATION( | 277 UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE); |
265 "Sdch3.ProblemCodes_5", problem, SDCH_MAX_PROBLEM_CODE); | |
266 } | 278 } |
267 | 279 |
268 void SdchManager::set_sdch_fetcher(scoped_ptr<SdchFetcher> fetcher) { | 280 void SdchManager::set_sdch_fetcher(scoped_ptr<SdchFetcher> fetcher) { |
269 DCHECK(CalledOnValidThread()); | 281 DCHECK(CalledOnValidThread()); |
270 fetcher_ = fetcher.Pass(); | 282 fetcher_ = fetcher.Pass(); |
271 } | 283 } |
272 | 284 |
273 // static | 285 // static |
274 void SdchManager::EnableSdchSupport(bool enabled) { | 286 void SdchManager::EnableSdchSupport(bool enabled) { |
275 g_sdch_enabled_ = enabled; | 287 g_sdch_enabled_ = enabled; |
276 } | 288 } |
277 | 289 |
278 // static | 290 // static |
279 void SdchManager::EnableSecureSchemeSupport(bool enabled) { | 291 void SdchManager::EnableSecureSchemeSupport(bool enabled) { |
280 g_secure_scheme_supported_ = enabled; | 292 g_secure_scheme_supported_ = enabled; |
281 } | 293 } |
282 | 294 |
283 void SdchManager::BlacklistDomain(const GURL& url, | 295 void SdchManager::BlacklistDomain(const GURL& url, |
284 SdchProblemCode blacklist_reason) { | 296 ProblemCodes blacklist_reason) { |
285 SetAllowLatencyExperiment(url, false); | 297 SetAllowLatencyExperiment(url, false); |
286 | 298 |
287 BlacklistInfo* blacklist_info = | 299 BlacklistInfo* blacklist_info = |
288 &blacklisted_domains_[base::StringToLowerASCII(url.host())]; | 300 &blacklisted_domains_[base::StringToLowerASCII(url.host())]; |
289 | 301 |
290 if (blacklist_info->count > 0) | 302 if (blacklist_info->count > 0) |
291 return; // Domain is already blacklisted. | 303 return; // Domain is already blacklisted. |
292 | 304 |
293 if (blacklist_info->exponential_count > (INT_MAX - 1) / 2) { | 305 if (blacklist_info->exponential_count > (INT_MAX - 1) / 2) { |
294 blacklist_info->exponential_count = INT_MAX; | 306 blacklist_info->exponential_count = INT_MAX; |
295 } else { | 307 } else { |
296 blacklist_info->exponential_count = | 308 blacklist_info->exponential_count = |
297 blacklist_info->exponential_count * 2 + 1; | 309 blacklist_info->exponential_count * 2 + 1; |
298 } | 310 } |
299 | 311 |
300 blacklist_info->count = blacklist_info->exponential_count; | 312 blacklist_info->count = blacklist_info->exponential_count; |
301 blacklist_info->reason = blacklist_reason; | 313 blacklist_info->reason = blacklist_reason; |
302 } | 314 } |
303 | 315 |
304 void SdchManager::BlacklistDomainForever(const GURL& url, | 316 void SdchManager::BlacklistDomainForever(const GURL& url, |
305 SdchProblemCode blacklist_reason) { | 317 ProblemCodes blacklist_reason) { |
306 SetAllowLatencyExperiment(url, false); | 318 SetAllowLatencyExperiment(url, false); |
307 | 319 |
308 BlacklistInfo* blacklist_info = | 320 BlacklistInfo* blacklist_info = |
309 &blacklisted_domains_[base::StringToLowerASCII(url.host())]; | 321 &blacklisted_domains_[base::StringToLowerASCII(url.host())]; |
310 blacklist_info->count = INT_MAX; | 322 blacklist_info->count = INT_MAX; |
311 blacklist_info->exponential_count = INT_MAX; | 323 blacklist_info->exponential_count = INT_MAX; |
312 blacklist_info->reason = blacklist_reason; | 324 blacklist_info->reason = blacklist_reason; |
313 } | 325 } |
314 | 326 |
315 void SdchManager::ClearBlacklistings() { | 327 void SdchManager::ClearBlacklistings() { |
316 blacklisted_domains_.clear(); | 328 blacklisted_domains_.clear(); |
317 } | 329 } |
318 | 330 |
319 void SdchManager::ClearDomainBlacklisting(const std::string& domain) { | 331 void SdchManager::ClearDomainBlacklisting(const std::string& domain) { |
320 BlacklistInfo* blacklist_info = &blacklisted_domains_[ | 332 BlacklistInfo* blacklist_info = &blacklisted_domains_[ |
321 base::StringToLowerASCII(domain)]; | 333 base::StringToLowerASCII(domain)]; |
322 blacklist_info->count = 0; | 334 blacklist_info->count = 0; |
323 blacklist_info->reason = SDCH_OK; | 335 blacklist_info->reason = MIN_PROBLEM_CODE; |
324 } | 336 } |
325 | 337 |
326 int SdchManager::BlackListDomainCount(const std::string& domain) { | 338 int SdchManager::BlackListDomainCount(const std::string& domain) { |
327 std::string domain_lower(base::StringToLowerASCII(domain)); | 339 std::string domain_lower(base::StringToLowerASCII(domain)); |
328 | 340 |
329 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) | 341 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) |
330 return 0; | 342 return 0; |
331 return blacklisted_domains_[domain_lower].count; | 343 return blacklisted_domains_[domain_lower].count; |
332 } | 344 } |
333 | 345 |
334 int SdchManager::BlacklistDomainExponential(const std::string& domain) { | 346 int SdchManager::BlacklistDomainExponential(const std::string& domain) { |
335 std::string domain_lower(base::StringToLowerASCII(domain)); | 347 std::string domain_lower(base::StringToLowerASCII(domain)); |
336 | 348 |
337 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) | 349 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) |
338 return 0; | 350 return 0; |
339 return blacklisted_domains_[domain_lower].exponential_count; | 351 return blacklisted_domains_[domain_lower].exponential_count; |
340 } | 352 } |
341 | 353 |
342 SdchProblemCode SdchManager::IsInSupportedDomain(const GURL& url) { | 354 bool SdchManager::IsInSupportedDomain(const GURL& url) { |
343 DCHECK(CalledOnValidThread()); | 355 DCHECK(CalledOnValidThread()); |
344 if (!g_sdch_enabled_ ) | 356 if (!g_sdch_enabled_ ) |
345 return SDCH_DISABLED; | 357 return false; |
346 | 358 |
347 if (!secure_scheme_supported() && url.SchemeIsSecure()) | 359 if (!secure_scheme_supported() && url.SchemeIsSecure()) |
348 return SDCH_SECURE_SCHEME_NOT_SUPPORTED; | 360 return false; |
349 | 361 |
350 if (blacklisted_domains_.empty()) | 362 if (blacklisted_domains_.empty()) |
351 return SDCH_OK; | 363 return true; |
352 | 364 |
353 DomainBlacklistInfo::iterator it = | 365 DomainBlacklistInfo::iterator it = |
354 blacklisted_domains_.find(base::StringToLowerASCII(url.host())); | 366 blacklisted_domains_.find(base::StringToLowerASCII(url.host())); |
355 if (blacklisted_domains_.end() == it || it->second.count == 0) | 367 if (blacklisted_domains_.end() == it || it->second.count == 0) |
356 return SDCH_OK; | 368 return true; |
357 | 369 |
358 UMA_HISTOGRAM_ENUMERATION( | 370 UMA_HISTOGRAM_ENUMERATION("Sdch3.BlacklistReason", it->second.reason, |
359 "Sdch3.BlacklistReason", it->second.reason, SDCH_MAX_PROBLEM_CODE); | 371 MAX_PROBLEM_CODE); |
| 372 SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET); |
360 | 373 |
361 int count = it->second.count - 1; | 374 int count = it->second.count - 1; |
362 if (count > 0) { | 375 if (count > 0) { |
363 it->second.count = count; | 376 it->second.count = count; |
364 } else { | 377 } else { |
365 it->second.count = 0; | 378 it->second.count = 0; |
366 it->second.reason = SDCH_OK; | 379 it->second.reason = MIN_PROBLEM_CODE; |
367 } | 380 } |
368 | 381 |
369 return SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET; | 382 return false; |
370 } | 383 } |
371 | 384 |
372 SdchProblemCode SdchManager::FetchDictionary(const GURL& request_url, | 385 void SdchManager::FetchDictionary(const GURL& request_url, |
373 const GURL& dictionary_url) { | 386 const GURL& dictionary_url) { |
374 DCHECK(CalledOnValidThread()); | 387 DCHECK(CalledOnValidThread()); |
375 SdchProblemCode rv = CanFetchDictionary(request_url, dictionary_url); | 388 if (CanFetchDictionary(request_url, dictionary_url) && fetcher_.get()) { |
376 if (rv != SDCH_OK) | |
377 return rv; | |
378 | |
379 if (fetcher_.get()) { | |
380 ++fetches_count_for_testing_; | 389 ++fetches_count_for_testing_; |
381 if (fetcher_->Schedule(dictionary_url)) | 390 fetcher_->Schedule(dictionary_url); |
382 return SDCH_DICTIONARY_PREVIOUSLY_SCHEDULED_TO_DOWNLOAD; | |
383 } | 391 } |
384 | |
385 return SDCH_OK; | |
386 } | 392 } |
387 | 393 |
388 SdchProblemCode SdchManager::CanFetchDictionary( | 394 bool SdchManager::CanFetchDictionary(const GURL& referring_url, |
389 const GURL& referring_url, | 395 const GURL& dictionary_url) const { |
390 const GURL& dictionary_url) const { | |
391 DCHECK(CalledOnValidThread()); | 396 DCHECK(CalledOnValidThread()); |
392 /* The user agent may retrieve a dictionary from the dictionary URL if all of | 397 /* The user agent may retrieve a dictionary from the dictionary URL if all of |
393 the following are true: | 398 the following are true: |
394 1 The dictionary URL host name matches the referrer URL host name and | 399 1 The dictionary URL host name matches the referrer URL host name and |
395 scheme. | 400 scheme. |
396 2 The dictionary URL host name domain matches the parent domain of the | 401 2 The dictionary URL host name domain matches the parent domain of the |
397 referrer URL host name | 402 referrer URL host name |
398 3 The parent domain of the referrer URL host name is not a top level | 403 3 The parent domain of the referrer URL host name is not a top level |
399 domain | 404 domain |
400 4 The dictionary URL is not an HTTPS URL. | 405 4 The dictionary URL is not an HTTPS URL. |
401 */ | 406 */ |
402 // Item (1) above implies item (2). Spec should be updated. | 407 // Item (1) above implies item (2). Spec should be updated. |
403 // I take "host name match" to be "is identical to" | 408 // I take "host name match" to be "is identical to" |
404 if (referring_url.host() != dictionary_url.host() || | 409 if (referring_url.host() != dictionary_url.host() || |
405 referring_url.scheme() != dictionary_url.scheme()) | 410 referring_url.scheme() != dictionary_url.scheme()) { |
406 return SDCH_DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST; | 411 SdchErrorRecovery(DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST); |
407 | 412 return false; |
408 if (!secure_scheme_supported() && referring_url.SchemeIsSecure()) | 413 } |
409 return SDCH_DICTIONARY_SELECTED_FOR_SSL; | 414 if (!secure_scheme_supported() && referring_url.SchemeIsSecure()) { |
| 415 SdchErrorRecovery(DICTIONARY_SELECTED_FOR_SSL); |
| 416 return false; |
| 417 } |
410 | 418 |
411 // TODO(jar): Remove this failsafe conservative hack which is more restrictive | 419 // TODO(jar): Remove this failsafe conservative hack which is more restrictive |
412 // than current SDCH spec when needed, and justified by security audit. | 420 // than current SDCH spec when needed, and justified by security audit. |
413 if (!referring_url.SchemeIsHTTPOrHTTPS()) | 421 if (!referring_url.SchemeIsHTTPOrHTTPS()) { |
414 return SDCH_DICTIONARY_SELECTED_FROM_NON_HTTP; | 422 SdchErrorRecovery(DICTIONARY_SELECTED_FROM_NON_HTTP); |
| 423 return false; |
| 424 } |
415 | 425 |
416 return SDCH_OK; | 426 return true; |
417 } | 427 } |
418 | 428 |
419 SdchProblemCode SdchManager::GetVcdiffDictionary( | 429 void SdchManager::GetVcdiffDictionary( |
420 const std::string& server_hash, | 430 const std::string& server_hash, |
421 const GURL& referring_url, | 431 const GURL& referring_url, |
422 scoped_refptr<Dictionary>* dictionary) { | 432 scoped_refptr<Dictionary>* dictionary) { |
423 DCHECK(CalledOnValidThread()); | 433 DCHECK(CalledOnValidThread()); |
424 *dictionary = NULL; | 434 *dictionary = NULL; |
425 DictionaryMap::iterator it = dictionaries_.find(server_hash); | 435 DictionaryMap::iterator it = dictionaries_.find(server_hash); |
426 if (it == dictionaries_.end()) | 436 if (it == dictionaries_.end()) { |
427 return SDCH_DICTIONARY_HASH_NOT_FOUND; | 437 return; |
428 | 438 } |
429 scoped_refptr<Dictionary> matching_dictionary = it->second; | 439 scoped_refptr<Dictionary> matching_dictionary = it->second; |
430 | 440 if (!IsInSupportedDomain(referring_url)) |
431 SdchProblemCode rv = IsInSupportedDomain(referring_url); | 441 return; |
432 if (rv != SDCH_OK) | 442 if (!matching_dictionary->CanUse(referring_url)) |
433 return rv; | 443 return; |
434 | 444 *dictionary = matching_dictionary; |
435 rv = matching_dictionary->CanUse(referring_url); | |
436 if (rv == SDCH_OK) | |
437 *dictionary = matching_dictionary; | |
438 return rv; | |
439 } | 445 } |
440 | 446 |
441 // TODO(jar): If we have evictions from the dictionaries_, then we need to | 447 // TODO(jar): If we have evictions from the dictionaries_, then we need to |
442 // change this interface to return a list of reference counted Dictionary | 448 // change this interface to return a list of reference counted Dictionary |
443 // instances that can be used if/when a server specifies one. | 449 // instances that can be used if/when a server specifies one. |
444 void SdchManager::GetAvailDictionaryList(const GURL& target_url, | 450 void SdchManager::GetAvailDictionaryList(const GURL& target_url, |
445 std::string* list) { | 451 std::string* list) { |
446 DCHECK(CalledOnValidThread()); | 452 DCHECK(CalledOnValidThread()); |
447 int count = 0; | 453 int count = 0; |
448 for (DictionaryMap::iterator it = dictionaries_.begin(); | 454 for (DictionaryMap::iterator it = dictionaries_.begin(); |
449 it != dictionaries_.end(); ++it) { | 455 it != dictionaries_.end(); ++it) { |
450 SdchProblemCode rv = IsInSupportedDomain(target_url); | 456 if (!IsInSupportedDomain(target_url)) |
451 if (rv != SDCH_OK) | |
452 continue; | 457 continue; |
453 | 458 if (!it->second->CanAdvertise(target_url)) |
454 if (it->second->CanAdvertise(target_url) != SDCH_OK) | |
455 continue; | 459 continue; |
456 ++count; | 460 ++count; |
457 if (!list->empty()) | 461 if (!list->empty()) |
458 list->append(","); | 462 list->append(","); |
459 list->append(it->second->client_hash()); | 463 list->append(it->second->client_hash()); |
460 } | 464 } |
461 // Watch to see if we have corrupt or numerous dictionaries. | 465 // Watch to see if we have corrupt or numerous dictionaries. |
462 if (count > 0) | 466 if (count > 0) |
463 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); | 467 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count); |
464 } | 468 } |
(...skipping 24 matching lines...) Expand all Loading... |
489 | 493 |
490 void SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) { | 494 void SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) { |
491 DCHECK(CalledOnValidThread()); | 495 DCHECK(CalledOnValidThread()); |
492 if (enable) { | 496 if (enable) { |
493 allow_latency_experiment_.insert(url.host()); | 497 allow_latency_experiment_.insert(url.host()); |
494 return; | 498 return; |
495 } | 499 } |
496 ExperimentSet::iterator it = allow_latency_experiment_.find(url.host()); | 500 ExperimentSet::iterator it = allow_latency_experiment_.find(url.host()); |
497 if (allow_latency_experiment_.end() == it) | 501 if (allow_latency_experiment_.end() == it) |
498 return; // It was already erased, or never allowed. | 502 return; // It was already erased, or never allowed. |
499 SdchErrorRecovery(SDCH_LATENCY_TEST_DISALLOWED); | 503 SdchErrorRecovery(LATENCY_TEST_DISALLOWED); |
500 allow_latency_experiment_.erase(it); | 504 allow_latency_experiment_.erase(it); |
501 } | 505 } |
502 | 506 |
503 SdchProblemCode SdchManager::AddSdchDictionary( | 507 void SdchManager::AddSdchDictionary(const std::string& dictionary_text, |
504 const std::string& dictionary_text, | |
505 const GURL& dictionary_url) { | 508 const GURL& dictionary_url) { |
506 DCHECK(CalledOnValidThread()); | 509 DCHECK(CalledOnValidThread()); |
507 std::string client_hash; | 510 std::string client_hash; |
508 std::string server_hash; | 511 std::string server_hash; |
509 GenerateHash(dictionary_text, &client_hash, &server_hash); | 512 GenerateHash(dictionary_text, &client_hash, &server_hash); |
510 if (dictionaries_.find(server_hash) != dictionaries_.end()) | 513 if (dictionaries_.find(server_hash) != dictionaries_.end()) { |
511 return SDCH_DICTIONARY_ALREADY_LOADED; // Already loaded. | 514 SdchErrorRecovery(DICTIONARY_ALREADY_LOADED); |
| 515 return; // Already loaded. |
| 516 } |
512 | 517 |
513 std::string domain, path; | 518 std::string domain, path; |
514 std::set<int> ports; | 519 std::set<int> ports; |
515 base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30)); | 520 base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30)); |
516 | 521 |
517 if (dictionary_text.empty()) | 522 if (dictionary_text.empty()) { |
518 return SDCH_DICTIONARY_HAS_NO_TEXT; // Missing header. | 523 SdchErrorRecovery(DICTIONARY_HAS_NO_TEXT); |
| 524 return; // Missing header. |
| 525 } |
519 | 526 |
520 size_t header_end = dictionary_text.find("\n\n"); | 527 size_t header_end = dictionary_text.find("\n\n"); |
521 if (std::string::npos == header_end) | 528 if (std::string::npos == header_end) { |
522 return SDCH_DICTIONARY_HAS_NO_HEADER; // Missing header. | 529 SdchErrorRecovery(DICTIONARY_HAS_NO_HEADER); |
523 | 530 return; // Missing header. |
| 531 } |
524 size_t line_start = 0; // Start of line being parsed. | 532 size_t line_start = 0; // Start of line being parsed. |
525 while (1) { | 533 while (1) { |
526 size_t line_end = dictionary_text.find('\n', line_start); | 534 size_t line_end = dictionary_text.find('\n', line_start); |
527 DCHECK(std::string::npos != line_end); | 535 DCHECK(std::string::npos != line_end); |
528 DCHECK_LE(line_end, header_end); | 536 DCHECK_LE(line_end, header_end); |
529 | 537 |
530 size_t colon_index = dictionary_text.find(':', line_start); | 538 size_t colon_index = dictionary_text.find(':', line_start); |
531 if (std::string::npos == colon_index) | 539 if (std::string::npos == colon_index) { |
532 return SDCH_DICTIONARY_HEADER_LINE_MISSING_COLON; // Illegal line missing | 540 SdchErrorRecovery(DICTIONARY_HEADER_LINE_MISSING_COLON); |
533 // a colon. | 541 return; // Illegal line missing a colon. |
| 542 } |
534 | 543 |
535 if (colon_index > line_end) | 544 if (colon_index > line_end) |
536 break; | 545 break; |
537 | 546 |
538 size_t value_start = dictionary_text.find_first_not_of(" \t", | 547 size_t value_start = dictionary_text.find_first_not_of(" \t", |
539 colon_index + 1); | 548 colon_index + 1); |
540 if (std::string::npos != value_start) { | 549 if (std::string::npos != value_start) { |
541 if (value_start >= line_end) | 550 if (value_start >= line_end) |
542 break; | 551 break; |
543 std::string name(dictionary_text, line_start, colon_index - line_start); | 552 std::string name(dictionary_text, line_start, colon_index - line_start); |
544 std::string value(dictionary_text, value_start, line_end - value_start); | 553 std::string value(dictionary_text, value_start, line_end - value_start); |
545 name = base::StringToLowerASCII(name); | 554 name = base::StringToLowerASCII(name); |
546 if (name == "domain") { | 555 if (name == "domain") { |
547 domain = value; | 556 domain = value; |
548 } else if (name == "path") { | 557 } else if (name == "path") { |
549 path = value; | 558 path = value; |
550 } else if (name == "format-version") { | 559 } else if (name == "format-version") { |
551 if (value != "1.0") | 560 if (value != "1.0") |
552 return SDCH_DICTIONARY_UNSUPPORTED_VERSION; | 561 return; |
553 } else if (name == "max-age") { | 562 } else if (name == "max-age") { |
554 int64 seconds; | 563 int64 seconds; |
555 base::StringToInt64(value, &seconds); | 564 base::StringToInt64(value, &seconds); |
556 expiration = base::Time::Now() + base::TimeDelta::FromSeconds(seconds); | 565 expiration = base::Time::Now() + base::TimeDelta::FromSeconds(seconds); |
557 } else if (name == "port") { | 566 } else if (name == "port") { |
558 int port; | 567 int port; |
559 base::StringToInt(value, &port); | 568 base::StringToInt(value, &port); |
560 if (port >= 0) | 569 if (port >= 0) |
561 ports.insert(port); | 570 ports.insert(port); |
562 } | 571 } |
563 } | 572 } |
564 | 573 |
565 if (line_end >= header_end) | 574 if (line_end >= header_end) |
566 break; | 575 break; |
567 line_start = line_end + 1; | 576 line_start = line_end + 1; |
568 } | 577 } |
569 | 578 |
570 // Narrow fix for http://crbug.com/389451. | 579 // Narrow fix for http://crbug.com/389451. |
571 GURL dictionary_url_normalized(dictionary_url); | 580 GURL dictionary_url_normalized(dictionary_url); |
572 StripTrailingDot(&dictionary_url_normalized); | 581 StripTrailingDot(&dictionary_url_normalized); |
573 | 582 |
574 SdchProblemCode rv = IsInSupportedDomain(dictionary_url_normalized); | 583 if (!IsInSupportedDomain(dictionary_url_normalized)) |
575 if (rv != SDCH_OK) | 584 return; |
576 return rv; | |
577 | 585 |
578 rv = Dictionary::CanSet(domain, path, ports, dictionary_url_normalized); | 586 if (!Dictionary::CanSet(domain, path, ports, dictionary_url_normalized)) |
579 if (rv != SDCH_OK) | 587 return; |
580 return rv; | |
581 | 588 |
582 // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of | 589 // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of |
583 // useless dictionaries. We should probably have a cache eviction plan, | 590 // useless dictionaries. We should probably have a cache eviction plan, |
584 // instead of just blocking additions. For now, with the spec in flux, it | 591 // instead of just blocking additions. For now, with the spec in flux, it |
585 // is probably not worth doing eviction handling. | 592 // is probably not worth doing eviction handling. |
586 if (kMaxDictionarySize < dictionary_text.size()) | 593 if (kMaxDictionarySize < dictionary_text.size()) { |
587 return SDCH_DICTIONARY_IS_TOO_LARGE; | 594 SdchErrorRecovery(DICTIONARY_IS_TOO_LARGE); |
588 | 595 return; |
589 if (kMaxDictionaryCount <= dictionaries_.size()) | 596 } |
590 return SDCH_DICTIONARY_COUNT_EXCEEDED; | 597 if (kMaxDictionaryCount <= dictionaries_.size()) { |
| 598 SdchErrorRecovery(DICTIONARY_COUNT_EXCEEDED); |
| 599 return; |
| 600 } |
591 | 601 |
592 UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size()); | 602 UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size()); |
593 DVLOG(1) << "Loaded dictionary with client hash " << client_hash | 603 DVLOG(1) << "Loaded dictionary with client hash " << client_hash |
594 << " and server hash " << server_hash; | 604 << " and server hash " << server_hash; |
595 Dictionary* dictionary = | 605 Dictionary* dictionary = |
596 new Dictionary(dictionary_text, header_end + 2, client_hash, | 606 new Dictionary(dictionary_text, header_end + 2, client_hash, |
597 dictionary_url_normalized, domain, | 607 dictionary_url_normalized, domain, |
598 path, expiration, ports); | 608 path, expiration, ports); |
599 dictionaries_[server_hash] = dictionary; | 609 dictionaries_[server_hash] = dictionary; |
600 return SDCH_OK; | 610 return; |
601 } | 611 } |
602 | 612 |
603 // static | 613 // static |
604 void SdchManager::UrlSafeBase64Encode(const std::string& input, | 614 void SdchManager::UrlSafeBase64Encode(const std::string& input, |
605 std::string* output) { | 615 std::string* output) { |
606 // Since this is only done during a dictionary load, and hashes are only 8 | 616 // Since this is only done during a dictionary load, and hashes are only 8 |
607 // characters, we just do the simple fixup, rather than rewriting the encoder. | 617 // characters, we just do the simple fixup, rather than rewriting the encoder. |
608 base::Base64Encode(input, output); | 618 base::Base64Encode(input, output); |
609 std::replace(output->begin(), output->end(), '+', '-'); | 619 std::replace(output->begin(), output->end(), '+', '-'); |
610 std::replace(output->begin(), output->end(), '/', '_'); | 620 std::replace(output->begin(), output->end(), '/', '_'); |
611 } | 621 } |
612 | 622 |
613 base::Value* SdchManager::SdchInfoToValue() const { | |
614 base::DictionaryValue* value = new base::DictionaryValue(); | |
615 | |
616 value->SetBoolean("sdch_enabled", sdch_enabled()); | |
617 value->SetBoolean("secure_scheme_support", secure_scheme_supported()); | |
618 | |
619 base::ListValue* entry_list = new base::ListValue(); | |
620 for (DictionaryMap::const_iterator it = dictionaries_.begin(); | |
621 it != dictionaries_.end(); | |
622 ++it) { | |
623 base::DictionaryValue* entry_dict = new base::DictionaryValue(); | |
624 entry_dict->SetString("url", it->second->url().spec()); | |
625 entry_dict->SetString("client_hash", it->second->client_hash()); | |
626 entry_dict->SetString("domain", it->second->domain()); | |
627 entry_dict->SetString("path", it->second->path()); | |
628 base::ListValue* port_list = new base::ListValue(); | |
629 for (std::set<int>::const_iterator port_it = it->second->ports().begin(); | |
630 port_it != it->second->ports().end(); | |
631 ++port_it) { | |
632 port_list->AppendInteger(*port_it); | |
633 } | |
634 entry_dict->Set("ports", port_list); | |
635 entry_dict->SetString("server_hash", it->first); | |
636 entry_list->Append(entry_dict); | |
637 } | |
638 value->Set("dictionaries", entry_list); | |
639 | |
640 entry_list = new base::ListValue(); | |
641 for (DomainBlacklistInfo::const_iterator it = blacklisted_domains_.begin(); | |
642 it != blacklisted_domains_.end(); | |
643 ++it) { | |
644 if (it->second.count == 0) | |
645 continue; | |
646 base::DictionaryValue* entry_dict = new base::DictionaryValue(); | |
647 entry_dict->SetString("domain", it->first); | |
648 if (it->second.count != INT_MAX) | |
649 entry_dict->SetInteger("tries", it->second.count); | |
650 entry_dict->SetInteger("reason", it->second.reason); | |
651 entry_list->Append(entry_dict); | |
652 } | |
653 value->Set("blacklisted", entry_list); | |
654 | |
655 return value; | |
656 } | |
657 | |
658 } // namespace net | 623 } // namespace net |
OLD | NEW |