172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 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
53345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "net/base/sdch_manager.h"
63345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
7c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/base64.h"
8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/logging.h"
9731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/metrics/histogram.h"
103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/string_number_conversions.h"
11c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/string_util.h"
12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "crypto/sha2.h"
13c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/base/registry_controlled_domain.h"
14c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/url_request/url_request_http_job.h"
15c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsennamespace net {
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
283f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen//------------------------------------------------------------------------------
293f50c38dc070f4bb515c1b64450dae14f316474eKristian MonsenSdchManager::Dictionary::Dictionary(const std::string& dictionary_text,
3072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                    size_t offset,
3172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                    const std::string& client_hash,
3272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                    const GURL& gurl,
3372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                    const std::string& domain,
3472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                    const std::string& path,
3572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                    const base::Time& expiration,
3672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                    const std::set<int>& ports)
3772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    : text_(dictionary_text, offset),
3872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      client_hash_(client_hash),
3972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      url_(gurl),
4072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      domain_(domain),
4172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      path_(path),
4272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      expiration_(expiration),
4372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      ports_(ports) {
44c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
45c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
463f50c38dc070f4bb515c1b64450dae14f316474eKristian MonsenSdchManager::Dictionary::~Dictionary() {
47c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
48c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
493f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenbool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) {
503f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (!SdchManager::Global()->IsInSupportedDomain(target_url))
513f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
523f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  /* The specific rules of when a dictionary should be advertised in an
533f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen     Avail-Dictionary header are modeled after the rules for cookie scoping. The
543f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen     terms "domain-match" and "pathmatch" are defined in RFC 2965 [6]. A
553f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen     dictionary may be advertised in the Avail-Dictionaries header exactly when
563f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen     all of the following are true:
573f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      1. The server's effective host name domain-matches the Domain attribute of
583f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen         the dictionary.
593f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      2. If the dictionary has a Port attribute, the request port is one of the
603f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen         ports listed in the Port attribute.
613f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      3. The request URI path-matches the path header of the dictionary.
623f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      4. The request is not an HTTPS request.
633f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    */
643f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (!DomainMatch(target_url, domain_))
653f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
663f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort()))
673f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
683f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (path_.size() && !PathMatch(target_url.path(), path_))
693f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
703f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (target_url.SchemeIsSecure())
713f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
7272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (base::Time::Now() > expiration_)
733f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
743f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  return true;
75c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
76c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
773f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen//------------------------------------------------------------------------------
783f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// Security functions restricting loads and use of dictionaries.
793f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
80c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
813f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenbool SdchManager::Dictionary::CanSet(const std::string& domain,
823f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen                                     const std::string& path,
8372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                     const std::set<int>& ports,
843f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen                                     const GURL& dictionary_url) {
853f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (!SdchManager::Global()->IsInSupportedDomain(dictionary_url))
863f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
873f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  /*
883f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  A dictionary is invalid and must not be stored if any of the following are
893f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  true:
903f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    1. The dictionary has no Domain attribute.
913f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    2. The effective host name that derives from the referer URL host name does
923f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      not domain-match the Domain attribute.
933f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    3. The Domain attribute is a top level domain.
943f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    4. The referer URL host is a host domain name (not IP address) and has the
953f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      form HD, where D is the value of the Domain attribute, and H is a string
963f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      that contains one or more dots.
973f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    5. If the dictionary has a Port attribute and the referer URL's port was not
983f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      in the list.
993f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  */
1003f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
1013f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // TODO(jar): Redirects in dictionary fetches might plausibly be problematic,
1023f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // and hence the conservative approach is to not allow any redirects (if there
1033f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // were any... then don't allow the dictionary to be set).
1043f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
1053f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (domain.empty()) {
1063f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    SdchErrorRecovery(DICTIONARY_MISSING_DOMAIN_SPECIFIER);
1073f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;  // Domain is required.
1083f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  }
109ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (RegistryControlledDomainService::GetDomainAndRegistry(domain).size()
1103f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      == 0) {
1113f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN);
1123f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;  // domain was a TLD.
1133f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  }
1143f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (!Dictionary::DomainMatch(dictionary_url, domain)) {
1153f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL);
1163f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
1173f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  }
1183f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
1193f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  std::string referrer_url_host = dictionary_url.host();
1203f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  size_t postfix_domain_index = referrer_url_host.rfind(domain);
1213f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // See if it is indeed a postfix, or just an internal string.
1223f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (referrer_url_host.size() == postfix_domain_index + domain.size()) {
1233f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    // It is a postfix... so check to see if there's a dot in the prefix.
1243f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    size_t end_of_host_index = referrer_url_host.find_first_of('.');
1253f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    if (referrer_url_host.npos != end_of_host_index  &&
1263f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen        end_of_host_index < postfix_domain_index) {
1273f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      SdchErrorRecovery(DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX);
1283f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      return false;
1293f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    }
1303f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  }
1313f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
1323f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (!ports.empty()
1333f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      && 0 == ports.count(dictionary_url.EffectiveIntPort())) {
1343f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL);
1353f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
1363f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  }
1373f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  return true;
138c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
139c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
140c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
1413f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenbool SdchManager::Dictionary::CanUse(const GURL& referring_url) {
1423f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (!SdchManager::Global()->IsInSupportedDomain(referring_url))
1433f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
1443f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  /*
1453f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    1. The request URL's host name domain-matches the Domain attribute of the
1463f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      dictionary.
1473f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    2. If the dictionary has a Port attribute, the request port is one of the
1483f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      ports listed in the Port attribute.
1493f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    3. The request URL path-matches the path attribute of the dictionary.
1503f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    4. The request is not an HTTPS request.
1513f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen*/
1523f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (!DomainMatch(referring_url, domain_)) {
1533f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_DOMAIN);
1543f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
1553f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  }
1563f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (!ports_.empty()
1573f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      && 0 == ports_.count(referring_url.EffectiveIntPort())) {
1583f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PORT_LIST);
1593f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
1603f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  }
1613f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (path_.size() && !PathMatch(referring_url.path(), path_)) {
1623f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PATH);
1633f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
1643f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  }
1653f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (referring_url.SchemeIsSecure()) {
1663f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME);
1673f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
1683f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  }
1693f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
1703f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // TODO(jar): Remove overly restrictive failsafe test (added per security
1713f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // review) when we have a need to be more general.
1723f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (!referring_url.SchemeIs("http")) {
1733f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    SdchErrorRecovery(ATTEMPT_TO_DECODE_NON_HTTP_DATA);
1743f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
1753f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  }
1763f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
1773f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  return true;
1783f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen}
1793f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
1803f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenbool SdchManager::Dictionary::PathMatch(const std::string& path,
1813f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen                                        const std::string& restriction) {
1823f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  /*  Must be either:
1833f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  1. P2 is equal to P1
1843f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  2. P2 is a prefix of P1 and either the final character in P2 is "/" or the
1853f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      character following P2 in P1 is "/".
1863f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      */
1873f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (path == restriction)
1883f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return true;
1893f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  size_t prefix_length = restriction.size();
1903f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (prefix_length > path.size())
1913f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;  // Can't be a prefix.
1923f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (0 != path.compare(0, prefix_length, restriction))
1933f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return false;
1943f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  return restriction[prefix_length - 1] == '/' || path[prefix_length] == '/';
195c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
196c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
197c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
1983f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenbool SdchManager::Dictionary::DomainMatch(const GURL& gurl,
1993f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen                                          const std::string& restriction) {
2003f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // TODO(jar): This is not precisely a domain match definition.
2013f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  return gurl.DomainIs(restriction.data(), restriction.size());
202c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
203c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
204c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//------------------------------------------------------------------------------
205c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottSdchManager::SdchManager() : sdch_enabled_(false) {
206c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK(!global_);
207c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_ = this;
208c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
209c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
210c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottSdchManager::~SdchManager() {
211c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK(global_ == this);
212c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (!dictionaries_.empty()) {
213c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DictionaryMap::iterator it = dictionaries_.begin();
214c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    it->second->Release();
215c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    dictionaries_.erase(it->first);
216c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
217c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_ = NULL;
218c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
219c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
220c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
221c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::Shutdown() {
222c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!global_ )
223c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
224c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_->fetcher_.reset(NULL);
225c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
226c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
227c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
2283f50c38dc070f4bb515c1b64450dae14f316474eKristian MonsenSdchManager* SdchManager::Global() {
2293f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  return global_;
2303f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen}
2313f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
2323f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// static
2333f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenvoid SdchManager::SdchErrorRecovery(ProblemCodes problem) {
2343f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE);
2353f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen}
2363f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
2373f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenvoid SdchManager::EnableSdchSupport(const std::string& domain) {
2383f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // We presume that there is a SDCH manager instance.
2393f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  global_->supported_domain_ = domain;
2403f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  global_->sdch_enabled_ = true;
2413f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen}
2423f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
2433f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// static
244c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::BlacklistDomain(const GURL& url) {
245c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!global_ )
246c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
247c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_->SetAllowLatencyExperiment(url, false);
248c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
249c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string domain(StringToLowerASCII(url.host()));
250c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int count = global_->blacklisted_domains_[domain];
251c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (count > 0)
252c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;  // Domain is already blacklisted.
253c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
254c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  count = 1 + 2 * global_->exponential_blacklist_count[domain];
255c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (count > 0)
256c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    global_->exponential_blacklist_count[domain] = count;
257c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  else
258c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    count = INT_MAX;
259c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
260c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_->blacklisted_domains_[domain] = count;
261c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
262c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
263c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
264c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::BlacklistDomainForever(const GURL& url) {
265c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!global_ )
266c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
267c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_->SetAllowLatencyExperiment(url, false);
268c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
269c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string domain(StringToLowerASCII(url.host()));
270c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_->exponential_blacklist_count[domain] = INT_MAX;
271c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  global_->blacklisted_domains_[domain] = INT_MAX;
272c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
273c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
2743f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// static
2753f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenvoid SdchManager::ClearBlacklistings() {
2763f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  Global()->blacklisted_domains_.clear();
2773f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  Global()->exponential_blacklist_count.clear();
2783f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen}
2793f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
2803f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// static
2813f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenvoid SdchManager::ClearDomainBlacklisting(const std::string& domain) {
2823f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  Global()->blacklisted_domains_.erase(StringToLowerASCII(domain));
2833f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen}
2843f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
2853f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// static
2863f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenint SdchManager::BlackListDomainCount(const std::string& domain) {
2873f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (Global()->blacklisted_domains_.end() ==
2883f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      Global()->blacklisted_domains_.find(domain))
2893f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return 0;
2903f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  return Global()->blacklisted_domains_[StringToLowerASCII(domain)];
2913f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen}
2923f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
2933f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// static
2943f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenint SdchManager::BlacklistDomainExponential(const std::string& domain) {
2953f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (Global()->exponential_blacklist_count.end() ==
2963f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      Global()->exponential_blacklist_count.find(domain))
2973f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    return 0;
2983f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  return Global()->exponential_blacklist_count[StringToLowerASCII(domain)];
299c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
300c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool SdchManager::IsInSupportedDomain(const GURL& url) {
302c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!sdch_enabled_ )
303c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
304c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!supported_domain_.empty() &&
305c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      !url.DomainIs(supported_domain_.data(), supported_domain_.size()))
306c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott     return false;  // It is not the singular supported domain.
307c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
308c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (blacklisted_domains_.empty())
309c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return true;
310c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
311c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string domain(StringToLowerASCII(url.host()));
312c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DomainCounter::iterator it = blacklisted_domains_.find(domain);
313c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (blacklisted_domains_.end() == it)
314c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return true;
315c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
316c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int count = it->second - 1;
317c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (count > 0)
318c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    blacklisted_domains_[domain] = count;
319c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  else
320c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    blacklisted_domains_.erase(domain);
321c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET);
322c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return false;
323c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
324c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
3253f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenvoid SdchManager::FetchDictionary(const GURL& request_url,
3263f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen                                  const GURL& dictionary_url) {
3273f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  if (SdchManager::Global()->CanFetchDictionary(request_url, dictionary_url) &&
3283f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      fetcher_.get())
3293f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    fetcher_->Schedule(dictionary_url);
3303f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen}
3313f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
332c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool SdchManager::CanFetchDictionary(const GURL& referring_url,
333c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                     const GURL& dictionary_url) const {
334c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  /* The user agent may retrieve a dictionary from the dictionary URL if all of
335c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott     the following are true:
336c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott       1 The dictionary URL host name matches the referrer URL host name
337c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott       2 The dictionary URL host name domain matches the parent domain of the
338c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott           referrer URL host name
339c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott       3 The parent domain of the referrer URL host name is not a top level
340c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott           domain
341c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott       4 The dictionary URL is not an HTTPS URL.
342c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott   */
343c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Item (1) above implies item (2).  Spec should be updated.
344c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // I take "host name match" to be "is identical to"
345c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (referring_url.host() != dictionary_url.host()) {
346c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST);
347c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
348c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
349c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (referring_url.SchemeIs("https")) {
350c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_SELECTED_FOR_SSL);
351c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
352c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
353c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
354c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // TODO(jar): Remove this failsafe conservative hack which is more restrictive
355c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // than current SDCH spec when needed, and justified by security audit.
356c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!referring_url.SchemeIs("http")) {
357c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_SELECTED_FROM_NON_HTTP);
358c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
359c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
360c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
361c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
362c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
363c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
364c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool SdchManager::AddSdchDictionary(const std::string& dictionary_text,
365c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    const GURL& dictionary_url) {
366c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string client_hash;
367c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string server_hash;
368c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  GenerateHash(dictionary_text, &client_hash, &server_hash);
369c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (dictionaries_.find(server_hash) != dictionaries_.end()) {
370c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_ALREADY_LOADED);
371c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;  // Already loaded.
372c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
373c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
374c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string domain, path;
375c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::set<int> ports;
37672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30));
377c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
378c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (dictionary_text.empty()) {
379c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_HAS_NO_TEXT);
380c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;  // Missing header.
381c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
382c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
383c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  size_t header_end = dictionary_text.find("\n\n");
384c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (std::string::npos == header_end) {
385c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_HAS_NO_HEADER);
386c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;  // Missing header.
387c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
388c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  size_t line_start = 0;  // Start of line being parsed.
389c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (1) {
390c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    size_t line_end = dictionary_text.find('\n', line_start);
391c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DCHECK(std::string::npos != line_end);
392c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    DCHECK(line_end <= header_end);
393c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
394c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    size_t colon_index = dictionary_text.find(':', line_start);
395c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (std::string::npos == colon_index) {
396c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      SdchErrorRecovery(DICTIONARY_HEADER_LINE_MISSING_COLON);
397c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return false;  // Illegal line missing a colon.
398c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
399c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
400c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (colon_index > line_end)
401c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      break;
402c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
403c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    size_t value_start = dictionary_text.find_first_not_of(" \t",
404c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                                           colon_index + 1);
405c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (std::string::npos != value_start) {
406c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if (value_start >= line_end)
407c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        break;
408c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      std::string name(dictionary_text, line_start, colon_index - line_start);
409c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      std::string value(dictionary_text, value_start, line_end - value_start);
410c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      name = StringToLowerASCII(name);
411c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if (name == "domain") {
412c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        domain = value;
413c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      } else if (name == "path") {
414c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        path = value;
415c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      } else if (name == "format-version") {
416c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        if (value != "1.0")
417c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott          return false;
418c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      } else if (name == "max-age") {
4193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        int64 seconds;
4203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        base::StringToInt64(value, &seconds);
42172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        expiration = base::Time::Now() + base::TimeDelta::FromSeconds(seconds);
422c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      } else if (name == "port") {
4233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        int port;
4243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        base::StringToInt(value, &port);
425c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        if (port >= 0)
426c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott          ports.insert(port);
427c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      }
428c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
429c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
430c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (line_end >= header_end)
431c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      break;
432c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    line_start = line_end + 1;
433c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
434c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
435c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!Dictionary::CanSet(domain, path, ports, dictionary_url))
436c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
437c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
438c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of
439c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // useless dictionaries.  We should probably have a cache eviction plan,
440c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // instead of just blocking additions.  For now, with the spec in flux, it
441c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // is probably not worth doing eviction handling.
442c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (kMaxDictionarySize < dictionary_text.size()) {
443c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_IS_TOO_LARGE);
444c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
445c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
446c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (kMaxDictionaryCount <= dictionaries_.size()) {
447c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    SdchErrorRecovery(DICTIONARY_COUNT_EXCEEDED);
448c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
449c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
450c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
4514ceda5254f89dd540c17e037d7ede22b1817f4e2Kristian Monsen  UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size());
452731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DVLOG(1) << "Loaded dictionary with client hash " << client_hash
453731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick           << " and server hash " << server_hash;
454c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  Dictionary* dictionary =
455c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      new Dictionary(dictionary_text, header_end + 2, client_hash,
456c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     dictionary_url, domain, path, expiration, ports);
457c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  dictionary->AddRef();
458c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  dictionaries_[server_hash] = dictionary;
459c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
460c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
461c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
462c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::GetVcdiffDictionary(const std::string& server_hash,
463c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    const GURL& referring_url, Dictionary** dictionary) {
464c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  *dictionary = NULL;
465c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DictionaryMap::iterator it = dictionaries_.find(server_hash);
466c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (it == dictionaries_.end()) {
467c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
468c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
469c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  Dictionary* matching_dictionary = it->second;
470c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!matching_dictionary->CanUse(referring_url))
471c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
472c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  *dictionary = matching_dictionary;
473c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
474c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
475c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// TODO(jar): If we have evictions from the dictionaries_, then we need to
476c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// change this interface to return a list of reference counted Dictionary
477c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// instances that can be used if/when a server specifies one.
478c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::GetAvailDictionaryList(const GURL& target_url,
479c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                         std::string* list) {
480c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int count = 0;
481c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (DictionaryMap::iterator it = dictionaries_.begin();
482c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott       it != dictionaries_.end(); ++it) {
483c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (!it->second->CanAdvertise(target_url))
484c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      continue;
485c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    ++count;
486c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (!list->empty())
487c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      list->append(",");
488c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    list->append(it->second->client_hash());
489c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
490c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Watch to see if we have corrupt or numerous dictionaries.
4914ceda5254f89dd540c17e037d7ede22b1817f4e2Kristian Monsen  if (count > 0)
4924ceda5254f89dd540c17e037d7ede22b1817f4e2Kristian Monsen    UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count);
493c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
494c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
495c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
496c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::GenerateHash(const std::string& dictionary_text,
497c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    std::string* client_hash, std::string* server_hash) {
498c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  char binary_hash[32];
499ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  crypto::SHA256HashString(dictionary_text, binary_hash, sizeof(binary_hash));
500c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
501c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string first_48_bits(&binary_hash[0], 6);
502c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string second_48_bits(&binary_hash[6], 6);
503c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  UrlSafeBase64Encode(first_48_bits, client_hash);
504c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  UrlSafeBase64Encode(second_48_bits, server_hash);
505c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
506c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK_EQ(server_hash->length(), 8u);
507c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK_EQ(client_hash->length(), 8u);
508c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
509c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
510c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//------------------------------------------------------------------------------
511c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Methods for supporting latency experiments.
512c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
513c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool SdchManager::AllowLatencyExperiment(const GURL& url) const {
514c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return allow_latency_experiment_.end() !=
515c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      allow_latency_experiment_.find(url.host());
516c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
517c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
518c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) {
519c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (enable) {
520c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    allow_latency_experiment_.insert(url.host());
521c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
522c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
523c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  ExperimentSet::iterator it = allow_latency_experiment_.find(url.host());
524c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (allow_latency_experiment_.end() == it)
525c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;  // It was already erased, or never allowed.
526c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  SdchErrorRecovery(LATENCY_TEST_DISALLOWED);
527c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  allow_latency_experiment_.erase(it);
528c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
5293f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen
5303f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// static
5313f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenvoid SdchManager::UrlSafeBase64Encode(const std::string& input,
5323f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen                                      std::string* output) {
5333f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // Since this is only done during a dictionary load, and hashes are only 8
5343f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // characters, we just do the simple fixup, rather than rewriting the encoder.
5353f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  base::Base64Encode(input, output);
5363f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  for (size_t i = 0; i < output->size(); ++i) {
5373f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    switch (output->data()[i]) {
5383f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      case '+':
5393f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen        (*output)[i] = '-';
5403f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen        continue;
5413f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      case '/':
5423f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen        (*output)[i] = '_';
5433f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen        continue;
5443f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      default:
5453f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen        continue;
5463f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    }
5473f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  }
5483f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen}
54972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
55072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}  // namespace net
551