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