Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(40)

Side by Side Diff: net/base/sdch_manager.cc

Issue 423813002: Sdch view for net-internals (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Review fixes. Lots. Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698