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