15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/sdch_manager.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/base64.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/metrics/histogram.h"
105e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
115e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_util.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "crypto/sha2.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_http_job.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net {
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//------------------------------------------------------------------------------
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const size_t SdchManager::kMaxDictionarySize = 1000000;
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const size_t SdchManager::kMaxDictionaryCount = 20;
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SdchManager* SdchManager::global_ = NULL;
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SdchManager::g_sdch_enabled_ = true;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//------------------------------------------------------------------------------
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SdchManager::Dictionary::Dictionary(const std::string& dictionary_text,
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    size_t offset,
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    const std::string& client_hash,
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    const GURL& gurl,
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    const std::string& domain,
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    const std::string& path,
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    const base::Time& expiration,
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    const std::set<int>& ports)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : text_(dictionary_text, offset),
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      client_hash_(client_hash),
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      url_(gurl),
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      domain_(domain),
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path_(path),
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      expiration_(expiration),
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ports_(ports) {
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SdchManager::Dictionary::~Dictionary() {
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SdchManager::Dictionary::CanAdvertise(const GURL& target_url) {
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!SdchManager::Global()->IsInSupportedDomain(target_url))
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /* The specific rules of when a dictionary should be advertised in an
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     Avail-Dictionary header are modeled after the rules for cookie scoping. The
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     terms "domain-match" and "pathmatch" are defined in RFC 2965 [6]. A
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     dictionary may be advertised in the Avail-Dictionaries header exactly when
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     all of the following are true:
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      1. The server's effective host name domain-matches the Domain attribute of
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         the dictionary.
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      2. If the dictionary has a Port attribute, the request port is one of the
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ports listed in the Port attribute.
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      3. The request URI path-matches the path header of the dictionary.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      4. The request is not an HTTPS request.
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    */
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!DomainMatch(target_url, domain_))
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ports_.empty() && 0 == ports_.count(target_url.EffectiveIntPort()))
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (path_.size() && !PathMatch(target_url.path(), path_))
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (target_url.SchemeIsSecure())
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (base::Time::Now() > expiration_)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//------------------------------------------------------------------------------
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Security functions restricting loads and use of dictionaries.
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SdchManager::Dictionary::CanSet(const std::string& domain,
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     const std::string& path,
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     const std::set<int>& ports,
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     const GURL& dictionary_url) {
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!SdchManager::Global()->IsInSupportedDomain(dictionary_url))
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /*
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  A dictionary is invalid and must not be stored if any of the following are
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  true:
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    1. The dictionary has no Domain attribute.
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    2. The effective host name that derives from the referer URL host name does
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      not domain-match the Domain attribute.
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    3. The Domain attribute is a top level domain.
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    4. The referer URL host is a host domain name (not IP address) and has the
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      form HD, where D is the value of the Domain attribute, and H is a string
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      that contains one or more dots.
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    5. If the dictionary has a Port attribute and the referer URL's port was not
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      in the list.
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  */
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(jar): Redirects in dictionary fetches might plausibly be problematic,
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and hence the conservative approach is to not allow any redirects (if there
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // were any... then don't allow the dictionary to be set).
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (domain.empty()) {
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_MISSING_DOMAIN_SPECIFIER);
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;  // Domain is required.
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
112a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  if (registry_controlled_domains::GetDomainAndRegistry(
113a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        domain,
114a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES).empty()) {
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_SPECIFIES_TOP_LEVEL_DOMAIN);
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;  // domain was a TLD.
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!Dictionary::DomainMatch(dictionary_url, domain)) {
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_DOMAIN_NOT_MATCHING_SOURCE_URL);
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string referrer_url_host = dictionary_url.host();
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t postfix_domain_index = referrer_url_host.rfind(domain);
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // See if it is indeed a postfix, or just an internal string.
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (referrer_url_host.size() == postfix_domain_index + domain.size()) {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // It is a postfix... so check to see if there's a dot in the prefix.
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t end_of_host_index = referrer_url_host.find_first_of('.');
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (referrer_url_host.npos != end_of_host_index  &&
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        end_of_host_index < postfix_domain_index) {
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SdchErrorRecovery(DICTIONARY_REFERER_URL_HAS_DOT_IN_PREFIX);
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ports.empty()
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      && 0 == ports.count(dictionary_url.EffectiveIntPort())) {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_PORT_NOT_MATCHING_SOURCE_URL);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SdchManager::Dictionary::CanUse(const GURL& referring_url) {
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!SdchManager::Global()->IsInSupportedDomain(referring_url))
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /*
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    1. The request URL's host name domain-matches the Domain attribute of the
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dictionary.
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    2. If the dictionary has a Port attribute, the request port is one of the
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ports listed in the Port attribute.
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    3. The request URL path-matches the path attribute of the dictionary.
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    4. The request is not an HTTPS request.
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)*/
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!DomainMatch(referring_url, domain_)) {
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_DOMAIN);
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ports_.empty()
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      && 0 == ports_.count(referring_url.EffectiveIntPort())) {
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PORT_LIST);
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (path_.size() && !PathMatch(referring_url.path(), path_)) {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_PATH);
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (referring_url.SchemeIsSecure()) {
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_FOUND_HAS_WRONG_SCHEME);
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(jar): Remove overly restrictive failsafe test (added per security
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // review) when we have a need to be more general.
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!referring_url.SchemeIs("http")) {
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(ATTEMPT_TO_DECODE_NON_HTTP_DATA);
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SdchManager::Dictionary::PathMatch(const std::string& path,
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        const std::string& restriction) {
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /*  Must be either:
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  1. P2 is equal to P1
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  2. P2 is a prefix of P1 and either the final character in P2 is "/" or the
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      character following P2 in P1 is "/".
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      */
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (path == restriction)
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t prefix_length = restriction.size();
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (prefix_length > path.size())
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;  // Can't be a prefix.
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (0 != path.compare(0, prefix_length, restriction))
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return restriction[prefix_length - 1] == '/' || path[prefix_length] == '/';
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SdchManager::Dictionary::DomainMatch(const GURL& gurl,
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          const std::string& restriction) {
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(jar): This is not precisely a domain match definition.
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return gurl.DomainIs(restriction.data(), restriction.size());
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//------------------------------------------------------------------------------
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SdchManager::SdchManager() {
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!global_);
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(CalledOnValidThread());
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  global_ = this;
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SdchManager::~SdchManager() {
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(this, global_);
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(CalledOnValidThread());
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (!dictionaries_.empty()) {
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DictionaryMap::iterator it = dictionaries_.begin();
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    it->second->Release();
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dictionaries_.erase(it->first);
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  global_ = NULL;
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SdchManager::Shutdown() {
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EnableSdchSupport(false);
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!global_ )
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  global_->set_sdch_fetcher(NULL);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SdchManager* SdchManager::Global() {
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return global_;
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SdchManager::SdchErrorRecovery(ProblemCodes problem) {
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("Sdch3.ProblemCodes_4", problem, MAX_PROBLEM_CODE);
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SdchManager::set_sdch_fetcher(SdchFetcher* fetcher) {
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(CalledOnValidThread());
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fetcher_.reset(fetcher);
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SdchManager::EnableSdchSupport(bool enabled) {
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_sdch_enabled_ = enabled;
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SdchManager::BlacklistDomain(const GURL& url) {
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!global_ )
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  global_->SetAllowLatencyExperiment(url, false);
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string domain(StringToLowerASCII(url.host()));
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int count = global_->blacklisted_domains_[domain];
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (count > 0)
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // Domain is already blacklisted.
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  count = 1 + 2 * global_->exponential_blacklist_count[domain];
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (count > 0)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    global_->exponential_blacklist_count[domain] = count;
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    count = INT_MAX;
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  global_->blacklisted_domains_[domain] = count;
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SdchManager::BlacklistDomainForever(const GURL& url) {
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!global_ )
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  global_->SetAllowLatencyExperiment(url, false);
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string domain(StringToLowerASCII(url.host()));
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  global_->exponential_blacklist_count[domain] = INT_MAX;
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  global_->blacklisted_domains_[domain] = INT_MAX;
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SdchManager::ClearBlacklistings() {
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Global()->blacklisted_domains_.clear();
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Global()->exponential_blacklist_count.clear();
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SdchManager::ClearDomainBlacklisting(const std::string& domain) {
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Global()->blacklisted_domains_.erase(StringToLowerASCII(domain));
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int SdchManager::BlackListDomainCount(const std::string& domain) {
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (Global()->blacklisted_domains_.end() ==
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Global()->blacklisted_domains_.find(domain))
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return Global()->blacklisted_domains_[StringToLowerASCII(domain)];
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int SdchManager::BlacklistDomainExponential(const std::string& domain) {
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (Global()->exponential_blacklist_count.end() ==
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Global()->exponential_blacklist_count.find(domain))
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return Global()->exponential_blacklist_count[StringToLowerASCII(domain)];
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SdchManager::IsInSupportedDomain(const GURL& url) {
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(CalledOnValidThread());
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!g_sdch_enabled_ )
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (blacklisted_domains_.empty())
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string domain(StringToLowerASCII(url.host()));
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DomainCounter::iterator it = blacklisted_domains_.find(domain);
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (blacklisted_domains_.end() == it)
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int count = it->second - 1;
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (count > 0)
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    blacklisted_domains_[domain] = count;
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    blacklisted_domains_.erase(domain);
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET);
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SdchManager::FetchDictionary(const GURL& request_url,
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  const GURL& dictionary_url) {
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(CalledOnValidThread());
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (SdchManager::Global()->CanFetchDictionary(request_url, dictionary_url) &&
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      fetcher_.get())
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fetcher_->Schedule(dictionary_url);
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SdchManager::CanFetchDictionary(const GURL& referring_url,
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     const GURL& dictionary_url) const {
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(CalledOnValidThread());
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /* The user agent may retrieve a dictionary from the dictionary URL if all of
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     the following are true:
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       1 The dictionary URL host name matches the referrer URL host name
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       2 The dictionary URL host name domain matches the parent domain of the
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           referrer URL host name
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       3 The parent domain of the referrer URL host name is not a top level
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           domain
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       4 The dictionary URL is not an HTTPS URL.
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Item (1) above implies item (2).  Spec should be updated.
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // I take "host name match" to be "is identical to"
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (referring_url.host() != dictionary_url.host()) {
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_LOAD_ATTEMPT_FROM_DIFFERENT_HOST);
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (referring_url.SchemeIs("https")) {
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_SELECTED_FOR_SSL);
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(jar): Remove this failsafe conservative hack which is more restrictive
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // than current SDCH spec when needed, and justified by security audit.
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!referring_url.SchemeIs("http")) {
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_SELECTED_FROM_NON_HTTP);
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SdchManager::AddSdchDictionary(const std::string& dictionary_text,
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const GURL& dictionary_url) {
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(CalledOnValidThread());
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string client_hash;
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string server_hash;
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GenerateHash(dictionary_text, &client_hash, &server_hash);
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (dictionaries_.find(server_hash) != dictionaries_.end()) {
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_ALREADY_LOADED);
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;  // Already loaded.
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string domain, path;
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::set<int> ports;
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Time expiration(base::Time::Now() + base::TimeDelta::FromDays(30));
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (dictionary_text.empty()) {
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_HAS_NO_TEXT);
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;  // Missing header.
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t header_end = dictionary_text.find("\n\n");
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (std::string::npos == header_end) {
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_HAS_NO_HEADER);
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;  // Missing header.
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t line_start = 0;  // Start of line being parsed.
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (1) {
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t line_end = dictionary_text.find('\n', line_start);
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(std::string::npos != line_end);
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_LE(line_end, header_end);
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t colon_index = dictionary_text.find(':', line_start);
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (std::string::npos == colon_index) {
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SdchErrorRecovery(DICTIONARY_HEADER_LINE_MISSING_COLON);
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;  // Illegal line missing a colon.
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (colon_index > line_end)
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t value_start = dictionary_text.find_first_not_of(" \t",
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                           colon_index + 1);
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (std::string::npos != value_start) {
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (value_start >= line_end)
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string name(dictionary_text, line_start, colon_index - line_start);
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string value(dictionary_text, value_start, line_end - value_start);
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      name = StringToLowerASCII(name);
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (name == "domain") {
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        domain = value;
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (name == "path") {
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        path = value;
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (name == "format-version") {
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (value != "1.0")
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return false;
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (name == "max-age") {
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        int64 seconds;
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::StringToInt64(value, &seconds);
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        expiration = base::Time::Now() + base::TimeDelta::FromSeconds(seconds);
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (name == "port") {
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        int port;
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::StringToInt(value, &port);
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (port >= 0)
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ports.insert(port);
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (line_end >= header_end)
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    line_start = line_end + 1;
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!Dictionary::CanSet(domain, path, ports, dictionary_url))
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(jar): Remove these hacks to preclude a DOS attack involving piles of
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // useless dictionaries.  We should probably have a cache eviction plan,
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // instead of just blocking additions.  For now, with the spec in flux, it
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // is probably not worth doing eviction handling.
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (kMaxDictionarySize < dictionary_text.size()) {
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_IS_TOO_LARGE);
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (kMaxDictionaryCount <= dictionaries_.size()) {
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SdchErrorRecovery(DICTIONARY_COUNT_EXCEEDED);
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UMA_HISTOGRAM_COUNTS("Sdch3.Dictionary size loaded", dictionary_text.size());
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DVLOG(1) << "Loaded dictionary with client hash " << client_hash
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           << " and server hash " << server_hash;
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Dictionary* dictionary =
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new Dictionary(dictionary_text, header_end + 2, client_hash,
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     dictionary_url, domain, path, expiration, ports);
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionary->AddRef();
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dictionaries_[server_hash] = dictionary;
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SdchManager::GetVcdiffDictionary(const std::string& server_hash,
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const GURL& referring_url, Dictionary** dictionary) {
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(CalledOnValidThread());
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *dictionary = NULL;
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DictionaryMap::iterator it = dictionaries_.find(server_hash);
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (it == dictionaries_.end()) {
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Dictionary* matching_dictionary = it->second;
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!matching_dictionary->CanUse(referring_url))
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *dictionary = matching_dictionary;
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(jar): If we have evictions from the dictionaries_, then we need to
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// change this interface to return a list of reference counted Dictionary
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// instances that can be used if/when a server specifies one.
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SdchManager::GetAvailDictionaryList(const GURL& target_url,
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         std::string* list) {
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(CalledOnValidThread());
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int count = 0;
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (DictionaryMap::iterator it = dictionaries_.begin();
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != dictionaries_.end(); ++it) {
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!it->second->CanAdvertise(target_url))
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++count;
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!list->empty())
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      list->append(",");
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    list->append(it->second->client_hash());
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Watch to see if we have corrupt or numerous dictionaries.
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (count > 0)
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UMA_HISTOGRAM_COUNTS("Sdch3.Advertisement_Count", count);
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SdchManager::GenerateHash(const std::string& dictionary_text,
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string* client_hash, std::string* server_hash) {
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  char binary_hash[32];
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  crypto::SHA256HashString(dictionary_text, binary_hash, sizeof(binary_hash));
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string first_48_bits(&binary_hash[0], 6);
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string second_48_bits(&binary_hash[6], 6);
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UrlSafeBase64Encode(first_48_bits, client_hash);
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UrlSafeBase64Encode(second_48_bits, server_hash);
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(server_hash->length(), 8u);
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(client_hash->length(), 8u);
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//------------------------------------------------------------------------------
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Methods for supporting latency experiments.
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SdchManager::AllowLatencyExperiment(const GURL& url) const {
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(CalledOnValidThread());
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return allow_latency_experiment_.end() !=
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      allow_latency_experiment_.find(url.host());
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SdchManager::SetAllowLatencyExperiment(const GURL& url, bool enable) {
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(CalledOnValidThread());
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (enable) {
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    allow_latency_experiment_.insert(url.host());
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExperimentSet::iterator it = allow_latency_experiment_.find(url.host());
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (allow_latency_experiment_.end() == it)
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // It was already erased, or never allowed.
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SdchErrorRecovery(LATENCY_TEST_DISALLOWED);
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  allow_latency_experiment_.erase(it);
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SdchManager::UrlSafeBase64Encode(const std::string& input,
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      std::string* output) {
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Since this is only done during a dictionary load, and hashes are only 8
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // characters, we just do the simple fixup, rather than rewriting the encoder.
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Base64Encode(input, output);
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < output->size(); ++i) {
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (output->data()[i]) {
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case '+':
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (*output)[i] = '-';
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue;
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case '/':
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (*output)[i] = '_';
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue;
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue;
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace net
567