toolkit/components/url-classifier/LookupCache.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "LookupCache.h"
michael@0 7 #include "HashStore.h"
michael@0 8 #include "nsISeekableStream.h"
michael@0 9 #include "mozilla/Telemetry.h"
michael@0 10 #include "prlog.h"
michael@0 11 #include "prprf.h"
michael@0 12
michael@0 13 // We act as the main entry point for all the real lookups,
michael@0 14 // so note that those are not done to the actual HashStore.
michael@0 15 // The latter solely exists to store the data needed to handle
michael@0 16 // the updates from the protocol.
michael@0 17
michael@0 18 // This module has its own store, which stores the Completions,
michael@0 19 // mostly caching lookups that have happened over the net.
michael@0 20 // The prefixes are cached/checked by looking them up in the
michael@0 21 // PrefixSet.
michael@0 22
michael@0 23 // Data format for the ".cache" files:
michael@0 24 // uint32_t magic Identify the file type
michael@0 25 // uint32_t version Version identifier for file format
michael@0 26 // uint32_t numCompletions Amount of completions stored
michael@0 27 // 0...numCompletions 256-bit Completions
michael@0 28
michael@0 29 // Name of the lookupcomplete cache
michael@0 30 #define CACHE_SUFFIX ".cache"
michael@0 31
michael@0 32 // Name of the persistent PrefixSet storage
michael@0 33 #define PREFIXSET_SUFFIX ".pset"
michael@0 34
michael@0 35 // NSPR_LOG_MODULES=UrlClassifierDbService:5
michael@0 36 extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
michael@0 37 #if defined(PR_LOGGING)
michael@0 38 #define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
michael@0 39 #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
michael@0 40 #else
michael@0 41 #define LOG(args)
michael@0 42 #define LOG_ENABLED() (false)
michael@0 43 #endif
michael@0 44
michael@0 45 namespace mozilla {
michael@0 46 namespace safebrowsing {
michael@0 47
michael@0 48 const uint32_t LOOKUPCACHE_MAGIC = 0x1231af3e;
michael@0 49 const uint32_t CURRENT_VERSION = 2;
michael@0 50
michael@0 51 LookupCache::LookupCache(const nsACString& aTableName, nsIFile* aStoreDir)
michael@0 52 : mPrimed(false)
michael@0 53 , mTableName(aTableName)
michael@0 54 , mStoreDirectory(aStoreDir)
michael@0 55 {
michael@0 56 }
michael@0 57
michael@0 58 nsresult
michael@0 59 LookupCache::Init()
michael@0 60 {
michael@0 61 mPrefixSet = new nsUrlClassifierPrefixSet();
michael@0 62 nsresult rv = mPrefixSet->Init(mTableName);
michael@0 63 NS_ENSURE_SUCCESS(rv, rv);
michael@0 64
michael@0 65 return NS_OK;
michael@0 66 }
michael@0 67
michael@0 68 LookupCache::~LookupCache()
michael@0 69 {
michael@0 70 }
michael@0 71
michael@0 72 nsresult
michael@0 73 LookupCache::Open()
michael@0 74 {
michael@0 75 nsCOMPtr<nsIFile> storeFile;
michael@0 76
michael@0 77 nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
michael@0 78 NS_ENSURE_SUCCESS(rv, rv);
michael@0 79
michael@0 80 rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
michael@0 81 NS_ENSURE_SUCCESS(rv, rv);
michael@0 82
michael@0 83 nsCOMPtr<nsIInputStream> inputStream;
michael@0 84 rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), storeFile,
michael@0 85 PR_RDONLY | nsIFile::OS_READAHEAD);
michael@0 86
michael@0 87 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
michael@0 88 Reset();
michael@0 89 return rv;
michael@0 90 }
michael@0 91
michael@0 92 if (rv == NS_ERROR_FILE_NOT_FOUND) {
michael@0 93 // Simply lacking a .cache file is a recoverable error,
michael@0 94 // as unlike the .pset/.sbstore files it is a pure cache.
michael@0 95 // Just create a new empty one.
michael@0 96 ClearCompleteCache();
michael@0 97 } else {
michael@0 98 // Read in the .cache file
michael@0 99 rv = ReadHeader(inputStream);
michael@0 100 NS_ENSURE_SUCCESS(rv, rv);
michael@0 101 LOG(("ReadCompletions"));
michael@0 102 rv = ReadCompletions(inputStream);
michael@0 103 NS_ENSURE_SUCCESS(rv, rv);
michael@0 104
michael@0 105 rv = inputStream->Close();
michael@0 106 NS_ENSURE_SUCCESS(rv, rv);
michael@0 107 }
michael@0 108
michael@0 109 LOG(("Loading PrefixSet"));
michael@0 110 rv = LoadPrefixSet();
michael@0 111 NS_ENSURE_SUCCESS(rv, rv);
michael@0 112
michael@0 113 return NS_OK;
michael@0 114 }
michael@0 115
michael@0 116 nsresult
michael@0 117 LookupCache::UpdateDirHandle(nsIFile* aStoreDirectory)
michael@0 118 {
michael@0 119 return aStoreDirectory->Clone(getter_AddRefs(mStoreDirectory));
michael@0 120 }
michael@0 121
michael@0 122 nsresult
michael@0 123 LookupCache::Reset()
michael@0 124 {
michael@0 125 LOG(("LookupCache resetting"));
michael@0 126
michael@0 127 nsCOMPtr<nsIFile> storeFile;
michael@0 128 nsCOMPtr<nsIFile> prefixsetFile;
michael@0 129 nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
michael@0 130 NS_ENSURE_SUCCESS(rv, rv);
michael@0 131 rv = mStoreDirectory->Clone(getter_AddRefs(prefixsetFile));
michael@0 132 NS_ENSURE_SUCCESS(rv, rv);
michael@0 133
michael@0 134 rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
michael@0 135 NS_ENSURE_SUCCESS(rv, rv);
michael@0 136 rv = prefixsetFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX));
michael@0 137 NS_ENSURE_SUCCESS(rv, rv);
michael@0 138
michael@0 139 rv = storeFile->Remove(false);
michael@0 140 NS_ENSURE_SUCCESS(rv, rv);
michael@0 141 rv = prefixsetFile->Remove(false);
michael@0 142 NS_ENSURE_SUCCESS(rv, rv);
michael@0 143
michael@0 144 ClearAll();
michael@0 145
michael@0 146 return NS_OK;
michael@0 147 }
michael@0 148
michael@0 149
michael@0 150 nsresult
michael@0 151 LookupCache::Build(AddPrefixArray& aAddPrefixes,
michael@0 152 AddCompleteArray& aAddCompletes)
michael@0 153 {
michael@0 154 Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_COMPLETIONS,
michael@0 155 static_cast<uint32_t>(aAddCompletes.Length()));
michael@0 156
michael@0 157 mCompletions.Clear();
michael@0 158 mCompletions.SetCapacity(aAddCompletes.Length());
michael@0 159 for (uint32_t i = 0; i < aAddCompletes.Length(); i++) {
michael@0 160 mCompletions.AppendElement(aAddCompletes[i].CompleteHash());
michael@0 161 }
michael@0 162 aAddCompletes.Clear();
michael@0 163 mCompletions.Sort();
michael@0 164
michael@0 165 Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_PREFIXES,
michael@0 166 static_cast<uint32_t>(aAddPrefixes.Length()));
michael@0 167
michael@0 168 nsresult rv = ConstructPrefixSet(aAddPrefixes);
michael@0 169 NS_ENSURE_SUCCESS(rv, rv);
michael@0 170 mPrimed = true;
michael@0 171
michael@0 172 return NS_OK;
michael@0 173 }
michael@0 174
michael@0 175 #if defined(DEBUG) && defined(PR_LOGGING)
michael@0 176 void
michael@0 177 LookupCache::Dump()
michael@0 178 {
michael@0 179 if (!LOG_ENABLED())
michael@0 180 return;
michael@0 181
michael@0 182 for (uint32_t i = 0; i < mCompletions.Length(); i++) {
michael@0 183 nsAutoCString str;
michael@0 184 mCompletions[i].ToString(str);
michael@0 185 LOG(("Completion: %s", str.get()));
michael@0 186 }
michael@0 187 }
michael@0 188 #endif
michael@0 189
michael@0 190 nsresult
michael@0 191 LookupCache::Has(const Completion& aCompletion,
michael@0 192 bool* aHas, bool* aComplete)
michael@0 193 {
michael@0 194 *aHas = *aComplete = false;
michael@0 195
michael@0 196 uint32_t prefix = aCompletion.ToUint32();
michael@0 197
michael@0 198 bool found;
michael@0 199 nsresult rv = mPrefixSet->Contains(prefix, &found);
michael@0 200 NS_ENSURE_SUCCESS(rv, rv);
michael@0 201
michael@0 202 LOG(("Probe in %s: %X, found %d", mTableName.get(), prefix, found));
michael@0 203
michael@0 204 if (found) {
michael@0 205 *aHas = true;
michael@0 206 }
michael@0 207
michael@0 208 if (mCompletions.BinaryIndexOf(aCompletion) != nsTArray<Completion>::NoIndex) {
michael@0 209 LOG(("Complete in %s", mTableName.get()));
michael@0 210 *aComplete = true;
michael@0 211 *aHas = true;
michael@0 212 }
michael@0 213
michael@0 214 return NS_OK;
michael@0 215 }
michael@0 216
michael@0 217 nsresult
michael@0 218 LookupCache::WriteFile()
michael@0 219 {
michael@0 220 nsCOMPtr<nsIFile> storeFile;
michael@0 221 nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
michael@0 222 NS_ENSURE_SUCCESS(rv, rv);
michael@0 223 rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
michael@0 224 NS_ENSURE_SUCCESS(rv, rv);
michael@0 225
michael@0 226 nsCOMPtr<nsIOutputStream> out;
michael@0 227 rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out), storeFile,
michael@0 228 PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
michael@0 229 NS_ENSURE_SUCCESS(rv, rv);
michael@0 230
michael@0 231 UpdateHeader();
michael@0 232 LOG(("Writing %d completions", mHeader.numCompletions));
michael@0 233
michael@0 234 uint32_t written;
michael@0 235 rv = out->Write(reinterpret_cast<char*>(&mHeader), sizeof(mHeader), &written);
michael@0 236 NS_ENSURE_SUCCESS(rv, rv);
michael@0 237
michael@0 238 rv = WriteTArray(out, mCompletions);
michael@0 239 NS_ENSURE_SUCCESS(rv, rv);
michael@0 240
michael@0 241 nsCOMPtr<nsISafeOutputStream> safeOut = do_QueryInterface(out);
michael@0 242 rv = safeOut->Finish();
michael@0 243 NS_ENSURE_SUCCESS(rv, rv);
michael@0 244
michael@0 245 rv = EnsureSizeConsistent();
michael@0 246 NS_ENSURE_SUCCESS(rv, rv);
michael@0 247
michael@0 248 nsCOMPtr<nsIFile> psFile;
michael@0 249 rv = mStoreDirectory->Clone(getter_AddRefs(psFile));
michael@0 250 NS_ENSURE_SUCCESS(rv, rv);
michael@0 251
michael@0 252 rv = psFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX));
michael@0 253 NS_ENSURE_SUCCESS(rv, rv);
michael@0 254
michael@0 255 rv = mPrefixSet->StoreToFile(psFile);
michael@0 256 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store the prefixset");
michael@0 257
michael@0 258 return NS_OK;
michael@0 259 }
michael@0 260
michael@0 261 void
michael@0 262 LookupCache::ClearAll()
michael@0 263 {
michael@0 264 ClearCompleteCache();
michael@0 265 mPrefixSet->SetPrefixes(nullptr, 0);
michael@0 266 mPrimed = false;
michael@0 267 }
michael@0 268
michael@0 269 void
michael@0 270 LookupCache::ClearCompleteCache()
michael@0 271 {
michael@0 272 mCompletions.Clear();
michael@0 273 UpdateHeader();
michael@0 274 }
michael@0 275
michael@0 276 void
michael@0 277 LookupCache::UpdateHeader()
michael@0 278 {
michael@0 279 mHeader.magic = LOOKUPCACHE_MAGIC;
michael@0 280 mHeader.version = CURRENT_VERSION;
michael@0 281 mHeader.numCompletions = mCompletions.Length();
michael@0 282 }
michael@0 283
michael@0 284 nsresult
michael@0 285 LookupCache::EnsureSizeConsistent()
michael@0 286 {
michael@0 287 nsCOMPtr<nsIFile> storeFile;
michael@0 288 nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
michael@0 289 NS_ENSURE_SUCCESS(rv, rv);
michael@0 290 rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
michael@0 291 NS_ENSURE_SUCCESS(rv, rv);
michael@0 292
michael@0 293 int64_t fileSize;
michael@0 294 rv = storeFile->GetFileSize(&fileSize);
michael@0 295 NS_ENSURE_SUCCESS(rv, rv);
michael@0 296
michael@0 297 if (fileSize < 0) {
michael@0 298 return NS_ERROR_FAILURE;
michael@0 299 }
michael@0 300
michael@0 301 int64_t expectedSize = sizeof(mHeader)
michael@0 302 + mHeader.numCompletions*sizeof(Completion);
michael@0 303 if (expectedSize != fileSize) {
michael@0 304 NS_WARNING("File length does not match. Probably corrupted.");
michael@0 305 Reset();
michael@0 306 return NS_ERROR_FILE_CORRUPTED;
michael@0 307 }
michael@0 308
michael@0 309 return NS_OK;
michael@0 310 }
michael@0 311
michael@0 312 nsresult
michael@0 313 LookupCache::ReadHeader(nsIInputStream* aInputStream)
michael@0 314 {
michael@0 315 if (!aInputStream) {
michael@0 316 ClearCompleteCache();
michael@0 317 return NS_OK;
michael@0 318 }
michael@0 319
michael@0 320 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aInputStream);
michael@0 321 nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
michael@0 322 NS_ENSURE_SUCCESS(rv, rv);
michael@0 323
michael@0 324 void *buffer = &mHeader;
michael@0 325 rv = NS_ReadInputStreamToBuffer(aInputStream,
michael@0 326 &buffer,
michael@0 327 sizeof(Header));
michael@0 328 NS_ENSURE_SUCCESS(rv, rv);
michael@0 329
michael@0 330 if (mHeader.magic != LOOKUPCACHE_MAGIC || mHeader.version != CURRENT_VERSION) {
michael@0 331 NS_WARNING("Unexpected header data in the store.");
michael@0 332 Reset();
michael@0 333 return NS_ERROR_FILE_CORRUPTED;
michael@0 334 }
michael@0 335 LOG(("%d completions present", mHeader.numCompletions));
michael@0 336
michael@0 337 rv = EnsureSizeConsistent();
michael@0 338 NS_ENSURE_SUCCESS(rv, rv);
michael@0 339
michael@0 340 return NS_OK;
michael@0 341 }
michael@0 342
michael@0 343 nsresult
michael@0 344 LookupCache::ReadCompletions(nsIInputStream* aInputStream)
michael@0 345 {
michael@0 346 if (!mHeader.numCompletions) {
michael@0 347 mCompletions.Clear();
michael@0 348 return NS_OK;
michael@0 349 }
michael@0 350
michael@0 351 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aInputStream);
michael@0 352 nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, sizeof(Header));
michael@0 353 NS_ENSURE_SUCCESS(rv, rv);
michael@0 354
michael@0 355 rv = ReadTArray(aInputStream, &mCompletions, mHeader.numCompletions);
michael@0 356 NS_ENSURE_SUCCESS(rv, rv);
michael@0 357
michael@0 358 LOG(("Read %d completions", mCompletions.Length()));
michael@0 359
michael@0 360 return NS_OK;
michael@0 361 }
michael@0 362
michael@0 363 /* static */ bool
michael@0 364 LookupCache::IsCanonicalizedIP(const nsACString& aHost)
michael@0 365 {
michael@0 366 // The canonicalization process will have left IP addresses in dotted
michael@0 367 // decimal with no surprises.
michael@0 368 uint32_t i1, i2, i3, i4;
michael@0 369 char c;
michael@0 370 if (PR_sscanf(PromiseFlatCString(aHost).get(), "%u.%u.%u.%u%c",
michael@0 371 &i1, &i2, &i3, &i4, &c) == 4) {
michael@0 372 return (i1 <= 0xFF && i2 <= 0xFF && i3 <= 0xFF && i4 <= 0xFF);
michael@0 373 }
michael@0 374
michael@0 375 return false;
michael@0 376 }
michael@0 377
michael@0 378 /* static */ nsresult
michael@0 379 LookupCache::GetKey(const nsACString& aSpec,
michael@0 380 Completion* aHash,
michael@0 381 nsCOMPtr<nsICryptoHash>& aCryptoHash)
michael@0 382 {
michael@0 383 nsACString::const_iterator begin, end, iter;
michael@0 384 aSpec.BeginReading(begin);
michael@0 385 aSpec.EndReading(end);
michael@0 386
michael@0 387 iter = begin;
michael@0 388 if (!FindCharInReadable('/', iter, end)) {
michael@0 389 return NS_OK;
michael@0 390 }
michael@0 391
michael@0 392 const nsCSubstring& host = Substring(begin, iter);
michael@0 393
michael@0 394 if (IsCanonicalizedIP(host)) {
michael@0 395 nsAutoCString key;
michael@0 396 key.Assign(host);
michael@0 397 key.Append("/");
michael@0 398 return aHash->FromPlaintext(key, aCryptoHash);
michael@0 399 }
michael@0 400
michael@0 401 nsTArray<nsCString> hostComponents;
michael@0 402 ParseString(PromiseFlatCString(host), '.', hostComponents);
michael@0 403
michael@0 404 if (hostComponents.Length() < 2)
michael@0 405 return NS_ERROR_FAILURE;
michael@0 406
michael@0 407 int32_t last = int32_t(hostComponents.Length()) - 1;
michael@0 408 nsAutoCString lookupHost;
michael@0 409
michael@0 410 if (hostComponents.Length() > 2) {
michael@0 411 lookupHost.Append(hostComponents[last - 2]);
michael@0 412 lookupHost.Append(".");
michael@0 413 }
michael@0 414
michael@0 415 lookupHost.Append(hostComponents[last - 1]);
michael@0 416 lookupHost.Append(".");
michael@0 417 lookupHost.Append(hostComponents[last]);
michael@0 418 lookupHost.Append("/");
michael@0 419
michael@0 420 return aHash->FromPlaintext(lookupHost, aCryptoHash);
michael@0 421 }
michael@0 422
michael@0 423 /* static */ nsresult
michael@0 424 LookupCache::GetLookupFragments(const nsACString& aSpec,
michael@0 425 nsTArray<nsCString>* aFragments)
michael@0 426
michael@0 427 {
michael@0 428 aFragments->Clear();
michael@0 429
michael@0 430 nsACString::const_iterator begin, end, iter;
michael@0 431 aSpec.BeginReading(begin);
michael@0 432 aSpec.EndReading(end);
michael@0 433
michael@0 434 iter = begin;
michael@0 435 if (!FindCharInReadable('/', iter, end)) {
michael@0 436 return NS_OK;
michael@0 437 }
michael@0 438
michael@0 439 const nsCSubstring& host = Substring(begin, iter++);
michael@0 440 nsAutoCString path;
michael@0 441 path.Assign(Substring(iter, end));
michael@0 442
michael@0 443 /**
michael@0 444 * From the protocol doc:
michael@0 445 * For the hostname, the client will try at most 5 different strings. They
michael@0 446 * are:
michael@0 447 * a) The exact hostname of the url
michael@0 448 * b) The 4 hostnames formed by starting with the last 5 components and
michael@0 449 * successivly removing the leading component. The top-level component
michael@0 450 * can be skipped. This is not done if the hostname is a numerical IP.
michael@0 451 */
michael@0 452 nsTArray<nsCString> hosts;
michael@0 453 hosts.AppendElement(host);
michael@0 454
michael@0 455 if (!IsCanonicalizedIP(host)) {
michael@0 456 host.BeginReading(begin);
michael@0 457 host.EndReading(end);
michael@0 458 int numHostComponents = 0;
michael@0 459 while (RFindInReadable(NS_LITERAL_CSTRING("."), begin, end) &&
michael@0 460 numHostComponents < MAX_HOST_COMPONENTS) {
michael@0 461 // don't bother checking toplevel domains
michael@0 462 if (++numHostComponents >= 2) {
michael@0 463 host.EndReading(iter);
michael@0 464 hosts.AppendElement(Substring(end, iter));
michael@0 465 }
michael@0 466 end = begin;
michael@0 467 host.BeginReading(begin);
michael@0 468 }
michael@0 469 }
michael@0 470
michael@0 471 /**
michael@0 472 * From the protocol doc:
michael@0 473 * For the path, the client will also try at most 6 different strings.
michael@0 474 * They are:
michael@0 475 * a) the exact path of the url, including query parameters
michael@0 476 * b) the exact path of the url, without query parameters
michael@0 477 * c) the 4 paths formed by starting at the root (/) and
michael@0 478 * successively appending path components, including a trailing
michael@0 479 * slash. This behavior should only extend up to the next-to-last
michael@0 480 * path component, that is, a trailing slash should never be
michael@0 481 * appended that was not present in the original url.
michael@0 482 */
michael@0 483 nsTArray<nsCString> paths;
michael@0 484 nsAutoCString pathToAdd;
michael@0 485
michael@0 486 path.BeginReading(begin);
michael@0 487 path.EndReading(end);
michael@0 488 iter = begin;
michael@0 489 if (FindCharInReadable('?', iter, end)) {
michael@0 490 pathToAdd = Substring(begin, iter);
michael@0 491 paths.AppendElement(pathToAdd);
michael@0 492 end = iter;
michael@0 493 }
michael@0 494
michael@0 495 int numPathComponents = 1;
michael@0 496 iter = begin;
michael@0 497 while (FindCharInReadable('/', iter, end) &&
michael@0 498 numPathComponents < MAX_PATH_COMPONENTS) {
michael@0 499 iter++;
michael@0 500 pathToAdd.Assign(Substring(begin, iter));
michael@0 501 paths.AppendElement(pathToAdd);
michael@0 502 numPathComponents++;
michael@0 503 }
michael@0 504
michael@0 505 // If we haven't already done so, add the full path
michael@0 506 if (!pathToAdd.Equals(path)) {
michael@0 507 paths.AppendElement(path);
michael@0 508 }
michael@0 509 // Check an empty path (for whole-domain blacklist entries)
michael@0 510 paths.AppendElement(EmptyCString());
michael@0 511
michael@0 512 for (uint32_t hostIndex = 0; hostIndex < hosts.Length(); hostIndex++) {
michael@0 513 for (uint32_t pathIndex = 0; pathIndex < paths.Length(); pathIndex++) {
michael@0 514 nsCString key;
michael@0 515 key.Assign(hosts[hostIndex]);
michael@0 516 key.Append('/');
michael@0 517 key.Append(paths[pathIndex]);
michael@0 518 LOG(("Checking fragment %s", key.get()));
michael@0 519
michael@0 520 aFragments->AppendElement(key);
michael@0 521 }
michael@0 522 }
michael@0 523
michael@0 524 return NS_OK;
michael@0 525 }
michael@0 526
michael@0 527 /* static */ nsresult
michael@0 528 LookupCache::GetHostKeys(const nsACString& aSpec,
michael@0 529 nsTArray<nsCString>* aHostKeys)
michael@0 530 {
michael@0 531 nsACString::const_iterator begin, end, iter;
michael@0 532 aSpec.BeginReading(begin);
michael@0 533 aSpec.EndReading(end);
michael@0 534
michael@0 535 iter = begin;
michael@0 536 if (!FindCharInReadable('/', iter, end)) {
michael@0 537 return NS_OK;
michael@0 538 }
michael@0 539
michael@0 540 const nsCSubstring& host = Substring(begin, iter);
michael@0 541
michael@0 542 if (IsCanonicalizedIP(host)) {
michael@0 543 nsCString *key = aHostKeys->AppendElement();
michael@0 544 if (!key)
michael@0 545 return NS_ERROR_OUT_OF_MEMORY;
michael@0 546
michael@0 547 key->Assign(host);
michael@0 548 key->Append("/");
michael@0 549 return NS_OK;
michael@0 550 }
michael@0 551
michael@0 552 nsTArray<nsCString> hostComponents;
michael@0 553 ParseString(PromiseFlatCString(host), '.', hostComponents);
michael@0 554
michael@0 555 if (hostComponents.Length() < 2) {
michael@0 556 // no host or toplevel host, this won't match anything in the db
michael@0 557 return NS_OK;
michael@0 558 }
michael@0 559
michael@0 560 // First check with two domain components
michael@0 561 int32_t last = int32_t(hostComponents.Length()) - 1;
michael@0 562 nsCString *lookupHost = aHostKeys->AppendElement();
michael@0 563 if (!lookupHost)
michael@0 564 return NS_ERROR_OUT_OF_MEMORY;
michael@0 565
michael@0 566 lookupHost->Assign(hostComponents[last - 1]);
michael@0 567 lookupHost->Append(".");
michael@0 568 lookupHost->Append(hostComponents[last]);
michael@0 569 lookupHost->Append("/");
michael@0 570
michael@0 571 // Now check with three domain components
michael@0 572 if (hostComponents.Length() > 2) {
michael@0 573 nsCString *lookupHost2 = aHostKeys->AppendElement();
michael@0 574 if (!lookupHost2)
michael@0 575 return NS_ERROR_OUT_OF_MEMORY;
michael@0 576 lookupHost2->Assign(hostComponents[last - 2]);
michael@0 577 lookupHost2->Append(".");
michael@0 578 lookupHost2->Append(*lookupHost);
michael@0 579 }
michael@0 580
michael@0 581 return NS_OK;
michael@0 582 }
michael@0 583
michael@0 584 bool LookupCache::IsPrimed()
michael@0 585 {
michael@0 586 return mPrimed;
michael@0 587 }
michael@0 588
michael@0 589 #ifdef DEBUG
michael@0 590 template <class T>
michael@0 591 static void EnsureSorted(T* aArray)
michael@0 592 {
michael@0 593 typename T::elem_type* start = aArray->Elements();
michael@0 594 typename T::elem_type* end = aArray->Elements() + aArray->Length();
michael@0 595 typename T::elem_type* iter = start;
michael@0 596 typename T::elem_type* previous = start;
michael@0 597
michael@0 598 while (iter != end) {
michael@0 599 previous = iter;
michael@0 600 ++iter;
michael@0 601 if (iter != end) {
michael@0 602 MOZ_ASSERT(*previous <= *iter);
michael@0 603 }
michael@0 604 }
michael@0 605 return;
michael@0 606 }
michael@0 607 #endif
michael@0 608
michael@0 609 nsresult
michael@0 610 LookupCache::ConstructPrefixSet(AddPrefixArray& aAddPrefixes)
michael@0 611 {
michael@0 612 Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_CONSTRUCT_TIME> timer;
michael@0 613
michael@0 614 nsTArray<uint32_t> array;
michael@0 615 array.SetCapacity(aAddPrefixes.Length());
michael@0 616
michael@0 617 for (uint32_t i = 0; i < aAddPrefixes.Length(); i++) {
michael@0 618 array.AppendElement(aAddPrefixes[i].PrefixHash().ToUint32());
michael@0 619 }
michael@0 620 aAddPrefixes.Clear();
michael@0 621
michael@0 622 #ifdef DEBUG
michael@0 623 // PrefixSet requires sorted order
michael@0 624 EnsureSorted(&array);
michael@0 625 #endif
michael@0 626
michael@0 627 // construct new one, replace old entries
michael@0 628 nsresult rv = mPrefixSet->SetPrefixes(array.Elements(), array.Length());
michael@0 629 if (NS_FAILED(rv)) {
michael@0 630 goto error_bailout;
michael@0 631 }
michael@0 632
michael@0 633 #ifdef DEBUG
michael@0 634 uint32_t size;
michael@0 635 size = mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
michael@0 636 LOG(("SB tree done, size = %d bytes\n", size));
michael@0 637 #endif
michael@0 638
michael@0 639 mPrimed = true;
michael@0 640
michael@0 641 return NS_OK;
michael@0 642
michael@0 643 error_bailout:
michael@0 644 Telemetry::Accumulate(Telemetry::URLCLASSIFIER_PS_FAILURE, 1);
michael@0 645 return rv;
michael@0 646 }
michael@0 647
michael@0 648 nsresult
michael@0 649 LookupCache::LoadPrefixSet()
michael@0 650 {
michael@0 651 nsCOMPtr<nsIFile> psFile;
michael@0 652 nsresult rv = mStoreDirectory->Clone(getter_AddRefs(psFile));
michael@0 653 NS_ENSURE_SUCCESS(rv, rv);
michael@0 654
michael@0 655 rv = psFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX));
michael@0 656 NS_ENSURE_SUCCESS(rv, rv);
michael@0 657
michael@0 658 bool exists;
michael@0 659 rv = psFile->Exists(&exists);
michael@0 660 NS_ENSURE_SUCCESS(rv, rv);
michael@0 661
michael@0 662 if (exists) {
michael@0 663 LOG(("stored PrefixSet exists, loading from disk"));
michael@0 664 rv = mPrefixSet->LoadFromFile(psFile);
michael@0 665 if (NS_FAILED(rv)) {
michael@0 666 if (rv == NS_ERROR_FILE_CORRUPTED) {
michael@0 667 Reset();
michael@0 668 }
michael@0 669 return rv;
michael@0 670 }
michael@0 671 mPrimed = true;
michael@0 672 } else {
michael@0 673 LOG(("no (usable) stored PrefixSet found"));
michael@0 674 }
michael@0 675
michael@0 676 #ifdef DEBUG
michael@0 677 if (mPrimed) {
michael@0 678 uint32_t size = mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
michael@0 679 LOG(("SB tree done, size = %d bytes\n", size));
michael@0 680 }
michael@0 681 #endif
michael@0 682
michael@0 683 return NS_OK;
michael@0 684 }
michael@0 685
michael@0 686 nsresult
michael@0 687 LookupCache::GetPrefixes(nsTArray<uint32_t>* aAddPrefixes)
michael@0 688 {
michael@0 689 if (!mPrimed) {
michael@0 690 // This can happen if its a new table, so no error.
michael@0 691 LOG(("GetPrefixes from empty LookupCache"));
michael@0 692 return NS_OK;
michael@0 693 }
michael@0 694 uint32_t cnt;
michael@0 695 uint32_t *arr;
michael@0 696 nsresult rv = mPrefixSet->GetPrefixes(&cnt, &arr);
michael@0 697 NS_ENSURE_SUCCESS(rv, rv);
michael@0 698 if (!aAddPrefixes->AppendElements(arr, cnt))
michael@0 699 return NS_ERROR_FAILURE;
michael@0 700 nsMemory::Free(arr);
michael@0 701 return NS_OK;
michael@0 702 }
michael@0 703
michael@0 704
michael@0 705 }
michael@0 706 }

mercurial