toolkit/components/url-classifier/LookupCache.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/components/url-classifier/LookupCache.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,706 @@
     1.4 +//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "LookupCache.h"
    1.10 +#include "HashStore.h"
    1.11 +#include "nsISeekableStream.h"
    1.12 +#include "mozilla/Telemetry.h"
    1.13 +#include "prlog.h"
    1.14 +#include "prprf.h"
    1.15 +
    1.16 +// We act as the main entry point for all the real lookups,
    1.17 +// so note that those are not done to the actual HashStore.
    1.18 +// The latter solely exists to store the data needed to handle
    1.19 +// the updates from the protocol.
    1.20 +
    1.21 +// This module has its own store, which stores the Completions,
    1.22 +// mostly caching lookups that have happened over the net.
    1.23 +// The prefixes are cached/checked by looking them up in the
    1.24 +// PrefixSet.
    1.25 +
    1.26 +// Data format for the ".cache" files:
    1.27 +//    uint32_t magic           Identify the file type
    1.28 +//    uint32_t version         Version identifier for file format
    1.29 +//    uint32_t numCompletions  Amount of completions stored
    1.30 +//    0...numCompletions     256-bit Completions
    1.31 +
    1.32 +// Name of the lookupcomplete cache
    1.33 +#define CACHE_SUFFIX ".cache"
    1.34 +
    1.35 +// Name of the persistent PrefixSet storage
    1.36 +#define PREFIXSET_SUFFIX  ".pset"
    1.37 +
    1.38 +// NSPR_LOG_MODULES=UrlClassifierDbService:5
    1.39 +extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
    1.40 +#if defined(PR_LOGGING)
    1.41 +#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
    1.42 +#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
    1.43 +#else
    1.44 +#define LOG(args)
    1.45 +#define LOG_ENABLED() (false)
    1.46 +#endif
    1.47 +
    1.48 +namespace mozilla {
    1.49 +namespace safebrowsing {
    1.50 +
    1.51 +const uint32_t LOOKUPCACHE_MAGIC = 0x1231af3e;
    1.52 +const uint32_t CURRENT_VERSION = 2;
    1.53 +
    1.54 +LookupCache::LookupCache(const nsACString& aTableName, nsIFile* aStoreDir)
    1.55 +  : mPrimed(false)
    1.56 +  , mTableName(aTableName)
    1.57 +  , mStoreDirectory(aStoreDir)
    1.58 +{
    1.59 +}
    1.60 +
    1.61 +nsresult
    1.62 +LookupCache::Init()
    1.63 +{
    1.64 +  mPrefixSet = new nsUrlClassifierPrefixSet();
    1.65 +  nsresult rv = mPrefixSet->Init(mTableName);
    1.66 +  NS_ENSURE_SUCCESS(rv, rv);
    1.67 +
    1.68 +  return NS_OK;
    1.69 +}
    1.70 +
    1.71 +LookupCache::~LookupCache()
    1.72 +{
    1.73 +}
    1.74 +
    1.75 +nsresult
    1.76 +LookupCache::Open()
    1.77 +{
    1.78 +  nsCOMPtr<nsIFile> storeFile;
    1.79 +
    1.80 +  nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
    1.81 +  NS_ENSURE_SUCCESS(rv, rv);
    1.82 +
    1.83 +  rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
    1.84 +  NS_ENSURE_SUCCESS(rv, rv);
    1.85 +
    1.86 +  nsCOMPtr<nsIInputStream> inputStream;
    1.87 +  rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), storeFile,
    1.88 +                                  PR_RDONLY | nsIFile::OS_READAHEAD);
    1.89 +
    1.90 +  if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
    1.91 +    Reset();
    1.92 +    return rv;
    1.93 +  }
    1.94 +
    1.95 +  if (rv == NS_ERROR_FILE_NOT_FOUND) {
    1.96 +    // Simply lacking a .cache file is a recoverable error,
    1.97 +    // as unlike the .pset/.sbstore files it is a pure cache.
    1.98 +    // Just create a new empty one.
    1.99 +    ClearCompleteCache();
   1.100 +  } else {
   1.101 +    // Read in the .cache file
   1.102 +    rv = ReadHeader(inputStream);
   1.103 +    NS_ENSURE_SUCCESS(rv, rv);
   1.104 +    LOG(("ReadCompletions"));
   1.105 +    rv = ReadCompletions(inputStream);
   1.106 +    NS_ENSURE_SUCCESS(rv, rv);
   1.107 +
   1.108 +    rv = inputStream->Close();
   1.109 +    NS_ENSURE_SUCCESS(rv, rv);
   1.110 +  }
   1.111 +
   1.112 +  LOG(("Loading PrefixSet"));
   1.113 +  rv = LoadPrefixSet();
   1.114 +  NS_ENSURE_SUCCESS(rv, rv);
   1.115 +
   1.116 +  return NS_OK;
   1.117 +}
   1.118 +
   1.119 +nsresult
   1.120 +LookupCache::UpdateDirHandle(nsIFile* aStoreDirectory)
   1.121 +{
   1.122 +  return aStoreDirectory->Clone(getter_AddRefs(mStoreDirectory));
   1.123 +}
   1.124 +
   1.125 +nsresult
   1.126 +LookupCache::Reset()
   1.127 +{
   1.128 +  LOG(("LookupCache resetting"));
   1.129 +
   1.130 +  nsCOMPtr<nsIFile> storeFile;
   1.131 +  nsCOMPtr<nsIFile> prefixsetFile;
   1.132 +  nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
   1.133 +  NS_ENSURE_SUCCESS(rv, rv);
   1.134 +  rv = mStoreDirectory->Clone(getter_AddRefs(prefixsetFile));
   1.135 +  NS_ENSURE_SUCCESS(rv, rv);
   1.136 +
   1.137 +  rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
   1.138 +  NS_ENSURE_SUCCESS(rv, rv);
   1.139 +  rv = prefixsetFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX));
   1.140 +  NS_ENSURE_SUCCESS(rv, rv);
   1.141 +
   1.142 +  rv = storeFile->Remove(false);
   1.143 +  NS_ENSURE_SUCCESS(rv, rv);
   1.144 +  rv = prefixsetFile->Remove(false);
   1.145 +  NS_ENSURE_SUCCESS(rv, rv);
   1.146 +
   1.147 +  ClearAll();
   1.148 +
   1.149 +  return NS_OK;
   1.150 +}
   1.151 +
   1.152 +
   1.153 +nsresult
   1.154 +LookupCache::Build(AddPrefixArray& aAddPrefixes,
   1.155 +                   AddCompleteArray& aAddCompletes)
   1.156 +{
   1.157 +  Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_COMPLETIONS,
   1.158 +                        static_cast<uint32_t>(aAddCompletes.Length()));
   1.159 +
   1.160 +  mCompletions.Clear();
   1.161 +  mCompletions.SetCapacity(aAddCompletes.Length());
   1.162 +  for (uint32_t i = 0; i < aAddCompletes.Length(); i++) {
   1.163 +    mCompletions.AppendElement(aAddCompletes[i].CompleteHash());
   1.164 +  }
   1.165 +  aAddCompletes.Clear();
   1.166 +  mCompletions.Sort();
   1.167 +
   1.168 +  Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_PREFIXES,
   1.169 +                        static_cast<uint32_t>(aAddPrefixes.Length()));
   1.170 +
   1.171 +  nsresult rv = ConstructPrefixSet(aAddPrefixes);
   1.172 +  NS_ENSURE_SUCCESS(rv, rv);
   1.173 +  mPrimed = true;
   1.174 +
   1.175 +  return NS_OK;
   1.176 +}
   1.177 +
   1.178 +#if defined(DEBUG) && defined(PR_LOGGING)
   1.179 +void
   1.180 +LookupCache::Dump()
   1.181 +{
   1.182 +  if (!LOG_ENABLED())
   1.183 +    return;
   1.184 +
   1.185 +  for (uint32_t i = 0; i < mCompletions.Length(); i++) {
   1.186 +    nsAutoCString str;
   1.187 +    mCompletions[i].ToString(str);
   1.188 +    LOG(("Completion: %s", str.get()));
   1.189 +  }
   1.190 +}
   1.191 +#endif
   1.192 +
   1.193 +nsresult
   1.194 +LookupCache::Has(const Completion& aCompletion,
   1.195 +                 bool* aHas, bool* aComplete)
   1.196 +{
   1.197 +  *aHas = *aComplete = false;
   1.198 +
   1.199 +  uint32_t prefix = aCompletion.ToUint32();
   1.200 +
   1.201 +  bool found;
   1.202 +  nsresult rv = mPrefixSet->Contains(prefix, &found);
   1.203 +  NS_ENSURE_SUCCESS(rv, rv);
   1.204 +
   1.205 +  LOG(("Probe in %s: %X, found %d", mTableName.get(), prefix, found));
   1.206 +
   1.207 +  if (found) {
   1.208 +    *aHas = true;
   1.209 +  }
   1.210 +
   1.211 +  if (mCompletions.BinaryIndexOf(aCompletion) != nsTArray<Completion>::NoIndex) {
   1.212 +    LOG(("Complete in %s", mTableName.get()));
   1.213 +    *aComplete = true;
   1.214 +    *aHas = true;
   1.215 +  }
   1.216 +
   1.217 +  return NS_OK;
   1.218 +}
   1.219 +
   1.220 +nsresult
   1.221 +LookupCache::WriteFile()
   1.222 +{
   1.223 +  nsCOMPtr<nsIFile> storeFile;
   1.224 +  nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
   1.225 +  NS_ENSURE_SUCCESS(rv, rv);
   1.226 +  rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
   1.227 +  NS_ENSURE_SUCCESS(rv, rv);
   1.228 +
   1.229 +  nsCOMPtr<nsIOutputStream> out;
   1.230 +  rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out), storeFile,
   1.231 +                                       PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
   1.232 +  NS_ENSURE_SUCCESS(rv, rv);
   1.233 +
   1.234 +  UpdateHeader();
   1.235 +  LOG(("Writing %d completions", mHeader.numCompletions));
   1.236 +
   1.237 +  uint32_t written;
   1.238 +  rv = out->Write(reinterpret_cast<char*>(&mHeader), sizeof(mHeader), &written);
   1.239 +  NS_ENSURE_SUCCESS(rv, rv);
   1.240 +
   1.241 +  rv = WriteTArray(out, mCompletions);
   1.242 +  NS_ENSURE_SUCCESS(rv, rv);
   1.243 +
   1.244 +  nsCOMPtr<nsISafeOutputStream> safeOut = do_QueryInterface(out);
   1.245 +  rv = safeOut->Finish();
   1.246 +  NS_ENSURE_SUCCESS(rv, rv);
   1.247 +
   1.248 +  rv = EnsureSizeConsistent();
   1.249 +  NS_ENSURE_SUCCESS(rv, rv);
   1.250 +
   1.251 +  nsCOMPtr<nsIFile> psFile;
   1.252 +  rv = mStoreDirectory->Clone(getter_AddRefs(psFile));
   1.253 +  NS_ENSURE_SUCCESS(rv, rv);
   1.254 +
   1.255 +  rv = psFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX));
   1.256 +  NS_ENSURE_SUCCESS(rv, rv);
   1.257 +
   1.258 +  rv = mPrefixSet->StoreToFile(psFile);
   1.259 +  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store the prefixset");
   1.260 +
   1.261 +  return NS_OK;
   1.262 +}
   1.263 +
   1.264 +void
   1.265 +LookupCache::ClearAll()
   1.266 +{
   1.267 +  ClearCompleteCache();
   1.268 +  mPrefixSet->SetPrefixes(nullptr, 0);
   1.269 +  mPrimed = false;
   1.270 +}
   1.271 +
   1.272 +void
   1.273 +LookupCache::ClearCompleteCache()
   1.274 +{
   1.275 +  mCompletions.Clear();
   1.276 +  UpdateHeader();
   1.277 +}
   1.278 +
   1.279 +void
   1.280 +LookupCache::UpdateHeader()
   1.281 +{
   1.282 +  mHeader.magic = LOOKUPCACHE_MAGIC;
   1.283 +  mHeader.version = CURRENT_VERSION;
   1.284 +  mHeader.numCompletions = mCompletions.Length();
   1.285 +}
   1.286 +
   1.287 +nsresult
   1.288 +LookupCache::EnsureSizeConsistent()
   1.289 +{
   1.290 +  nsCOMPtr<nsIFile> storeFile;
   1.291 +  nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
   1.292 +  NS_ENSURE_SUCCESS(rv, rv);
   1.293 +  rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
   1.294 +  NS_ENSURE_SUCCESS(rv, rv);
   1.295 +
   1.296 +  int64_t fileSize;
   1.297 +  rv = storeFile->GetFileSize(&fileSize);
   1.298 +  NS_ENSURE_SUCCESS(rv, rv);
   1.299 +
   1.300 +  if (fileSize < 0) {
   1.301 +    return NS_ERROR_FAILURE;
   1.302 +  }
   1.303 +
   1.304 +  int64_t expectedSize = sizeof(mHeader)
   1.305 +                        + mHeader.numCompletions*sizeof(Completion);
   1.306 +  if (expectedSize != fileSize) {
   1.307 +    NS_WARNING("File length does not match. Probably corrupted.");
   1.308 +    Reset();
   1.309 +    return NS_ERROR_FILE_CORRUPTED;
   1.310 +  }
   1.311 +
   1.312 +  return NS_OK;
   1.313 +}
   1.314 +
   1.315 +nsresult
   1.316 +LookupCache::ReadHeader(nsIInputStream* aInputStream)
   1.317 +{
   1.318 +  if (!aInputStream) {
   1.319 +    ClearCompleteCache();
   1.320 +    return NS_OK;
   1.321 +  }
   1.322 +
   1.323 +  nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aInputStream);
   1.324 +  nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
   1.325 +  NS_ENSURE_SUCCESS(rv, rv);
   1.326 +
   1.327 +  void *buffer = &mHeader;
   1.328 +  rv = NS_ReadInputStreamToBuffer(aInputStream,
   1.329 +                                  &buffer,
   1.330 +                                  sizeof(Header));
   1.331 +  NS_ENSURE_SUCCESS(rv, rv);
   1.332 +
   1.333 +  if (mHeader.magic != LOOKUPCACHE_MAGIC || mHeader.version != CURRENT_VERSION) {
   1.334 +    NS_WARNING("Unexpected header data in the store.");
   1.335 +    Reset();
   1.336 +    return NS_ERROR_FILE_CORRUPTED;
   1.337 +  }
   1.338 +  LOG(("%d completions present", mHeader.numCompletions));
   1.339 +
   1.340 +  rv = EnsureSizeConsistent();
   1.341 +  NS_ENSURE_SUCCESS(rv, rv);
   1.342 +
   1.343 +  return NS_OK;
   1.344 +}
   1.345 +
   1.346 +nsresult
   1.347 +LookupCache::ReadCompletions(nsIInputStream* aInputStream)
   1.348 +{
   1.349 +  if (!mHeader.numCompletions) {
   1.350 +    mCompletions.Clear();
   1.351 +    return NS_OK;
   1.352 +  }
   1.353 +
   1.354 +  nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aInputStream);
   1.355 +  nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, sizeof(Header));
   1.356 +  NS_ENSURE_SUCCESS(rv, rv);
   1.357 +
   1.358 +  rv = ReadTArray(aInputStream, &mCompletions, mHeader.numCompletions);
   1.359 +  NS_ENSURE_SUCCESS(rv, rv);
   1.360 +
   1.361 +  LOG(("Read %d completions", mCompletions.Length()));
   1.362 +
   1.363 +  return NS_OK;
   1.364 +}
   1.365 +
   1.366 +/* static */ bool
   1.367 +LookupCache::IsCanonicalizedIP(const nsACString& aHost)
   1.368 +{
   1.369 +  // The canonicalization process will have left IP addresses in dotted
   1.370 +  // decimal with no surprises.
   1.371 +  uint32_t i1, i2, i3, i4;
   1.372 +  char c;
   1.373 +  if (PR_sscanf(PromiseFlatCString(aHost).get(), "%u.%u.%u.%u%c",
   1.374 +                &i1, &i2, &i3, &i4, &c) == 4) {
   1.375 +    return (i1 <= 0xFF && i2 <= 0xFF && i3 <= 0xFF && i4 <= 0xFF);
   1.376 +  }
   1.377 +
   1.378 +  return false;
   1.379 +}
   1.380 +
   1.381 +/* static */ nsresult
   1.382 +LookupCache::GetKey(const nsACString& aSpec,
   1.383 +                    Completion* aHash,
   1.384 +                    nsCOMPtr<nsICryptoHash>& aCryptoHash)
   1.385 +{
   1.386 +  nsACString::const_iterator begin, end, iter;
   1.387 +  aSpec.BeginReading(begin);
   1.388 +  aSpec.EndReading(end);
   1.389 +
   1.390 +  iter = begin;
   1.391 +  if (!FindCharInReadable('/', iter, end)) {
   1.392 +   return NS_OK;
   1.393 +  }
   1.394 +
   1.395 +  const nsCSubstring& host = Substring(begin, iter);
   1.396 +
   1.397 +  if (IsCanonicalizedIP(host)) {
   1.398 +    nsAutoCString key;
   1.399 +    key.Assign(host);
   1.400 +    key.Append("/");
   1.401 +    return aHash->FromPlaintext(key, aCryptoHash);
   1.402 +  }
   1.403 +
   1.404 +  nsTArray<nsCString> hostComponents;
   1.405 +  ParseString(PromiseFlatCString(host), '.', hostComponents);
   1.406 +
   1.407 +  if (hostComponents.Length() < 2)
   1.408 +    return NS_ERROR_FAILURE;
   1.409 +
   1.410 +  int32_t last = int32_t(hostComponents.Length()) - 1;
   1.411 +  nsAutoCString lookupHost;
   1.412 +
   1.413 +  if (hostComponents.Length() > 2) {
   1.414 +    lookupHost.Append(hostComponents[last - 2]);
   1.415 +    lookupHost.Append(".");
   1.416 +  }
   1.417 +
   1.418 +  lookupHost.Append(hostComponents[last - 1]);
   1.419 +  lookupHost.Append(".");
   1.420 +  lookupHost.Append(hostComponents[last]);
   1.421 +  lookupHost.Append("/");
   1.422 +
   1.423 +  return aHash->FromPlaintext(lookupHost, aCryptoHash);
   1.424 +}
   1.425 +
   1.426 +/* static */ nsresult
   1.427 +LookupCache::GetLookupFragments(const nsACString& aSpec,
   1.428 +                                nsTArray<nsCString>* aFragments)
   1.429 +
   1.430 +{
   1.431 +  aFragments->Clear();
   1.432 +
   1.433 +  nsACString::const_iterator begin, end, iter;
   1.434 +  aSpec.BeginReading(begin);
   1.435 +  aSpec.EndReading(end);
   1.436 +
   1.437 +  iter = begin;
   1.438 +  if (!FindCharInReadable('/', iter, end)) {
   1.439 +    return NS_OK;
   1.440 +  }
   1.441 +
   1.442 +  const nsCSubstring& host = Substring(begin, iter++);
   1.443 +  nsAutoCString path;
   1.444 +  path.Assign(Substring(iter, end));
   1.445 +
   1.446 +  /**
   1.447 +   * From the protocol doc:
   1.448 +   * For the hostname, the client will try at most 5 different strings.  They
   1.449 +   * are:
   1.450 +   * a) The exact hostname of the url
   1.451 +   * b) The 4 hostnames formed by starting with the last 5 components and
   1.452 +   *    successivly removing the leading component.  The top-level component
   1.453 +   *    can be skipped. This is not done if the hostname is a numerical IP.
   1.454 +   */
   1.455 +  nsTArray<nsCString> hosts;
   1.456 +  hosts.AppendElement(host);
   1.457 +
   1.458 +  if (!IsCanonicalizedIP(host)) {
   1.459 +    host.BeginReading(begin);
   1.460 +    host.EndReading(end);
   1.461 +    int numHostComponents = 0;
   1.462 +    while (RFindInReadable(NS_LITERAL_CSTRING("."), begin, end) &&
   1.463 +           numHostComponents < MAX_HOST_COMPONENTS) {
   1.464 +      // don't bother checking toplevel domains
   1.465 +      if (++numHostComponents >= 2) {
   1.466 +        host.EndReading(iter);
   1.467 +        hosts.AppendElement(Substring(end, iter));
   1.468 +      }
   1.469 +      end = begin;
   1.470 +      host.BeginReading(begin);
   1.471 +    }
   1.472 +  }
   1.473 +
   1.474 +  /**
   1.475 +   * From the protocol doc:
   1.476 +   * For the path, the client will also try at most 6 different strings.
   1.477 +   * They are:
   1.478 +   * a) the exact path of the url, including query parameters
   1.479 +   * b) the exact path of the url, without query parameters
   1.480 +   * c) the 4 paths formed by starting at the root (/) and
   1.481 +   *    successively appending path components, including a trailing
   1.482 +   *    slash.  This behavior should only extend up to the next-to-last
   1.483 +   *    path component, that is, a trailing slash should never be
   1.484 +   *    appended that was not present in the original url.
   1.485 +   */
   1.486 +  nsTArray<nsCString> paths;
   1.487 +  nsAutoCString pathToAdd;
   1.488 +
   1.489 +  path.BeginReading(begin);
   1.490 +  path.EndReading(end);
   1.491 +  iter = begin;
   1.492 +  if (FindCharInReadable('?', iter, end)) {
   1.493 +    pathToAdd = Substring(begin, iter);
   1.494 +    paths.AppendElement(pathToAdd);
   1.495 +    end = iter;
   1.496 +  }
   1.497 +
   1.498 +  int numPathComponents = 1;
   1.499 +  iter = begin;
   1.500 +  while (FindCharInReadable('/', iter, end) &&
   1.501 +         numPathComponents < MAX_PATH_COMPONENTS) {
   1.502 +    iter++;
   1.503 +    pathToAdd.Assign(Substring(begin, iter));
   1.504 +    paths.AppendElement(pathToAdd);
   1.505 +    numPathComponents++;
   1.506 +  }
   1.507 +
   1.508 +  // If we haven't already done so, add the full path
   1.509 +  if (!pathToAdd.Equals(path)) {
   1.510 +    paths.AppendElement(path);
   1.511 +  }
   1.512 +  // Check an empty path (for whole-domain blacklist entries)
   1.513 +  paths.AppendElement(EmptyCString());
   1.514 +
   1.515 +  for (uint32_t hostIndex = 0; hostIndex < hosts.Length(); hostIndex++) {
   1.516 +    for (uint32_t pathIndex = 0; pathIndex < paths.Length(); pathIndex++) {
   1.517 +      nsCString key;
   1.518 +      key.Assign(hosts[hostIndex]);
   1.519 +      key.Append('/');
   1.520 +      key.Append(paths[pathIndex]);
   1.521 +      LOG(("Checking fragment %s", key.get()));
   1.522 +
   1.523 +      aFragments->AppendElement(key);
   1.524 +    }
   1.525 +  }
   1.526 +
   1.527 +  return NS_OK;
   1.528 +}
   1.529 +
   1.530 +/* static */ nsresult
   1.531 +LookupCache::GetHostKeys(const nsACString& aSpec,
   1.532 +                         nsTArray<nsCString>* aHostKeys)
   1.533 +{
   1.534 +  nsACString::const_iterator begin, end, iter;
   1.535 +  aSpec.BeginReading(begin);
   1.536 +  aSpec.EndReading(end);
   1.537 +
   1.538 +  iter = begin;
   1.539 +  if (!FindCharInReadable('/', iter, end)) {
   1.540 +    return NS_OK;
   1.541 +  }
   1.542 +
   1.543 +  const nsCSubstring& host = Substring(begin, iter);
   1.544 +
   1.545 +  if (IsCanonicalizedIP(host)) {
   1.546 +    nsCString *key = aHostKeys->AppendElement();
   1.547 +    if (!key)
   1.548 +      return NS_ERROR_OUT_OF_MEMORY;
   1.549 +
   1.550 +    key->Assign(host);
   1.551 +    key->Append("/");
   1.552 +    return NS_OK;
   1.553 +  }
   1.554 +
   1.555 +  nsTArray<nsCString> hostComponents;
   1.556 +  ParseString(PromiseFlatCString(host), '.', hostComponents);
   1.557 +
   1.558 +  if (hostComponents.Length() < 2) {
   1.559 +    // no host or toplevel host, this won't match anything in the db
   1.560 +    return NS_OK;
   1.561 +  }
   1.562 +
   1.563 +  // First check with two domain components
   1.564 +  int32_t last = int32_t(hostComponents.Length()) - 1;
   1.565 +  nsCString *lookupHost = aHostKeys->AppendElement();
   1.566 +  if (!lookupHost)
   1.567 +    return NS_ERROR_OUT_OF_MEMORY;
   1.568 +
   1.569 +  lookupHost->Assign(hostComponents[last - 1]);
   1.570 +  lookupHost->Append(".");
   1.571 +  lookupHost->Append(hostComponents[last]);
   1.572 +  lookupHost->Append("/");
   1.573 +
   1.574 +  // Now check with three domain components
   1.575 +  if (hostComponents.Length() > 2) {
   1.576 +    nsCString *lookupHost2 = aHostKeys->AppendElement();
   1.577 +    if (!lookupHost2)
   1.578 +      return NS_ERROR_OUT_OF_MEMORY;
   1.579 +    lookupHost2->Assign(hostComponents[last - 2]);
   1.580 +    lookupHost2->Append(".");
   1.581 +    lookupHost2->Append(*lookupHost);
   1.582 +  }
   1.583 +
   1.584 +  return NS_OK;
   1.585 +}
   1.586 +
   1.587 +bool LookupCache::IsPrimed()
   1.588 +{
   1.589 +  return mPrimed;
   1.590 +}
   1.591 +
   1.592 +#ifdef DEBUG
   1.593 +template <class T>
   1.594 +static void EnsureSorted(T* aArray)
   1.595 +{
   1.596 +  typename T::elem_type* start = aArray->Elements();
   1.597 +  typename T::elem_type* end = aArray->Elements() + aArray->Length();
   1.598 +  typename T::elem_type* iter = start;
   1.599 +  typename T::elem_type* previous = start;
   1.600 +
   1.601 +  while (iter != end) {
   1.602 +    previous = iter;
   1.603 +    ++iter;
   1.604 +    if (iter != end) {
   1.605 +      MOZ_ASSERT(*previous <= *iter);
   1.606 +    }
   1.607 +  }
   1.608 +  return;
   1.609 +}
   1.610 +#endif
   1.611 +
   1.612 +nsresult
   1.613 +LookupCache::ConstructPrefixSet(AddPrefixArray& aAddPrefixes)
   1.614 +{
   1.615 +  Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_CONSTRUCT_TIME> timer;
   1.616 +
   1.617 +  nsTArray<uint32_t> array;
   1.618 +  array.SetCapacity(aAddPrefixes.Length());
   1.619 +
   1.620 +  for (uint32_t i = 0; i < aAddPrefixes.Length(); i++) {
   1.621 +    array.AppendElement(aAddPrefixes[i].PrefixHash().ToUint32());
   1.622 +  }
   1.623 +  aAddPrefixes.Clear();
   1.624 +
   1.625 +#ifdef DEBUG
   1.626 +  // PrefixSet requires sorted order
   1.627 +  EnsureSorted(&array);
   1.628 +#endif
   1.629 +
   1.630 +  // construct new one, replace old entries
   1.631 +  nsresult rv = mPrefixSet->SetPrefixes(array.Elements(), array.Length());
   1.632 +  if (NS_FAILED(rv)) {
   1.633 +    goto error_bailout;
   1.634 +  }
   1.635 +
   1.636 +#ifdef DEBUG
   1.637 +  uint32_t size;
   1.638 +  size = mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
   1.639 +  LOG(("SB tree done, size = %d bytes\n", size));
   1.640 +#endif
   1.641 +
   1.642 +  mPrimed = true;
   1.643 +
   1.644 +  return NS_OK;
   1.645 +
   1.646 + error_bailout:
   1.647 +  Telemetry::Accumulate(Telemetry::URLCLASSIFIER_PS_FAILURE, 1);
   1.648 +  return rv;
   1.649 +}
   1.650 +
   1.651 +nsresult
   1.652 +LookupCache::LoadPrefixSet()
   1.653 +{
   1.654 +  nsCOMPtr<nsIFile> psFile;
   1.655 +  nsresult rv = mStoreDirectory->Clone(getter_AddRefs(psFile));
   1.656 +  NS_ENSURE_SUCCESS(rv, rv);
   1.657 +
   1.658 +  rv = psFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX));
   1.659 +  NS_ENSURE_SUCCESS(rv, rv);
   1.660 +
   1.661 +  bool exists;
   1.662 +  rv = psFile->Exists(&exists);
   1.663 +  NS_ENSURE_SUCCESS(rv, rv);
   1.664 +
   1.665 +  if (exists) {
   1.666 +    LOG(("stored PrefixSet exists, loading from disk"));
   1.667 +    rv = mPrefixSet->LoadFromFile(psFile);
   1.668 +    if (NS_FAILED(rv)) {
   1.669 +      if (rv == NS_ERROR_FILE_CORRUPTED) {
   1.670 +        Reset();
   1.671 +      }
   1.672 +      return rv;
   1.673 +    }
   1.674 +    mPrimed = true;
   1.675 +  } else {
   1.676 +    LOG(("no (usable) stored PrefixSet found"));
   1.677 +  }
   1.678 +
   1.679 +#ifdef DEBUG
   1.680 +  if (mPrimed) {
   1.681 +    uint32_t size = mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
   1.682 +    LOG(("SB tree done, size = %d bytes\n", size));
   1.683 +  }
   1.684 +#endif
   1.685 +
   1.686 +  return NS_OK;
   1.687 +}
   1.688 +
   1.689 +nsresult
   1.690 +LookupCache::GetPrefixes(nsTArray<uint32_t>* aAddPrefixes)
   1.691 +{
   1.692 +  if (!mPrimed) {
   1.693 +    // This can happen if its a new table, so no error.
   1.694 +    LOG(("GetPrefixes from empty LookupCache"));
   1.695 +    return NS_OK;
   1.696 +  }
   1.697 +  uint32_t cnt;
   1.698 +  uint32_t *arr;
   1.699 +  nsresult rv = mPrefixSet->GetPrefixes(&cnt, &arr);
   1.700 +  NS_ENSURE_SUCCESS(rv, rv);
   1.701 +  if (!aAddPrefixes->AppendElements(arr, cnt))
   1.702 +    return NS_ERROR_FAILURE;
   1.703 +  nsMemory::Free(arr);
   1.704 +  return NS_OK;
   1.705 +}
   1.706 +
   1.707 +
   1.708 +}
   1.709 +}

mercurial