sdch_manager.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be
3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file.
4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
5c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/base64.h"
6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/field_trial.h"
7c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/histogram.h"
8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/logging.h"
9c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/sha2.h"
10c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/string_util.h"
11c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/base/registry_controlled_domain.h"
12c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/base/sdch_manager.h"
13c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/url_request/url_request_http_job.h"
14c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
15c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottusing base::Time;
16c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottusing base::TimeDelta;
17c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
18c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//------------------------------------------------------------------------------
19c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
20c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottconst size_t SdchManager::kMaxDictionarySize = 1000000;
21c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
22c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottconst size_t SdchManager::kMaxDictionaryCount = 20;
24c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
25c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
26c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottSdchManager* SdchManager::global_;
27c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
28c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
29c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottSdchManager* SdchManager::Global() {
30c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return global_;
31c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
32c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
33c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
34c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::SdchErrorRecovery(ProblemCodes problem) {
35c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE);
36c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
37c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
38c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
39c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::ClearBlacklistings() {
40c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  Global()->blacklisted_domains_.clear();
41c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  Global()->exponential_blacklist_count.clear();
42c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
43c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
44c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
45c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::ClearDomainBlacklisting(const std::string& domain) {
46c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  Global()->blacklisted_domains_.erase(StringToLowerASCII(domain));
47c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
48c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
49c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
50c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottint SdchManager::BlackListDomainCount(const std::string& domain) {
51c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (Global()->blacklisted_domains_.end() ==
52c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      Global()->blacklisted_domains_.find(domain))
53c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return 0;
54c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return Global()->blacklisted_domains_[StringToLowerASCII(domain)];
55c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
56c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
57c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
58c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottint SdchManager::BlacklistDomainExponential(const std::string& domain) {
59c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (Global()->exponential_blacklist_count.end() ==
60c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      Global()->exponential_blacklist_count.find(domain))
61c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return 0;
62c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return Global()->exponential_blacklist_count[StringToLowerASCII(domain)];
63c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
64c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
65c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//------------------------------------------------------------------------------
66c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottSdchManager::SdchManager() : sdch_enabled_(false) {
67c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK(!global_);
68c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_ = this;
69c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
70c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
71c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottSdchManager::~SdchManager() {
72c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK(global_ == this);
73c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (!dictionaries_.empty()) {
74c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DictionaryMap::iterator it = dictionaries_.begin();
75c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    it->second->Release();
76c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    dictionaries_.erase(it->first);
77c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
78c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_ = NULL;
79c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
80c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
81c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
82c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::Shutdown() {
83c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!global_ )
84c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
85c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_->fetcher_.reset(NULL);
86c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
87c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
88c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
89c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::BlacklistDomain(const GURL& url) {
90c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!global_ )
91c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
92c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_->SetAllowLatencyExperiment(url, false);
93c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
94c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string domain(StringToLowerASCII(url.host()));
95c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int count = global_->blacklisted_domains_[domain];
96c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (count > 0)
97c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;  // Domain is already blacklisted.
98c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
99c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  count = 1 + 2 * global_->exponential_blacklist_count[domain];
100c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (count > 0)
101c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    global_->exponential_blacklist_count[domain] = count;
102c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  else
103c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    count = INT_MAX;
104c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
105c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_->blacklisted_domains_[domain] = count;
106c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
107c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
108c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
109c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::BlacklistDomainForever(const GURL& url) {
110c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!global_ )
111c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
112c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_->SetAllowLatencyExperiment(url, false);
113c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
114c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string domain(StringToLowerASCII(url.host()));
115c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_->exponential_blacklist_count[domain] = INT_MAX;
116c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_->blacklisted_domains_[domain] = INT_MAX;
117c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
118c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
119c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::EnableSdchSupport(const std::string& domain) {
120c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // We presume that there is a SDCH manager instance.
121c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_->supported_domain_ = domain;
122c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_->sdch_enabled_ = true;
123c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
124c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool SdchManager::IsInSupportedDomain(const GURL& url) {
126c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!sdch_enabled_ )
127c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
128c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!supported_domain_.empty() &&
129c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      !url.DomainIs(supported_domain_.data(), supported_domain_.size()))
130c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott     return false;  // It is not the singular supported domain.
131c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
132c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (blacklisted_domains_.empty())
133c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return true;
134c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
135c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string domain(StringToLowerASCII(url.host()));
136c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DomainCounter::iterator it = blacklisted_domains_.find(domain);
137c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (blacklisted_domains_.end() == it)
138c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return true;
139c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
140c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int count = it->second - 1;
141c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (count > 0)
142c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    blacklisted_domains_[domain] = count;
143c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  else
144c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    blacklisted_domains_.erase(domain);
145c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET);
146c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return false;
147c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
148c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
149c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool SdchManager::CanFetchDictionary(const GURL& referring_url,
150c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                     const GURL& dictionary_url) const {
151c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  /* The user agent may retrieve a dictionary from the dictionary URL if all of
152c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott     the following are true:
153c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott       1 The dictionary URL host name matches the referrer URL host name
154c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott       2 The dictionary URL host name domain matches the parent domain of the
155c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott           referrer URL host name
156c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott       3 The parent domain of the referrer URL host name is not a top level
157c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott           domain
158c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott       4 The dictionary URL is not an HTTPS URL.
159c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott   */
160c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Item (1) above implies item (2).  Spec should be updated.
161c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // I take "host name match" to be "is identical to"
162c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (referring_url.host() != dictionary_url.host()) {
163c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST);
164c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
165c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
166c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (referring_url.SchemeIs("https")) {
167c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_SELECTED_FOR_SSL);
168c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
169c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
170c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
171c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // TODO(jar): Remove this failsafe conservative hack which is more restrictive
172c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // than current SDCH spec when needed, and justified by security audit.
173c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!referring_url.SchemeIs("http")) {
174c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_SELECTED_FROM_NON_HTTP);
175c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
176c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
177c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
178c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
179c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
180c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
181c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::FetchDictionary(const GURL& request_url,
182c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                  const GURL& dictionary_url) {
183c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (SdchManager::Global()->CanFetchDictionary(request_url, dictionary_url) &&
184c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      fetcher_.get())
185c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    fetcher_->Schedule(dictionary_url);
186c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
187c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
188c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool SdchManager::AddSdchDictionary(const std::string& dictionary_text,
189c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    const GURL& dictionary_url) {
190c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string client_hash;
191c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string server_hash;
192c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  GenerateHash(dictionary_text, &client_hash, &server_hash);
193c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (dictionaries_.find(server_hash) != dictionaries_.end()) {
194c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_ALREADY_LOADED);
195c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;  // Already loaded.
196c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
197c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
198c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string domain, path;
199c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::set<int> ports;
200c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  Time expiration(Time::Now() + TimeDelta::FromDays(30));
201c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
202c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (dictionary_text.empty()) {
203c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_HAS_NO_TEXT);
204c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;  // Missing header.
205c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
206c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
207c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  size_t header_end = dictionary_text.find("\n\n");
208c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (std::string::npos == header_end) {
209c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_HAS_NO_HEADER);
210c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;  // Missing header.
211c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
212c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  size_t line_start = 0;  // Start of line being parsed.
213c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (1) {
214c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    size_t line_end = dictionary_text.find('\n', line_start);
215c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DCHECK(std::string::npos != line_end);
216c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DCHECK(line_end <= header_end);
217c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
218c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    size_t colon_index = dictionary_text.find(':', line_start);
219c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (std::string::npos == colon_index) {
220c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      SdchErrorRecovery(DICTIONARY_HEADER_LINE_MISSING_COLON);
221c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return false;  // Illegal line missing a colon.
222c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
223c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
224c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (colon_index > line_end)
225c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      break;
226c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
227c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    size_t value_start = dictionary_text.find_first_not_of(" \t",
228c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                                           colon_index + 1);
229c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (std::string::npos != value_start) {
230c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if (value_start >= line_end)
231c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        break;
232c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      std::string name(dictionary_text, line_start, colon_index - line_start);
233c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      std::string value(dictionary_text, value_start, line_end - value_start);
234c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      name = StringToLowerASCII(name);
235c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if (name == "domain") {
236c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        domain = value;
237c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      } else if (name == "path") {
238c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        path = value;
239c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      } else if (name == "format-version") {
240c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        if (value != "1.0")
241c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott          return false;
242c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      } else if (name == "max-age") {
243c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        expiration = Time::Now() + TimeDelta::FromSeconds(StringToInt64(value));
244c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      } else if (name == "port") {
245c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        int port = StringToInt(value);
246c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        if (port >= 0)
247c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott          ports.insert(port);
248c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      }
249c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
250c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
251c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (line_end >= header_end)
252c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      break;
253c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    line_start = line_end + 1;
254c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
255c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
256c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!Dictionary::CanSet(domain, path, ports, dictionary_url))
257c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
258c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
259c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of
260c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // useless dictionaries.  We should probably have a cache eviction plan,
261c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // instead of just blocking additions.  For now, with the spec in flux, it
262c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // is probably not worth doing eviction handling.
263c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (kMaxDictionarySize < dictionary_text.size()) {
264c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_IS_TOO_LARGE);
265c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
266c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
267c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (kMaxDictionaryCount <= dictionaries_.size()) {
268c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_COUNT_EXCEEDED);
269c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
270c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
271c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
272c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size());
273c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DLOG(INFO) << "Loaded dictionary with client hash " << client_hash <<
274c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      " and server hash " << server_hash;
275c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  Dictionary* dictionary =
276c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      new Dictionary(dictionary_text, header_end + 2, client_hash,
277c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     dictionary_url, domain, path, expiration, ports);
278c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  dictionary->AddRef();
279c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  dictionaries_[server_hash] = dictionary;
280c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
281c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
282c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
283c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::GetVcdiffDictionary(const std::string& server_hash,
284c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    const GURL& referring_url, Dictionary** dictionary) {
285c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  *dictionary = NULL;
286c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DictionaryMap::iterator it = dictionaries_.find(server_hash);
287c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (it == dictionaries_.end()) {
288c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
289c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
290c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  Dictionary* matching_dictionary = it->second;
291c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!matching_dictionary->CanUse(referring_url))
292c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
293c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  *dictionary = matching_dictionary;
294c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
295c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
296c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// TODO(jar): If we have evictions from the dictionaries_, then we need to
297c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// change this interface to return a list of reference counted Dictionary
298c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// instances that can be used if/when a server specifies one.
299c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::GetAvailDictionaryList(const GURL& target_url,
300c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                         std::string* list) {
301c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int count = 0;
302c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (DictionaryMap::iterator it = dictionaries_.begin();
303c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott       it != dictionaries_.end(); ++it) {
304c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (!it->second->CanAdvertise(target_url))
305c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      continue;
306c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    ++count;
307c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (!list->empty())
308c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      list->append(",");
309c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    list->append(it->second->client_hash());
310c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
311c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Watch to see if we have corrupt or numerous dictionaries.
312c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (count > 0)
313c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count);
314c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
315c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
316c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottSdchManager::Dictionary::Dictionary(const std::string& dictionary_text,
317c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    size_t offset, const std::string& client_hash, const GURL& gurl,
318c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    const std::string& domain, const std::string& path, const Time& expiration,
319c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    const std::set<int> ports)
320c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      : text_(dictionary_text, offset),
321c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        client_hash_(client_hash),
322c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        url_(gurl),
323c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        domain_(domain),
324c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        path_(path),
325c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        expiration_(expiration),
326c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        ports_(ports) {
327c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
328c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
329c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
330c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::GenerateHash(const std::string& dictionary_text,
331c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    std::string* client_hash, std::string* server_hash) {
332c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  char binary_hash[32];
333c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  base::SHA256HashString(dictionary_text, binary_hash, sizeof(binary_hash));
334c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
335c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string first_48_bits(&binary_hash[0], 6);
336c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string second_48_bits(&binary_hash[6], 6);
337c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  UrlSafeBase64Encode(first_48_bits, client_hash);
338c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  UrlSafeBase64Encode(second_48_bits, server_hash);
339c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
340c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK_EQ(server_hash->length(), 8u);
341c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK_EQ(client_hash->length(), 8u);
342c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
343c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
344c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
345c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::UrlSafeBase64Encode(const std::string& input,
346c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                      std::string* output) {
347c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Since this is only done during a dictionary load, and hashes are only 8
348c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // characters, we just do the simple fixup, rather than rewriting the encoder.
349c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  base::Base64Encode(input, output);
350c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (size_t i = 0; i < output->size(); ++i) {
351c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    switch (output->data()[i]) {
352c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      case '+':
353c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        (*output)[i] = '-';
354c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        continue;
355c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      case '/':
356c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        (*output)[i] = '_';
357c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        continue;
358c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      default:
359c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        continue;
360c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
361c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
362c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
363c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
364c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//------------------------------------------------------------------------------
365c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Security functions restricting loads and use of dictionaries.
366c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
367c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
368c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool SdchManager::Dictionary::CanSet(const std::string& domain,
369c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                     const std::string& path,
370c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                     const std::set<int> ports,
371c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                     const GURL& dictionary_url) {
372c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!SdchManager::Global()->IsInSupportedDomain(dictionary_url))
373c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
374c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  /*
375c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  A dictionary is invalid and must not be stored if any of the following are
376c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  true:
377c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    1. The dictionary has no Domain attribute.
378c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    2. The effective host name that derives from the referer URL host name does
379c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      not domain-match the Domain attribute.
380c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    3. The Domain attribute is a top level domain.
381c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    4. The referer URL host is a host domain name (not IP address) and has the
382c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      form HD, where D is the value of the Domain attribute, and H is a string
383c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      that contains one or more dots.
384c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    5. If the dictionary has a Port attribute and the referer URL's port was not
385c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      in the list.
386c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  */
387c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
388c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // TODO(jar): Redirects in dictionary fetches might plausibly be problematic,
389c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // and hence the conservative approach is to not allow any redirects (if there
390c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // were any... then don't allow the dictionary to be set).
391c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
392c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (domain.empty()) {
393c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_MISSING_DOMAIN_SPECIFIER);
394c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;  // Domain is required.
395c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
396c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (net::RegistryControlledDomainService::GetDomainAndRegistry(domain).size()
397c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      == 0) {
398c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN);
399c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;  // domain was a TLD.
400c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
401c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!Dictionary::DomainMatch(dictionary_url, domain)) {
402c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL);
403c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
404c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
405c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
406c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string referrer_url_host = dictionary_url.host();
407c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  size_t postfix_domain_index = referrer_url_host.rfind(domain);
408c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // See if it is indeed a postfix, or just an internal string.
409c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (referrer_url_host.size() == postfix_domain_index + domain.size()) {
410c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // It is a postfix... so check to see if there's a dot in the prefix.
411c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    size_t end_of_host_index = referrer_url_host.find_first_of('.');
412c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (referrer_url_host.npos != end_of_host_index  &&
413c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        end_of_host_index < postfix_domain_index) {
414c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      SdchErrorRecovery(DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX);
415c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return false;
416c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
417c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
418c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
419c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!ports.empty()
420c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      && 0 == ports.count(dictionary_url.EffectiveIntPort())) {
421c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL);
422c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
423c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
424c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
425c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
426c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
427c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
428c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool SdchManager::Dictionary::CanUse(const GURL& referring_url) {
429c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!SdchManager::Global()->IsInSupportedDomain(referring_url))
430c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
431c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  /*
432c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    1. The request URL's host name domain-matches the Domain attribute of the
433c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      dictionary.
434c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    2. If the dictionary has a Port attribute, the request port is one of the
435c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      ports listed in the Port attribute.
436c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    3. The request URL path-matches the path attribute of the dictionary.
437c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    4. The request is not an HTTPS request.
438c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott*/
439c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!DomainMatch(referring_url, domain_)) {
440c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_DOMAIN);
441c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
442c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
443c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!ports_.empty()
444c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      && 0 == ports_.count(referring_url.EffectiveIntPort())) {
445c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PORT_LIST);
446c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
447c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
448c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (path_.size() && !PathMatch(referring_url.path(), path_)) {
449c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PATH);
450c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
451c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
452c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (referring_url.SchemeIsSecure()) {
453c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME);
454c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
455c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
456c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
457c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // TODO(jar): Remove overly restrictive failsafe test (added per security
458c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // review) when we have a need to be more general.
459c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!referring_url.SchemeIs("http")) {
460c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(ATTEMPT_TO_DECODE_NON_HTTP_DATA);
461c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
462c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
463c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
464c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
465c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
466c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
467c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) {
468c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!SdchManager::Global()->IsInSupportedDomain(target_url))
469c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
470c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  /* The specific rules of when a dictionary should be advertised in an
471c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott     Avail-Dictionary header are modeled after the rules for cookie scoping. The
472c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott     terms "domain-match" and "pathmatch" are defined in RFC 2965 [6]. A
473c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott     dictionary may be advertised in the Avail-Dictionaries header exactly when
474c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott     all of the following are true:
475c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      1. The server's effective host name domain-matches the Domain attribute of
476c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott         the dictionary.
477c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      2. If the dictionary has a Port attribute, the request port is one of the
478c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott         ports listed in the Port attribute.
479c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      3. The request URI path-matches the path header of the dictionary.
480c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      4. The request is not an HTTPS request.
481c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    */
482c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!DomainMatch(target_url, domain_))
483c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
484c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort()))
485c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
486c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (path_.size() && !PathMatch(target_url.path(), path_))
487c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
488c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (target_url.SchemeIsSecure())
489c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
490c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (Time::Now() > expiration_)
491c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
492c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
493c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
494c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
495c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool SdchManager::Dictionary::PathMatch(const std::string& path,
496c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                        const std::string& restriction) {
497c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  /*  Must be either:
498c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  1. P2 is equal to P1
499c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  2. P2 is a prefix of P1 and either the final character in P2 is "/" or the
500c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      character following P2 in P1 is "/".
501c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      */
502c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (path == restriction)
503c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return true;
504c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  size_t prefix_length = restriction.size();
505c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (prefix_length > path.size())
506c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;  // Can't be a prefix.
507c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (0 != path.compare(0, prefix_length, restriction))
508c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
509c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return restriction[prefix_length - 1] == '/' || path[prefix_length] == '/';
510c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
511c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
512c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
513c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool SdchManager::Dictionary::DomainMatch(const GURL& gurl,
514c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                          const std::string& restriction) {
515c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // TODO(jar): This is not precisely a domain match definition.
516c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return gurl.DomainIs(restriction.data(), restriction.size());
517c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
518c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
519c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//------------------------------------------------------------------------------
520c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Methods for supporting latency experiments.
521c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
522c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool SdchManager::AllowLatencyExperiment(const GURL& url) const {
523c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return allow_latency_experiment_.end() !=
524c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      allow_latency_experiment_.find(url.host());
525c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
526c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
527c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) {
528c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (enable) {
529c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    allow_latency_experiment_.insert(url.host());
530c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
531c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
532c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  ExperimentSet::iterator it = allow_latency_experiment_.find(url.host());
533c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (allow_latency_experiment_.end() == it)
534c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;  // It was already erased, or never allowed.
535c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  SdchErrorRecovery(LATENCY_TEST_DISALLOWED);
536c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  allow_latency_experiment_.erase(it);
537c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
538