blob: 27d86ee858f61e581c2197dca5edfe62eb2e2ea5 [file] [log] [blame]
[email protected]be28b5f42012-07-20 11:31:251// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]60889422008-09-23 01:18:162// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]e83326f2010-07-31 17:29:255#include "net/base/sdch_manager.h"
6
[email protected]978df342009-11-24 06:21:537#include "base/base64.h"
[email protected]60889422008-09-23 01:18:168#include "base/logging.h"
[email protected]835d7c82010-10-14 04:38:389#include "base/metrics/histogram.h"
[email protected]4b355212013-06-11 10:35:1910#include "base/strings/string_number_conversions.h"
11#include "base/strings/string_util.h"
rdsmith81f607562014-11-21 18:35:1612#include "base/time/default_clock.h"
baranovichc5e38652014-11-14 03:08:1513#include "base/values.h"
[email protected]4b559b4d2011-04-14 17:37:1414#include "crypto/sha2.h"
[email protected]be28b5f42012-07-20 11:31:2515#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
rdsmith47c133b2014-11-06 19:02:3016#include "net/base/sdch_observer.h"
[email protected]60889422008-09-23 01:18:1617#include "net/url_request/url_request_http_job.h"
18
rdsmith14fd6592014-09-23 21:13:1019namespace {
20
21void StripTrailingDot(GURL* gurl) {
22 std::string host(gurl->host());
23
24 if (host.empty())
25 return;
26
27 if (*host.rbegin() != '.')
28 return;
29
30 host.resize(host.size() - 1);
31
32 GURL::Replacements replacements;
33 replacements.SetHostStr(host);
34 *gurl = gurl->ReplaceComponents(replacements);
35 return;
36}
37
38} // namespace
39
[email protected]ba5734e2011-02-01 03:42:1140namespace net {
[email protected]60889422008-09-23 01:18:1641
rdsmithc4add97e2014-12-17 17:24:3142// Workaround for http://crbug.com/437794; remove when fixed.
43#if defined(OS_IOS)
44// static
45bool SdchManager::g_sdch_enabled_ = false;
46#else
[email protected]075bc8cb2008-11-18 20:22:5247// static
rdsmith24322bca2014-10-29 15:50:2048bool SdchManager::g_sdch_enabled_ = true;
rdsmithc4add97e2014-12-17 17:24:3149#endif
[email protected]ef993102014-01-10 19:29:3750
rdsmith24322bca2014-10-29 15:50:2051// static
52bool SdchManager::g_secure_scheme_supported_ = true;
53
[email protected]4b3c95dd2011-01-07 23:02:1154SdchManager::Dictionary::Dictionary(const std::string& dictionary_text,
[email protected]ba5734e2011-02-01 03:42:1155 size_t offset,
56 const std::string& client_hash,
rdsmitha8a4e3c82015-01-08 20:18:1757 const std::string& server_hash,
[email protected]ba5734e2011-02-01 03:42:1158 const GURL& gurl,
59 const std::string& domain,
60 const std::string& path,
61 const base::Time& expiration,
62 const std::set<int>& ports)
63 : text_(dictionary_text, offset),
64 client_hash_(client_hash),
rdsmitha8a4e3c82015-01-08 20:18:1765 server_hash_(server_hash),
[email protected]ba5734e2011-02-01 03:42:1166 url_(gurl),
67 domain_(domain),
68 path_(path),
69 expiration_(expiration),
rdsmith81f607562014-11-21 18:35:1670 ports_(ports),
71 clock_(new base::DefaultClock) {
[email protected]4b3c95dd2011-01-07 23:02:1172}
73
rdsmith81f607562014-11-21 18:35:1674SdchManager::Dictionary::Dictionary(const SdchManager::Dictionary& rhs)
75 : text_(rhs.text_),
76 client_hash_(rhs.client_hash_),
rdsmitha8a4e3c82015-01-08 20:18:1777 server_hash_(rhs.server_hash_),
rdsmith81f607562014-11-21 18:35:1678 url_(rhs.url_),
79 domain_(rhs.domain_),
80 path_(rhs.path_),
81 expiration_(rhs.expiration_),
82 ports_(rhs.ports_),
rdsmitha8a4e3c82015-01-08 20:18:1783 clock_(new base::DefaultClock) {
84}
[email protected]4b3c95dd2011-01-07 23:02:1185
rdsmith81f607562014-11-21 18:35:1686SdchManager::Dictionary::~Dictionary() {}
[email protected]4b3c95dd2011-01-07 23:02:1187
[email protected]4b3c95dd2011-01-07 23:02:1188// Security functions restricting loads and use of dictionaries.
89
[email protected]60889422008-09-23 01:18:1690// static
baranovichc5e38652014-11-14 03:08:1591SdchProblemCode SdchManager::Dictionary::CanSet(const std::string& domain,
92 const std::string& path,
93 const std::set<int>& ports,
94 const GURL& dictionary_url) {
[email protected]4b3c95dd2011-01-07 23:02:1195 /*
96 A dictionary is invalid and must not be stored if any of the following are
97 true:
98 1. The dictionary has no Domain attribute.
99 2. The effective host name that derives from the referer URL host name does
100 not domain-match the Domain attribute.
101 3. The Domain attribute is a top level domain.
102 4. The referer URL host is a host domain name (not IP address) and has the
103 form HD, where D is the value of the Domain attribute, and H is a string
104 that contains one or more dots.
105 5. If the dictionary has a Port attribute and the referer URL's port was not
106 in the list.
107 */
108
109 // TODO(jar): Redirects in dictionary fetches might plausibly be problematic,
110 // and hence the conservative approach is to not allow any redirects (if there
111 // were any... then don't allow the dictionary to be set).
112
baranovichc5e38652014-11-14 03:08:15113 if (domain.empty())
114 return SDCH_DICTIONARY_MISSING_DOMAIN_SPECIFIER; // Domain is required.
115
tzik2f98de302014-11-04 06:38:19116 if (registry_controlled_domains::GetDomainAndRegistry(
baranovichc5e38652014-11-14 03:08:15117 domain, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)
118 .empty()) {
119 return SDCH_DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN; // domain was a TLD.
tzik2f98de302014-11-04 06:38:19120 }
baranovichc5e38652014-11-14 03:08:15121
122 if (!Dictionary::DomainMatch(dictionary_url, domain))
123 return SDCH_DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL;
[email protected]4b3c95dd2011-01-07 23:02:11124
125 std::string referrer_url_host = dictionary_url.host();
126 size_t postfix_domain_index = referrer_url_host.rfind(domain);
127 // See if it is indeed a postfix, or just an internal string.
128 if (referrer_url_host.size() == postfix_domain_index + domain.size()) {
129 // It is a postfix... so check to see if there's a dot in the prefix.
130 size_t end_of_host_index = referrer_url_host.find_first_of('.');
baranovichc5e38652014-11-14 03:08:15131 if (referrer_url_host.npos != end_of_host_index &&
[email protected]4b3c95dd2011-01-07 23:02:11132 end_of_host_index < postfix_domain_index) {
baranovichc5e38652014-11-14 03:08:15133 return SDCH_DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX;
[email protected]4b3c95dd2011-01-07 23:02:11134 }
135 }
136
baranovichc5e38652014-11-14 03:08:15137 if (!ports.empty() && 0 == ports.count(dictionary_url.EffectiveIntPort()))
138 return SDCH_DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL;
139
140 return SDCH_OK;
[email protected]60889422008-09-23 01:18:16141}
142
baranovichc5e38652014-11-14 03:08:15143SdchProblemCode SdchManager::Dictionary::CanUse(
rdsmith81f607562014-11-21 18:35:16144 const GURL& target_url) const {
[email protected]4b3c95dd2011-01-07 23:02:11145 /*
146 1. The request URL's host name domain-matches the Domain attribute of the
147 dictionary.
148 2. If the dictionary has a Port attribute, the request port is one of the
149 ports listed in the Port attribute.
150 3. The request URL path-matches the path attribute of the dictionary.
151 4. The request is not an HTTPS request.
[email protected]ef993102014-01-10 19:29:37152 We can override (ignore) item (4) only when we have explicitly enabled
[email protected]ed2f7cc2014-07-08 02:35:49153 HTTPS support AND the dictionary acquisition scheme matches the target
154 url scheme.
155 */
rdsmith81f607562014-11-21 18:35:16156 if (!DomainMatch(target_url, domain_))
baranovichc5e38652014-11-14 03:08:15157 return SDCH_DICTIONARY_FOUND_HAS_WRONG_DOMAIN;
158
rdsmith81f607562014-11-21 18:35:16159 if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort()))
baranovichc5e38652014-11-14 03:08:15160 return SDCH_DICTIONARY_FOUND_HAS_WRONG_PORT_LIST;
161
rdsmith81f607562014-11-21 18:35:16162 if (path_.size() && !PathMatch(target_url.path(), path_))
baranovichc5e38652014-11-14 03:08:15163 return SDCH_DICTIONARY_FOUND_HAS_WRONG_PATH;
164
rdsmith81f607562014-11-21 18:35:16165 if (!SdchManager::secure_scheme_supported() && target_url.SchemeIsSecure())
baranovichc5e38652014-11-14 03:08:15166 return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME;
167
rdsmith81f607562014-11-21 18:35:16168 if (target_url.SchemeIsSecure() != url_.SchemeIsSecure())
baranovichc5e38652014-11-14 03:08:15169 return SDCH_DICTIONARY_FOUND_HAS_WRONG_SCHEME;
[email protected]4b3c95dd2011-01-07 23:02:11170
171 // TODO(jar): Remove overly restrictive failsafe test (added per security
172 // review) when we have a need to be more general.
rdsmith81f607562014-11-21 18:35:16173 if (!target_url.SchemeIsHTTPOrHTTPS())
baranovichc5e38652014-11-14 03:08:15174 return SDCH_ATTEMPT_TO_DECODE_NON_HTTP_DATA;
[email protected]4b3c95dd2011-01-07 23:02:11175
baranovichc5e38652014-11-14 03:08:15176 return SDCH_OK;
[email protected]4b3c95dd2011-01-07 23:02:11177}
178
baranovichc5e38652014-11-14 03:08:15179// static
[email protected]4b3c95dd2011-01-07 23:02:11180bool SdchManager::Dictionary::PathMatch(const std::string& path,
181 const std::string& restriction) {
182 /* Must be either:
183 1. P2 is equal to P1
184 2. P2 is a prefix of P1 and either the final character in P2 is "/" or the
185 character following P2 in P1 is "/".
186 */
187 if (path == restriction)
188 return true;
189 size_t prefix_length = restriction.size();
190 if (prefix_length > path.size())
191 return false; // Can't be a prefix.
192 if (0 != path.compare(0, prefix_length, restriction))
193 return false;
194 return restriction[prefix_length - 1] == '/' || path[prefix_length] == '/';
[email protected]c631b6aa2008-10-15 21:21:37195}
196
197// static
[email protected]4b3c95dd2011-01-07 23:02:11198bool SdchManager::Dictionary::DomainMatch(const GURL& gurl,
199 const std::string& restriction) {
200 // TODO(jar): This is not precisely a domain match definition.
201 return gurl.DomainIs(restriction.data(), restriction.size());
[email protected]6a1f7dd42009-01-20 18:15:02202}
[email protected]c631b6aa2008-10-15 21:21:37203
rdsmith81f607562014-11-21 18:35:16204bool SdchManager::Dictionary::Expired() const {
205 return clock_->Now() > expiration_;
206}
207
208void SdchManager::Dictionary::SetClockForTesting(
209 scoped_ptr<base::Clock> clock) {
210 clock_ = clock.Pass();
211}
212
213SdchManager::DictionarySet::DictionarySet() {}
214
215SdchManager::DictionarySet::~DictionarySet() {}
216
217std::string SdchManager::DictionarySet::GetDictionaryClientHashList() const {
218 std::string result;
219 bool first = true;
220 for (const auto& entry: dictionaries_) {
221 if (!first)
222 result.append(",");
223
224 result.append(entry.second->data.client_hash());
225 first = false;
226 }
227 return result;
228}
229
230const SdchManager::Dictionary* SdchManager::DictionarySet::GetDictionary(
231 const std::string& hash) const {
232 auto it = dictionaries_.find(hash);
233 if (it == dictionaries_.end())
234 return NULL;
235
236 return &it->second->data;
237}
238
239bool SdchManager::DictionarySet::Empty() const {
240 return dictionaries_.empty();
241}
242
243void SdchManager::DictionarySet::AddDictionary(
244 const std::string& server_hash,
245 const scoped_refptr<base::RefCountedData<SdchManager::Dictionary>>&
246 dictionary) {
247 DCHECK(dictionaries_.end() == dictionaries_.find(server_hash));
248
249 dictionaries_[server_hash] = dictionary;
250}
251
rdsmith47c133b2014-11-06 19:02:30252SdchManager::SdchManager() {
253 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]60889422008-09-23 01:18:16254}
255
256SdchManager::~SdchManager() {
rdsmith47c133b2014-11-06 19:02:30257 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]60889422008-09-23 01:18:16258 while (!dictionaries_.empty()) {
rdsmith81f607562014-11-21 18:35:16259 auto it = dictionaries_.begin();
[email protected]60889422008-09-23 01:18:16260 dictionaries_.erase(it->first);
261 }
[email protected]4b3c95dd2011-01-07 23:02:11262}
263
[email protected]3de57172014-06-19 07:43:50264void SdchManager::ClearData() {
265 blacklisted_domains_.clear();
[email protected]3de57172014-06-19 07:43:50266 allow_latency_experiment_.clear();
[email protected]3de57172014-06-19 07:43:50267 dictionaries_.clear();
rdsmith47c133b2014-11-06 19:02:30268 FOR_EACH_OBSERVER(SdchObserver, observers_, OnClearDictionaries(this));
[email protected]3de57172014-06-19 07:43:50269}
270
[email protected]4b3c95dd2011-01-07 23:02:11271// static
baranovichc5e38652014-11-14 03:08:15272void SdchManager::SdchErrorRecovery(SdchProblemCode problem) {
273 UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_5", problem,
274 SDCH_MAX_PROBLEM_CODE);
[email protected]4b3c95dd2011-01-07 23:02:11275}
276
[email protected]3b543ab2011-09-17 21:47:00277// static
278void SdchManager::EnableSdchSupport(bool enabled) {
279 g_sdch_enabled_ = enabled;
[email protected]4b3c95dd2011-01-07 23:02:11280}
281
282// static
[email protected]ef993102014-01-10 19:29:37283void SdchManager::EnableSecureSchemeSupport(bool enabled) {
284 g_secure_scheme_supported_ = enabled;
285}
286
[email protected]6121e11a2014-08-13 19:10:39287void SdchManager::BlacklistDomain(const GURL& url,
baranovichc5e38652014-11-14 03:08:15288 SdchProblemCode blacklist_reason) {
[email protected]6a586762014-06-15 16:02:22289 SetAllowLatencyExperiment(url, false);
[email protected]6a1f7dd42009-01-20 18:15:02290
[email protected]6121e11a2014-08-13 19:10:39291 BlacklistInfo* blacklist_info =
292 &blacklisted_domains_[base::StringToLowerASCII(url.host())];
293
294 if (blacklist_info->count > 0)
[email protected]6a1f7dd42009-01-20 18:15:02295 return; // Domain is already blacklisted.
296
[email protected]6121e11a2014-08-13 19:10:39297 if (blacklist_info->exponential_count > (INT_MAX - 1) / 2) {
298 blacklist_info->exponential_count = INT_MAX;
299 } else {
300 blacklist_info->exponential_count =
301 blacklist_info->exponential_count * 2 + 1;
302 }
[email protected]6a1f7dd42009-01-20 18:15:02303
[email protected]6121e11a2014-08-13 19:10:39304 blacklist_info->count = blacklist_info->exponential_count;
305 blacklist_info->reason = blacklist_reason;
[email protected]6a1f7dd42009-01-20 18:15:02306}
307
[email protected]6121e11a2014-08-13 19:10:39308void SdchManager::BlacklistDomainForever(const GURL& url,
baranovichc5e38652014-11-14 03:08:15309 SdchProblemCode blacklist_reason) {
[email protected]6a586762014-06-15 16:02:22310 SetAllowLatencyExperiment(url, false);
[email protected]6a1f7dd42009-01-20 18:15:02311
[email protected]6121e11a2014-08-13 19:10:39312 BlacklistInfo* blacklist_info =
313 &blacklisted_domains_[base::StringToLowerASCII(url.host())];
314 blacklist_info->count = INT_MAX;
315 blacklist_info->exponential_count = INT_MAX;
316 blacklist_info->reason = blacklist_reason;
[email protected]c631b6aa2008-10-15 21:21:37317}
318
[email protected]4b3c95dd2011-01-07 23:02:11319void SdchManager::ClearBlacklistings() {
[email protected]6a586762014-06-15 16:02:22320 blacklisted_domains_.clear();
[email protected]4b3c95dd2011-01-07 23:02:11321}
322
[email protected]4b3c95dd2011-01-07 23:02:11323void SdchManager::ClearDomainBlacklisting(const std::string& domain) {
[email protected]6121e11a2014-08-13 19:10:39324 BlacklistInfo* blacklist_info = &blacklisted_domains_[
325 base::StringToLowerASCII(domain)];
326 blacklist_info->count = 0;
baranovichc5e38652014-11-14 03:08:15327 blacklist_info->reason = SDCH_OK;
[email protected]4b3c95dd2011-01-07 23:02:11328}
329
[email protected]4b3c95dd2011-01-07 23:02:11330int SdchManager::BlackListDomainCount(const std::string& domain) {
[email protected]6121e11a2014-08-13 19:10:39331 std::string domain_lower(base::StringToLowerASCII(domain));
332
333 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower))
[email protected]4b3c95dd2011-01-07 23:02:11334 return 0;
[email protected]6121e11a2014-08-13 19:10:39335 return blacklisted_domains_[domain_lower].count;
[email protected]4b3c95dd2011-01-07 23:02:11336}
337
[email protected]4b3c95dd2011-01-07 23:02:11338int SdchManager::BlacklistDomainExponential(const std::string& domain) {
[email protected]6121e11a2014-08-13 19:10:39339 std::string domain_lower(base::StringToLowerASCII(domain));
340
341 if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower))
[email protected]4b3c95dd2011-01-07 23:02:11342 return 0;
[email protected]6121e11a2014-08-13 19:10:39343 return blacklisted_domains_[domain_lower].exponential_count;
[email protected]c631b6aa2008-10-15 21:21:37344}
345
baranovichc5e38652014-11-14 03:08:15346SdchProblemCode SdchManager::IsInSupportedDomain(const GURL& url) {
rdsmith47c133b2014-11-06 19:02:30347 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]3b543ab2011-09-17 21:47:00348 if (!g_sdch_enabled_ )
baranovichc5e38652014-11-14 03:08:15349 return SDCH_DISABLED;
[email protected]c631b6aa2008-10-15 21:21:37350
[email protected]e1ee27482014-06-16 07:13:22351 if (!secure_scheme_supported() && url.SchemeIsSecure())
baranovichc5e38652014-11-14 03:08:15352 return SDCH_SECURE_SCHEME_NOT_SUPPORTED;
[email protected]e1ee27482014-06-16 07:13:22353
[email protected]c631b6aa2008-10-15 21:21:37354 if (blacklisted_domains_.empty())
baranovichc5e38652014-11-14 03:08:15355 return SDCH_OK;
[email protected]c631b6aa2008-10-15 21:21:37356
[email protected]6121e11a2014-08-13 19:10:39357 DomainBlacklistInfo::iterator it =
358 blacklisted_domains_.find(base::StringToLowerASCII(url.host()));
359 if (blacklisted_domains_.end() == it || it->second.count == 0)
baranovichc5e38652014-11-14 03:08:15360 return SDCH_OK;
[email protected]6a1f7dd42009-01-20 18:15:02361
tzik2f98de302014-11-04 06:38:19362 UMA_HISTOGRAM_ENUMERATION("Sdch3.BlacklistReason", it->second.reason,
baranovichc5e38652014-11-14 03:08:15363 SDCH_MAX_PROBLEM_CODE);
[email protected]6121e11a2014-08-13 19:10:39364
365 int count = it->second.count - 1;
366 if (count > 0) {
367 it->second.count = count;
368 } else {
369 it->second.count = 0;
baranovichc5e38652014-11-14 03:08:15370 it->second.reason = SDCH_OK;
[email protected]6121e11a2014-08-13 19:10:39371 }
372
baranovichc5e38652014-11-14 03:08:15373 return SDCH_DOMAIN_BLACKLIST_INCLUDES_TARGET;
[email protected]60889422008-09-23 01:18:16374}
375
baranovichc5e38652014-11-14 03:08:15376SdchProblemCode SdchManager::OnGetDictionary(const GURL& request_url,
377 const GURL& dictionary_url) {
378 DCHECK(thread_checker_.CalledOnValidThread());
379 SdchProblemCode rv = CanFetchDictionary(request_url, dictionary_url);
380 if (rv != SDCH_OK)
381 return rv;
rdsmith47c133b2014-11-06 19:02:30382
383 FOR_EACH_OBSERVER(SdchObserver,
384 observers_,
385 OnGetDictionary(this, request_url, dictionary_url));
baranovichc5e38652014-11-14 03:08:15386
387 return SDCH_OK;
[email protected]4b3c95dd2011-01-07 23:02:11388}
389
rdsmitha8a4e3c82015-01-08 20:18:17390void SdchManager::OnDictionaryUsed(const std::string& server_hash) {
391 FOR_EACH_OBSERVER(SdchObserver, observers_,
392 OnDictionaryUsed(this, server_hash));
393}
394
baranovichc5e38652014-11-14 03:08:15395SdchProblemCode SdchManager::CanFetchDictionary(
396 const GURL& referring_url,
397 const GURL& dictionary_url) const {
rdsmith47c133b2014-11-06 19:02:30398 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]f0a51fb52009-03-05 12:46:38399 /* The user agent may retrieve a dictionary from the dictionary URL if all of
[email protected]60889422008-09-23 01:18:16400 the following are true:
[email protected]ef993102014-01-10 19:29:37401 1 The dictionary URL host name matches the referrer URL host name and
402 scheme.
[email protected]60889422008-09-23 01:18:16403 2 The dictionary URL host name domain matches the parent domain of the
404 referrer URL host name
[email protected]f0a51fb52009-03-05 12:46:38405 3 The parent domain of the referrer URL host name is not a top level
[email protected]60889422008-09-23 01:18:16406 domain
[email protected]60889422008-09-23 01:18:16407 */
rdsmith81f607562014-11-21 18:35:16408 // Item (1) above implies item (2). Spec should be updated.
[email protected]60889422008-09-23 01:18:16409 // I take "host name match" to be "is identical to"
[email protected]ef993102014-01-10 19:29:37410 if (referring_url.host() != dictionary_url.host() ||
baranovichc5e38652014-11-14 03:08:15411 referring_url.scheme() != dictionary_url.scheme())
412 return SDCH_DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST;
413
414 if (!secure_scheme_supported() && referring_url.SchemeIsSecure())
415 return SDCH_DICTIONARY_SELECTED_FOR_SSL;
[email protected]075bc8cb2008-11-18 20:22:52416
417 // TODO(jar): Remove this failsafe conservative hack which is more restrictive
418 // than current SDCH spec when needed, and justified by security audit.
baranovichc5e38652014-11-14 03:08:15419 if (!referring_url.SchemeIsHTTPOrHTTPS())
420 return SDCH_DICTIONARY_SELECTED_FROM_NON_HTTP;
[email protected]075bc8cb2008-11-18 20:22:52421
baranovichc5e38652014-11-14 03:08:15422 return SDCH_OK;
[email protected]075bc8cb2008-11-18 20:22:52423}
424
rdsmith81f607562014-11-21 18:35:16425scoped_ptr<SdchManager::DictionarySet>
426SdchManager::GetDictionarySet(const GURL& target_url) {
427 if (IsInSupportedDomain(target_url) != SDCH_OK)
428 return NULL;
baranovichc5e38652014-11-14 03:08:15429
[email protected]075bc8cb2008-11-18 20:22:52430 int count = 0;
rdsmith81f607562014-11-21 18:35:16431 scoped_ptr<SdchManager::DictionarySet> result(new DictionarySet);
432 for (const auto& entry: dictionaries_) {
433 if (entry.second->data.CanUse(target_url) != SDCH_OK)
[email protected]6a586762014-06-15 16:02:22434 continue;
rdsmith81f607562014-11-21 18:35:16435 if (entry.second->data.Expired())
[email protected]60889422008-09-23 01:18:16436 continue;
[email protected]075bc8cb2008-11-18 20:22:52437 ++count;
rdsmith81f607562014-11-21 18:35:16438 result->AddDictionary(entry.first, entry.second);
[email protected]60889422008-09-23 01:18:16439 }
rdsmith81f607562014-11-21 18:35:16440
441 if (count == 0)
442 return NULL;
443
444 UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count);
445
446 return result.Pass();
447}
448
449scoped_ptr<SdchManager::DictionarySet>
450SdchManager::GetDictionarySetByHash(
451 const GURL& target_url,
452 const std::string& server_hash,
453 SdchProblemCode* problem_code) {
454 scoped_ptr<SdchManager::DictionarySet> result;
455
456 *problem_code = SDCH_DICTIONARY_HASH_NOT_FOUND;
457 const auto& it = dictionaries_.find(server_hash);
458 if (it == dictionaries_.end())
scottmgd9afd5482015-01-27 23:35:35459 return result.Pass();
rdsmith81f607562014-11-21 18:35:16460
461 *problem_code = it->second->data.CanUse(target_url);
462 if (*problem_code != SDCH_OK)
scottmgd9afd5482015-01-27 23:35:35463 return result.Pass();
rdsmith81f607562014-11-21 18:35:16464
465 result.reset(new DictionarySet);
466 result->AddDictionary(it->first, it->second);
scottmgd9afd5482015-01-27 23:35:35467 return result.Pass();
[email protected]60889422008-09-23 01:18:16468}
469
[email protected]60889422008-09-23 01:18:16470// static
471void SdchManager::GenerateHash(const std::string& dictionary_text,
472 std::string* client_hash, std::string* server_hash) {
473 char binary_hash[32];
[email protected]4b559b4d2011-04-14 17:37:14474 crypto::SHA256HashString(dictionary_text, binary_hash, sizeof(binary_hash));
[email protected]60889422008-09-23 01:18:16475
476 std::string first_48_bits(&binary_hash[0], 6);
477 std::string second_48_bits(&binary_hash[6], 6);
478 UrlSafeBase64Encode(first_48_bits, client_hash);
479 UrlSafeBase64Encode(second_48_bits, server_hash);
480
[email protected]5b90b5d2009-04-30 23:06:01481 DCHECK_EQ(server_hash->length(), 8u);
482 DCHECK_EQ(client_hash->length(), 8u);
[email protected]60889422008-09-23 01:18:16483}
484
[email protected]5b90b5d2009-04-30 23:06:01485// Methods for supporting latency experiments.
486
487bool SdchManager::AllowLatencyExperiment(const GURL& url) const {
rdsmith47c133b2014-11-06 19:02:30488 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]5b90b5d2009-04-30 23:06:01489 return allow_latency_experiment_.end() !=
490 allow_latency_experiment_.find(url.host());
491}
492
493void SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) {
rdsmith47c133b2014-11-06 19:02:30494 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]5b90b5d2009-04-30 23:06:01495 if (enable) {
496 allow_latency_experiment_.insert(url.host());
497 return;
498 }
499 ExperimentSet::iterator it = allow_latency_experiment_.find(url.host());
500 if (allow_latency_experiment_.end() == it)
501 return; // It was already erased, or never allowed.
baranovichc5e38652014-11-14 03:08:15502 SdchErrorRecovery(SDCH_LATENCY_TEST_DISALLOWED);
[email protected]5b90b5d2009-04-30 23:06:01503 allow_latency_experiment_.erase(it);
504}
[email protected]4b3c95dd2011-01-07 23:02:11505
rdsmith47c133b2014-11-06 19:02:30506void SdchManager::AddObserver(SdchObserver* observer) {
507 observers_.AddObserver(observer);
508}
509
510void SdchManager::RemoveObserver(SdchObserver* observer) {
511 observers_.RemoveObserver(observer);
512}
513
baranovichc5e38652014-11-14 03:08:15514SdchProblemCode SdchManager::AddSdchDictionary(
515 const std::string& dictionary_text,
rdsmitha8a4e3c82015-01-08 20:18:17516 const GURL& dictionary_url,
517 std::string* server_hash_p) {
rdsmith47c133b2014-11-06 19:02:30518 DCHECK(thread_checker_.CalledOnValidThread());
rdsmith1df5b7132014-09-09 20:36:47519 std::string client_hash;
520 std::string server_hash;
521 GenerateHash(dictionary_text, &client_hash, &server_hash);
baranovichc5e38652014-11-14 03:08:15522 if (dictionaries_.find(server_hash) != dictionaries_.end())
523 return SDCH_DICTIONARY_ALREADY_LOADED; // Already loaded.
rdsmith1df5b7132014-09-09 20:36:47524
525 std::string domain, path;
526 std::set<int> ports;
527 base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30));
528
baranovichc5e38652014-11-14 03:08:15529 if (dictionary_text.empty())
530 return SDCH_DICTIONARY_HAS_NO_TEXT; // Missing header.
rdsmith1df5b7132014-09-09 20:36:47531
532 size_t header_end = dictionary_text.find("\n\n");
baranovichc5e38652014-11-14 03:08:15533 if (std::string::npos == header_end)
534 return SDCH_DICTIONARY_HAS_NO_HEADER; // Missing header.
535
rdsmith1df5b7132014-09-09 20:36:47536 size_t line_start = 0; // Start of line being parsed.
537 while (1) {
538 size_t line_end = dictionary_text.find('\n', line_start);
539 DCHECK(std::string::npos != line_end);
540 DCHECK_LE(line_end, header_end);
541
542 size_t colon_index = dictionary_text.find(':', line_start);
baranovichc5e38652014-11-14 03:08:15543 if (std::string::npos == colon_index)
544 return SDCH_DICTIONARY_HEADER_LINE_MISSING_COLON; // Illegal line missing
545 // a colon.
rdsmith1df5b7132014-09-09 20:36:47546
547 if (colon_index > line_end)
548 break;
549
550 size_t value_start = dictionary_text.find_first_not_of(" \t",
551 colon_index + 1);
552 if (std::string::npos != value_start) {
553 if (value_start >= line_end)
554 break;
555 std::string name(dictionary_text, line_start, colon_index - line_start);
556 std::string value(dictionary_text, value_start, line_end - value_start);
557 name = base::StringToLowerASCII(name);
558 if (name == "domain") {
559 domain = value;
560 } else if (name == "path") {
561 path = value;
562 } else if (name == "format-version") {
563 if (value != "1.0")
baranovichc5e38652014-11-14 03:08:15564 return SDCH_DICTIONARY_UNSUPPORTED_VERSION;
rdsmith1df5b7132014-09-09 20:36:47565 } else if (name == "max-age") {
566 int64 seconds;
567 base::StringToInt64(value, &seconds);
568 expiration = base::Time::Now() + base::TimeDelta::FromSeconds(seconds);
569 } else if (name == "port") {
570 int port;
571 base::StringToInt(value, &port);
572 if (port >= 0)
573 ports.insert(port);
574 }
575 }
576
577 if (line_end >= header_end)
578 break;
579 line_start = line_end + 1;
580 }
581
rdsmith14fd6592014-09-23 21:13:10582 // Narrow fix for http://crbug.com/389451.
583 GURL dictionary_url_normalized(dictionary_url);
584 StripTrailingDot(&dictionary_url_normalized);
585
baranovichc5e38652014-11-14 03:08:15586 SdchProblemCode rv = IsInSupportedDomain(dictionary_url_normalized);
587 if (rv != SDCH_OK)
588 return rv;
rdsmith1df5b7132014-09-09 20:36:47589
baranovichc5e38652014-11-14 03:08:15590 rv = Dictionary::CanSet(domain, path, ports, dictionary_url_normalized);
591 if (rv != SDCH_OK)
592 return rv;
rdsmith1df5b7132014-09-09 20:36:47593
rdsmith1df5b7132014-09-09 20:36:47594 UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size());
595 DVLOG(1) << "Loaded dictionary with client hash " << client_hash
596 << " and server hash " << server_hash;
rdsmith81f607562014-11-21 18:35:16597 Dictionary dictionary(dictionary_text, header_end + 2, client_hash,
rdsmitha8a4e3c82015-01-08 20:18:17598 server_hash, dictionary_url_normalized, domain, path,
599 expiration, ports);
rdsmith81f607562014-11-21 18:35:16600 dictionaries_[server_hash] =
601 new base::RefCountedData<Dictionary>(dictionary);
rdsmitha8a4e3c82015-01-08 20:18:17602 if (server_hash_p)
603 *server_hash_p = server_hash;
rdsmith81f607562014-11-21 18:35:16604
baranovichc5e38652014-11-14 03:08:15605 return SDCH_OK;
rdsmith1df5b7132014-09-09 20:36:47606}
607
rdsmitha8a4e3c82015-01-08 20:18:17608SdchProblemCode SdchManager::RemoveSdchDictionary(
609 const std::string& server_hash) {
610 if (dictionaries_.find(server_hash) == dictionaries_.end())
611 return SDCH_DICTIONARY_HASH_NOT_FOUND;
612
613 dictionaries_.erase(server_hash);
614 return SDCH_OK;
615}
616
[email protected]4b3c95dd2011-01-07 23:02:11617// static
rdsmith81f607562014-11-21 18:35:16618scoped_ptr<SdchManager::DictionarySet>
619SdchManager::CreateEmptyDictionarySetForTesting() {
620 return scoped_ptr<DictionarySet>(new DictionarySet).Pass();
621}
622
623// static
[email protected]4b3c95dd2011-01-07 23:02:11624void SdchManager::UrlSafeBase64Encode(const std::string& input,
625 std::string* output) {
626 // Since this is only done during a dictionary load, and hashes are only 8
627 // characters, we just do the simple fixup, rather than rewriting the encoder.
628 base::Base64Encode(input, output);
[email protected]33acd5b2014-08-19 19:56:22629 std::replace(output->begin(), output->end(), '+', '-');
630 std::replace(output->begin(), output->end(), '/', '_');
[email protected]4b3c95dd2011-01-07 23:02:11631}
[email protected]ba5734e2011-02-01 03:42:11632
baranovichc5e38652014-11-14 03:08:15633base::Value* SdchManager::SdchInfoToValue() const {
634 base::DictionaryValue* value = new base::DictionaryValue();
635
636 value->SetBoolean("sdch_enabled", sdch_enabled());
637 value->SetBoolean("secure_scheme_support", secure_scheme_supported());
638
639 base::ListValue* entry_list = new base::ListValue();
rdsmith81f607562014-11-21 18:35:16640 for (const auto& entry: dictionaries_) {
baranovichc5e38652014-11-14 03:08:15641 base::DictionaryValue* entry_dict = new base::DictionaryValue();
rdsmith81f607562014-11-21 18:35:16642 entry_dict->SetString("url", entry.second->data.url().spec());
643 entry_dict->SetString("client_hash", entry.second->data.client_hash());
644 entry_dict->SetString("domain", entry.second->data.domain());
645 entry_dict->SetString("path", entry.second->data.path());
baranovichc5e38652014-11-14 03:08:15646 base::ListValue* port_list = new base::ListValue();
rdsmith81f607562014-11-21 18:35:16647 for (std::set<int>::const_iterator port_it =
648 entry.second->data.ports().begin();
649 port_it != entry.second->data.ports().end(); ++port_it) {
baranovichc5e38652014-11-14 03:08:15650 port_list->AppendInteger(*port_it);
651 }
652 entry_dict->Set("ports", port_list);
rdsmith81f607562014-11-21 18:35:16653 entry_dict->SetString("server_hash", entry.first);
baranovichc5e38652014-11-14 03:08:15654 entry_list->Append(entry_dict);
655 }
656 value->Set("dictionaries", entry_list);
657
658 entry_list = new base::ListValue();
659 for (DomainBlacklistInfo::const_iterator it = blacklisted_domains_.begin();
660 it != blacklisted_domains_.end(); ++it) {
661 if (it->second.count == 0)
662 continue;
663 base::DictionaryValue* entry_dict = new base::DictionaryValue();
664 entry_dict->SetString("domain", it->first);
665 if (it->second.count != INT_MAX)
666 entry_dict->SetInteger("tries", it->second.count);
667 entry_dict->SetInteger("reason", it->second.reason);
668 entry_list->Append(entry_dict);
669 }
670 value->Set("blacklisted", entry_list);
671
672 return value;
673}
674
[email protected]ba5734e2011-02-01 03:42:11675} // namespace net