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 +}