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