LCOV - code coverage report
Current view: top level - toolkit/components/url-classifier - LookupCache.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 175 359 48.7 %
Date: 2017-07-14 16:53:18 Functions: 21 33 63.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "LookupCache.h"
       7             : #include "HashStore.h"
       8             : #include "nsISeekableStream.h"
       9             : #include "mozilla/ArrayUtils.h"
      10             : #include "mozilla/Telemetry.h"
      11             : #include "mozilla/Logging.h"
      12             : #include "nsNetUtil.h"
      13             : #include "prprf.h"
      14             : #include "Classifier.h"
      15             : #include "nsUrlClassifierInfo.h"
      16             : 
      17             : // We act as the main entry point for all the real lookups,
      18             : // so note that those are not done to the actual HashStore.
      19             : // The latter solely exists to store the data needed to handle
      20             : // the updates from the protocol.
      21             : 
      22             : // This module provides a front for PrefixSet, mUpdateCompletions,
      23             : // and mGetHashCache, which together contain everything needed to
      24             : // provide a classification as long as the data is up to date.
      25             : 
      26             : // PrefixSet stores and provides lookups for 4-byte prefixes.
      27             : // mUpdateCompletions contains 32-byte completions which were
      28             : // contained in updates. They are retrieved from HashStore/.sbtore
      29             : // on startup.
      30             : // mGetHashCache contains 32-byte completions which were
      31             : // returned from the gethash server. They are not serialized,
      32             : // only cached until the next update.
      33             : 
      34             : // Name of the persistent PrefixSet storage
      35             : #define PREFIXSET_SUFFIX  ".pset"
      36             : 
      37             : #define V2_CACHE_DURATION_SEC (15 * 60)
      38             : 
      39             : // MOZ_LOG=UrlClassifierDbService:5
      40             : extern mozilla::LazyLogModule gUrlClassifierDbServiceLog;
      41             : #define LOG(args) MOZ_LOG(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug, args)
      42             : #define LOG_ENABLED() MOZ_LOG_TEST(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug)
      43             : 
      44             : namespace mozilla {
      45             : namespace safebrowsing {
      46             : 
      47             : const int CacheResultV2::VER = CacheResult::V2;
      48             : const int CacheResultV4::VER = CacheResult::V4;
      49             : 
      50             : const int LookupCacheV2::VER = 2;
      51             : 
      52             : static
      53           0 : void CStringToHexString(const nsACString& aIn, nsACString& aOut)
      54             : {
      55             :   static const char* const lut = "0123456789ABCDEF";
      56             : 
      57           0 :   size_t len = aIn.Length();
      58           0 :   MOZ_ASSERT(len <= COMPLETE_SIZE);
      59             : 
      60           0 :   aOut.SetCapacity(2 * len);
      61           0 :   for (size_t i = 0; i < aIn.Length(); ++i) {
      62           0 :     const char c = static_cast<const char>(aIn[i]);
      63           0 :     aOut.Append(lut[(c >> 4) & 0x0F]);
      64           0 :     aOut.Append(lut[c & 15]);
      65             :   }
      66           0 : }
      67             : 
      68          13 : LookupCache::LookupCache(const nsACString& aTableName,
      69             :                          const nsACString& aProvider,
      70          13 :                          nsIFile* aRootStoreDir)
      71             :   : mPrimed(false)
      72             :   , mTableName(aTableName)
      73             :   , mProvider(aProvider)
      74          13 :   , mRootStoreDirectory(aRootStoreDir)
      75             : {
      76          13 :   UpdateRootDirHandle(mRootStoreDirectory);
      77          13 : }
      78             : 
      79             : nsresult
      80          13 : LookupCache::Open()
      81             : {
      82          13 :   LOG(("Loading PrefixSet"));
      83          13 :   nsresult rv = LoadPrefixSet();
      84          13 :   NS_ENSURE_SUCCESS(rv, rv);
      85             : 
      86          13 :   return NS_OK;
      87             : }
      88             : 
      89             : nsresult
      90          19 : LookupCache::UpdateRootDirHandle(nsIFile* aNewRootStoreDirectory)
      91             : {
      92             :   nsresult rv;
      93             : 
      94          19 :   if (aNewRootStoreDirectory != mRootStoreDirectory) {
      95           6 :     rv = aNewRootStoreDirectory->Clone(getter_AddRefs(mRootStoreDirectory));
      96           6 :     NS_ENSURE_SUCCESS(rv, rv);
      97             :   }
      98             : 
      99          19 :   rv = Classifier::GetPrivateStoreDirectory(mRootStoreDirectory,
     100             :                                             mTableName,
     101             :                                             mProvider,
     102          38 :                                             getter_AddRefs(mStoreDirectory));
     103             : 
     104          19 :   if (NS_FAILED(rv)) {
     105           0 :     LOG(("Failed to get private store directory for %s", mTableName.get()));
     106           0 :     mStoreDirectory = mRootStoreDirectory;
     107             :   }
     108             : 
     109          19 :   if (LOG_ENABLED()) {
     110           0 :     nsString path;
     111           0 :     mStoreDirectory->GetPath(path);
     112           0 :     LOG(("Private store directory for %s is %s", mTableName.get(),
     113             :                                                  NS_ConvertUTF16toUTF8(path).get()));
     114             :   }
     115             : 
     116          19 :   return rv;
     117             : }
     118             : 
     119             : nsresult
     120           6 : LookupCache::WriteFile()
     121             : {
     122           6 :   if (nsUrlClassifierDBService::ShutdownHasStarted()) {
     123           0 :     return NS_ERROR_ABORT;
     124             :   }
     125             : 
     126          12 :   nsCOMPtr<nsIFile> psFile;
     127           6 :   nsresult rv = mStoreDirectory->Clone(getter_AddRefs(psFile));
     128           6 :   NS_ENSURE_SUCCESS(rv, rv);
     129             : 
     130           6 :   rv = psFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX));
     131           6 :   NS_ENSURE_SUCCESS(rv, rv);
     132             : 
     133           6 :   rv = StoreToFile(psFile);
     134           6 :   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "failed to store the prefixset");
     135             : 
     136           6 :   return NS_OK;
     137             : }
     138             : 
     139             : nsresult
     140           0 : LookupCache::CheckCache(const Completion& aCompletion,
     141             :                         bool* aHas,
     142             :                         bool* aConfirmed)
     143             : {
     144             :   // Shouldn't call this function if prefix is not in the database.
     145           0 :   MOZ_ASSERT(*aHas);
     146             : 
     147           0 :   *aConfirmed = false;
     148             : 
     149           0 :   uint32_t prefix = aCompletion.ToUint32();
     150             : 
     151           0 :   CachedFullHashResponse* fullHashResponse = mFullHashCache.Get(prefix);
     152           0 :   if (!fullHashResponse) {
     153           0 :     return NS_OK;
     154             :   }
     155             : 
     156           0 :   int64_t nowSec = PR_Now() / PR_USEC_PER_SEC;
     157             :   int64_t expiryTimeSec;
     158             : 
     159           0 :   FullHashExpiryCache& fullHashes = fullHashResponse->fullHashes;
     160             :   nsDependentCSubstring completion(
     161           0 :     reinterpret_cast<const char*>(aCompletion.buf), COMPLETE_SIZE);
     162             : 
     163             :   // Check if we can find the fullhash in positive cache
     164           0 :   if (fullHashes.Get(completion, &expiryTimeSec)) {
     165           0 :     if (nowSec <= expiryTimeSec) {
     166             :       // Url is NOT safe.
     167           0 :       *aConfirmed = true;
     168           0 :       LOG(("Found a valid fullhash in the positive cache"));
     169             :     } else {
     170             :       // Trigger a gethash request in this case(aConfirmed is false).
     171           0 :       LOG(("Found an expired fullhash in the positive cache"));
     172             : 
     173             :       // Remove fullhash entry from the cache when the negative cache
     174             :       // is also expired because whether or not the fullhash is cached
     175             :       // locally, we will need to consult the server next time we
     176             :       // lookup this hash. We may as well remove it from our cache.
     177           0 :       if (fullHashResponse->negativeCacheExpirySec < expiryTimeSec) {
     178           0 :         fullHashes.Remove(completion);
     179           0 :         if (fullHashes.Count() == 0 &&
     180           0 :             fullHashResponse->negativeCacheExpirySec < nowSec) {
     181           0 :           mFullHashCache.Remove(prefix);
     182             :         }
     183             :       }
     184             :     }
     185           0 :     return NS_OK;
     186             :   }
     187             : 
     188             :   // Check negative cache.
     189           0 :   if (fullHashResponse->negativeCacheExpirySec >= nowSec) {
     190             :     // Url is safe.
     191           0 :     LOG(("Found a valid prefix in the negative cache"));
     192           0 :     *aHas = false;
     193             :   } else {
     194           0 :     LOG(("Found an expired prefix in the negative cache"));
     195           0 :     if (fullHashes.Count() == 0) {
     196           0 :       mFullHashCache.Remove(prefix);
     197             :     }
     198             :   }
     199             : 
     200           0 :   return NS_OK;
     201             : }
     202             : 
     203             : // This function remove cache entries whose negative cache time is expired.
     204             : // It is possible that a cache entry whose positive cache time is not yet
     205             : // expired but still being removed after calling this API. Right now we call
     206             : // this on every update.
     207             : void
     208           6 : LookupCache::InvalidateExpiredCacheEntries()
     209             : {
     210           6 :   int64_t nowSec = PR_Now() / PR_USEC_PER_SEC;
     211             : 
     212           6 :   for (auto iter = mFullHashCache.Iter(); !iter.Done(); iter.Next()) {
     213           0 :     CachedFullHashResponse* response = iter.Data();
     214           0 :     if (response->negativeCacheExpirySec < nowSec) {
     215           0 :       iter.Remove();
     216             :     }
     217             :   }
     218           6 : }
     219             : 
     220             : void
     221           6 : LookupCache::CopyFullHashCache(const LookupCache* aSource)
     222             : {
     223           6 :   if (!aSource) {
     224           0 :     return;
     225             :   }
     226             : 
     227           6 :   CopyClassHashTable<FullHashResponseMap>(aSource->mFullHashCache,
     228           6 :                                           mFullHashCache);
     229             : }
     230             : 
     231             : void
     232           0 : LookupCache::ClearCache()
     233             : {
     234           0 :   mFullHashCache.Clear();
     235           0 : }
     236             : 
     237             : void
     238           0 : LookupCache::ClearAll()
     239             : {
     240           0 :   ClearCache();
     241           0 :   ClearPrefixes();
     242           0 :   mPrimed = false;
     243           0 : }
     244             : 
     245             : void
     246           0 : LookupCache::GetCacheInfo(nsIUrlClassifierCacheInfo** aCache)
     247             : {
     248           0 :   MOZ_ASSERT(aCache);
     249             : 
     250           0 :   RefPtr<nsUrlClassifierCacheInfo> info = new nsUrlClassifierCacheInfo;
     251           0 :   info->table = mTableName;
     252             : 
     253           0 :   for (auto iter = mFullHashCache.ConstIter(); !iter.Done(); iter.Next()) {
     254           0 :     RefPtr<nsUrlClassifierCacheEntry> entry = new nsUrlClassifierCacheEntry;
     255             : 
     256             :     // Set prefix of the cache entry.
     257           0 :     nsAutoCString prefix(reinterpret_cast<const char*>(&iter.Key()), PREFIX_SIZE);
     258           0 :     CStringToHexString(prefix, entry->prefix);
     259             : 
     260             :     // Set expiry of the cache entry.
     261           0 :     CachedFullHashResponse* response = iter.Data();
     262           0 :     entry->expirySec = response->negativeCacheExpirySec;
     263             : 
     264             :     // Set positive cache.
     265           0 :     FullHashExpiryCache& fullHashes = response->fullHashes;
     266           0 :     for (auto iter2 = fullHashes.ConstIter(); !iter2.Done(); iter2.Next()) {
     267             :       RefPtr<nsUrlClassifierPositiveCacheEntry> match =
     268           0 :         new nsUrlClassifierPositiveCacheEntry;
     269             : 
     270             :       // Set fullhash of positive cache entry.
     271           0 :       CStringToHexString(iter2.Key(), match->fullhash);
     272             : 
     273             :       // Set expiry of positive cache entry.
     274           0 :       match->expirySec = iter2.Data();
     275             : 
     276           0 :       entry->matches.AppendElement(
     277           0 :         static_cast<nsIUrlClassifierPositiveCacheEntry*>(match));
     278             :     }
     279             : 
     280           0 :     info->entries.AppendElement(static_cast<nsIUrlClassifierCacheEntry*>(entry));
     281             :   }
     282             : 
     283           0 :   NS_ADDREF(*aCache = info);
     284             : 
     285           0 :   return;
     286             : }
     287             : 
     288             : /* static */ bool
     289           1 : LookupCache::IsCanonicalizedIP(const nsACString& aHost)
     290             : {
     291             :   // The canonicalization process will have left IP addresses in dotted
     292             :   // decimal with no surprises.
     293             :   uint32_t i1, i2, i3, i4;
     294             :   char c;
     295           1 :   if (PR_sscanf(PromiseFlatCString(aHost).get(), "%u.%u.%u.%u%c",
     296             :                 &i1, &i2, &i3, &i4, &c) == 4) {
     297           0 :     return (i1 <= 0xFF && i2 <= 0xFF && i3 <= 0xFF && i4 <= 0xFF);
     298             :   }
     299             : 
     300           1 :   return false;
     301             : }
     302             : 
     303             : /* static */ nsresult
     304           1 : LookupCache::GetLookupFragments(const nsACString& aSpec,
     305             :                                 nsTArray<nsCString>* aFragments)
     306             : 
     307             : {
     308           1 :   aFragments->Clear();
     309             : 
     310           1 :   nsACString::const_iterator begin, end, iter;
     311           1 :   aSpec.BeginReading(begin);
     312           1 :   aSpec.EndReading(end);
     313             : 
     314           1 :   iter = begin;
     315           1 :   if (!FindCharInReadable('/', iter, end)) {
     316           0 :     return NS_OK;
     317             :   }
     318             : 
     319           2 :   const nsACString& host = Substring(begin, iter++);
     320           2 :   nsAutoCString path;
     321           1 :   path.Assign(Substring(iter, end));
     322             : 
     323             :   /**
     324             :    * From the protocol doc:
     325             :    * For the hostname, the client will try at most 5 different strings.  They
     326             :    * are:
     327             :    * a) The exact hostname of the url
     328             :    * b) The 4 hostnames formed by starting with the last 5 components and
     329             :    *    successivly removing the leading component.  The top-level component
     330             :    *    can be skipped. This is not done if the hostname is a numerical IP.
     331             :    */
     332           2 :   nsTArray<nsCString> hosts;
     333           1 :   hosts.AppendElement(host);
     334             : 
     335           1 :   if (!IsCanonicalizedIP(host)) {
     336           1 :     host.BeginReading(begin);
     337           1 :     host.EndReading(end);
     338           1 :     int numHostComponents = 0;
     339           1 :     while (RFindInReadable(NS_LITERAL_CSTRING("."), begin, end) &&
     340             :            numHostComponents < MAX_HOST_COMPONENTS) {
     341             :       // don't bother checking toplevel domains
     342           0 :       if (++numHostComponents >= 2) {
     343           0 :         host.EndReading(iter);
     344           0 :         hosts.AppendElement(Substring(end, iter));
     345             :       }
     346           0 :       end = begin;
     347           0 :       host.BeginReading(begin);
     348             :     }
     349             :   }
     350             : 
     351             :   /**
     352             :    * From the protocol doc:
     353             :    * For the path, the client will also try at most 6 different strings.
     354             :    * They are:
     355             :    * a) the exact path of the url, including query parameters
     356             :    * b) the exact path of the url, without query parameters
     357             :    * c) the 4 paths formed by starting at the root (/) and
     358             :    *    successively appending path components, including a trailing
     359             :    *    slash.  This behavior should only extend up to the next-to-last
     360             :    *    path component, that is, a trailing slash should never be
     361             :    *    appended that was not present in the original url.
     362             :    */
     363           2 :   nsTArray<nsCString> paths;
     364           2 :   nsAutoCString pathToAdd;
     365             : 
     366           1 :   path.BeginReading(begin);
     367           1 :   path.EndReading(end);
     368           1 :   iter = begin;
     369           1 :   if (FindCharInReadable('?', iter, end)) {
     370           0 :     pathToAdd = Substring(begin, iter);
     371           0 :     paths.AppendElement(pathToAdd);
     372           0 :     end = iter;
     373             :   }
     374             : 
     375           1 :   int numPathComponents = 1;
     376           1 :   iter = begin;
     377           1 :   while (FindCharInReadable('/', iter, end) &&
     378             :          numPathComponents < MAX_PATH_COMPONENTS) {
     379           0 :     iter++;
     380           0 :     pathToAdd.Assign(Substring(begin, iter));
     381           0 :     paths.AppendElement(pathToAdd);
     382           0 :     numPathComponents++;
     383             :   }
     384             : 
     385             :   // If we haven't already done so, add the full path
     386           1 :   if (!pathToAdd.Equals(path)) {
     387           1 :     paths.AppendElement(path);
     388             :   }
     389             :   // Check an empty path (for whole-domain blacklist entries)
     390           1 :   paths.AppendElement(EmptyCString());
     391             : 
     392           2 :   for (uint32_t hostIndex = 0; hostIndex < hosts.Length(); hostIndex++) {
     393           3 :     for (uint32_t pathIndex = 0; pathIndex < paths.Length(); pathIndex++) {
     394           4 :       nsCString key;
     395           2 :       key.Assign(hosts[hostIndex]);
     396           2 :       key.Append('/');
     397           2 :       key.Append(paths[pathIndex]);
     398           2 :       LOG(("Checking fragment %s", key.get()));
     399             : 
     400           2 :       aFragments->AppendElement(key);
     401             :     }
     402             :   }
     403             : 
     404           1 :   return NS_OK;
     405             : }
     406             : 
     407             : /* static */ nsresult
     408           0 : LookupCache::GetHostKeys(const nsACString& aSpec,
     409             :                          nsTArray<nsCString>* aHostKeys)
     410             : {
     411           0 :   nsACString::const_iterator begin, end, iter;
     412           0 :   aSpec.BeginReading(begin);
     413           0 :   aSpec.EndReading(end);
     414             : 
     415           0 :   iter = begin;
     416           0 :   if (!FindCharInReadable('/', iter, end)) {
     417           0 :     return NS_OK;
     418             :   }
     419             : 
     420           0 :   const nsACString& host = Substring(begin, iter);
     421             : 
     422           0 :   if (IsCanonicalizedIP(host)) {
     423           0 :     nsCString *key = aHostKeys->AppendElement();
     424           0 :     if (!key)
     425           0 :       return NS_ERROR_OUT_OF_MEMORY;
     426             : 
     427           0 :     key->Assign(host);
     428           0 :     key->Append("/");
     429           0 :     return NS_OK;
     430             :   }
     431             : 
     432           0 :   nsTArray<nsCString> hostComponents;
     433           0 :   ParseString(PromiseFlatCString(host), '.', hostComponents);
     434             : 
     435           0 :   if (hostComponents.Length() < 2) {
     436             :     // no host or toplevel host, this won't match anything in the db
     437           0 :     return NS_OK;
     438             :   }
     439             : 
     440             :   // First check with two domain components
     441           0 :   int32_t last = int32_t(hostComponents.Length()) - 1;
     442           0 :   nsCString *lookupHost = aHostKeys->AppendElement();
     443           0 :   if (!lookupHost)
     444           0 :     return NS_ERROR_OUT_OF_MEMORY;
     445             : 
     446           0 :   lookupHost->Assign(hostComponents[last - 1]);
     447           0 :   lookupHost->Append(".");
     448           0 :   lookupHost->Append(hostComponents[last]);
     449           0 :   lookupHost->Append("/");
     450             : 
     451             :   // Now check with three domain components
     452           0 :   if (hostComponents.Length() > 2) {
     453           0 :     nsCString *lookupHost2 = aHostKeys->AppendElement();
     454           0 :     if (!lookupHost2)
     455           0 :       return NS_ERROR_OUT_OF_MEMORY;
     456           0 :     lookupHost2->Assign(hostComponents[last - 2]);
     457           0 :     lookupHost2->Append(".");
     458           0 :     lookupHost2->Append(*lookupHost);
     459             :   }
     460             : 
     461           0 :   return NS_OK;
     462             : }
     463             : 
     464             : nsresult
     465          13 : LookupCache::LoadPrefixSet()
     466             : {
     467          26 :   nsCOMPtr<nsIFile> psFile;
     468          13 :   nsresult rv = mStoreDirectory->Clone(getter_AddRefs(psFile));
     469          13 :   NS_ENSURE_SUCCESS(rv, rv);
     470             : 
     471          13 :   rv = psFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX));
     472          13 :   NS_ENSURE_SUCCESS(rv, rv);
     473             : 
     474             :   bool exists;
     475          13 :   rv = psFile->Exists(&exists);
     476          13 :   NS_ENSURE_SUCCESS(rv, rv);
     477             : 
     478          13 :   if (exists) {
     479          12 :     LOG(("stored PrefixSet exists, loading from disk"));
     480          12 :     rv = LoadFromFile(psFile);
     481          12 :     if (NS_FAILED(rv)) {
     482           0 :       return rv;
     483             :     }
     484          12 :     mPrimed = true;
     485             :   } else {
     486           1 :     LOG(("no (usable) stored PrefixSet found"));
     487             :   }
     488             : 
     489             : #ifdef DEBUG
     490          13 :   if (mPrimed) {
     491          12 :     uint32_t size = SizeOfPrefixSet();
     492          12 :     LOG(("SB tree done, size = %d bytes\n", size));
     493             :   }
     494             : #endif
     495             : 
     496          13 :   return NS_OK;
     497             : }
     498             : 
     499             : #if defined(DEBUG)
     500             : static
     501           0 : nsCString GetFormattedTimeString(int64_t aCurTimeSec)
     502             : {
     503             :   PRExplodedTime pret;
     504           0 :   PR_ExplodeTime(aCurTimeSec * PR_USEC_PER_SEC, PR_GMTParameters, &pret);
     505             : 
     506           0 :   return nsPrintfCString(
     507             :          "%04d-%02d-%02d %02d:%02d:%02d UTC",
     508           0 :          pret.tm_year, pret.tm_month + 1, pret.tm_mday,
     509           0 :          pret.tm_hour, pret.tm_min, pret.tm_sec);
     510             : }
     511             : 
     512             : void
     513           0 : LookupCache::DumpCache()
     514             : {
     515           0 :   if (!LOG_ENABLED()) {
     516           0 :     return;
     517             :   }
     518             : 
     519           0 :   for (auto iter = mFullHashCache.ConstIter(); !iter.Done(); iter.Next()) {
     520           0 :     CachedFullHashResponse* response = iter.Data();
     521             : 
     522           0 :     nsAutoCString prefix;
     523             :     CStringToHexString(
     524           0 :       nsCString(reinterpret_cast<const char*>(&iter.Key()), PREFIX_SIZE), prefix);
     525           0 :     LOG(("Cache prefix(%s): %s, Expiry: %s", mTableName.get(), prefix.get(),
     526             :           GetFormattedTimeString(response->negativeCacheExpirySec).get()));
     527             : 
     528           0 :     FullHashExpiryCache& fullHashes = response->fullHashes;
     529           0 :     for (auto iter2 = fullHashes.ConstIter(); !iter2.Done(); iter2.Next()) {
     530           0 :       nsAutoCString fullhash;
     531           0 :       CStringToHexString(iter2.Key(), fullhash);
     532           0 :       LOG(("  - %s, Expiry: %s", fullhash.get(),
     533             :             GetFormattedTimeString(iter2.Data()).get()));
     534             :     }
     535             :   }
     536             : }
     537             : #endif
     538             : 
     539             : nsresult
     540          13 : LookupCacheV2::Init()
     541             : {
     542          13 :   mPrefixSet = new nsUrlClassifierPrefixSet();
     543          13 :   nsresult rv = mPrefixSet->Init(mTableName);
     544          13 :   NS_ENSURE_SUCCESS(rv, rv);
     545             : 
     546          13 :   return NS_OK;
     547             : }
     548             : 
     549             : nsresult
     550          13 : LookupCacheV2::Open()
     551             : {
     552          13 :   nsresult rv = LookupCache::Open();
     553          13 :   NS_ENSURE_SUCCESS(rv, rv);
     554             : 
     555          13 :   LOG(("Reading Completions"));
     556          13 :   rv = ReadCompletions();
     557          13 :   NS_ENSURE_SUCCESS(rv, rv);
     558             : 
     559          13 :   return NS_OK;
     560             : }
     561             : 
     562             : void
     563           0 : LookupCacheV2::ClearAll()
     564             : {
     565           0 :   LookupCache::ClearAll();
     566           0 :   mUpdateCompletions.Clear();
     567           0 : }
     568             : 
     569             : nsresult
     570           4 : LookupCacheV2::Has(const Completion& aCompletion,
     571             :                    bool* aHas,
     572             :                    uint32_t* aMatchLength,
     573             :                    bool* aConfirmed)
     574             : {
     575           4 :   *aHas = *aConfirmed = false;
     576           4 :   *aMatchLength = 0;
     577             : 
     578           4 :   uint32_t prefix = aCompletion.ToUint32();
     579             : 
     580             :   bool found;
     581           4 :   nsresult rv = mPrefixSet->Contains(prefix, &found);
     582           4 :   NS_ENSURE_SUCCESS(rv, rv);
     583             : 
     584           4 :   if (found) {
     585           0 :     *aHas = true;
     586           0 :     *aMatchLength = PREFIX_SIZE;
     587           4 :   } else if (mUpdateCompletions.BinaryIndexOf(aCompletion) !=
     588             :              nsTArray<Completion>::NoIndex) {
     589             :     // Completions is found in database, confirm the result
     590           0 :     *aHas = true;
     591           0 :     *aMatchLength = COMPLETE_SIZE;
     592           0 :     *aConfirmed = true;
     593             :   }
     594             : 
     595           4 :   if (*aHas && !(*aConfirmed)) {
     596           0 :     rv = CheckCache(aCompletion, aHas, aConfirmed);
     597             :   }
     598             : 
     599           4 :   LOG(("Probe in %s: %X, has %d, confirmed %d",
     600             :        mTableName.get(), prefix, *aHas, *aConfirmed));
     601             : 
     602           4 :   return rv;
     603             : }
     604             : 
     605             : bool
     606           0 : LookupCacheV2::IsEmpty()
     607             : {
     608             :   bool isEmpty;
     609           0 :   mPrefixSet->IsEmpty(&isEmpty);
     610           0 :   return isEmpty;
     611             : }
     612             : 
     613             : nsresult
     614           6 : LookupCacheV2::Build(AddPrefixArray& aAddPrefixes,
     615             :                      AddCompleteArray& aAddCompletes)
     616             : {
     617           6 :   Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_COMPLETIONS,
     618          12 :                         static_cast<uint32_t>(aAddCompletes.Length()));
     619             : 
     620           6 :   mUpdateCompletions.Clear();
     621           6 :   mUpdateCompletions.SetCapacity(aAddCompletes.Length());
     622          13 :   for (uint32_t i = 0; i < aAddCompletes.Length(); i++) {
     623           7 :     mUpdateCompletions.AppendElement(aAddCompletes[i].CompleteHash());
     624             :   }
     625           6 :   aAddCompletes.Clear();
     626           6 :   mUpdateCompletions.Sort();
     627             : 
     628           6 :   Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_PREFIXES,
     629          12 :                         static_cast<uint32_t>(aAddPrefixes.Length()));
     630             : 
     631           6 :   nsresult rv = ConstructPrefixSet(aAddPrefixes);
     632           6 :   NS_ENSURE_SUCCESS(rv, rv);
     633           6 :   mPrimed = true;
     634             : 
     635           6 :   return NS_OK;
     636             : }
     637             : 
     638             : nsresult
     639           6 : LookupCacheV2::GetPrefixes(FallibleTArray<uint32_t>& aAddPrefixes)
     640             : {
     641           6 :   if (!mPrimed) {
     642             :     // This can happen if its a new table, so no error.
     643           0 :     LOG(("GetPrefixes from empty LookupCache"));
     644           0 :     return NS_OK;
     645             :   }
     646           6 :   return mPrefixSet->GetPrefixesNative(aAddPrefixes);
     647             : }
     648             : 
     649             : void
     650           0 : LookupCacheV2::AddGethashResultToCache(AddCompleteArray& aAddCompletes,
     651             :                                        MissPrefixArray& aMissPrefixes,
     652             :                                        int64_t aExpirySec)
     653             : {
     654           0 :   int64_t defaultExpirySec = PR_Now() / PR_USEC_PER_SEC + V2_CACHE_DURATION_SEC;
     655           0 :   if (aExpirySec != 0) {
     656           0 :     defaultExpirySec = aExpirySec;
     657             :   }
     658             : 
     659           0 :   for (const AddComplete& add : aAddCompletes) {
     660             :     nsDependentCSubstring fullhash(
     661           0 :       reinterpret_cast<const char*>(add.CompleteHash().buf), COMPLETE_SIZE);
     662             : 
     663             :     CachedFullHashResponse* response =
     664           0 :       mFullHashCache.LookupOrAdd(add.ToUint32());
     665           0 :     response->negativeCacheExpirySec = defaultExpirySec;
     666             : 
     667           0 :     FullHashExpiryCache& fullHashes = response->fullHashes;
     668           0 :     fullHashes.Put(fullhash, defaultExpirySec);
     669             :   }
     670             : 
     671           0 :   for (const Prefix& prefix : aMissPrefixes) {
     672             :     CachedFullHashResponse* response =
     673           0 :       mFullHashCache.LookupOrAdd(prefix.ToUint32());
     674             : 
     675           0 :     response->negativeCacheExpirySec = defaultExpirySec;
     676             :   }
     677           0 : }
     678             : 
     679             : nsresult
     680          13 : LookupCacheV2::ReadCompletions()
     681             : {
     682          26 :   HashStore store(mTableName, mProvider, mRootStoreDirectory);
     683             : 
     684          13 :   nsresult rv = store.Open();
     685          13 :   NS_ENSURE_SUCCESS(rv, rv);
     686             : 
     687          13 :   mUpdateCompletions.Clear();
     688             : 
     689          13 :   const AddCompleteArray& addComplete = store.AddCompletes();
     690          27 :   for (uint32_t i = 0; i < addComplete.Length(); i++) {
     691          14 :     mUpdateCompletions.AppendElement(addComplete[i].complete);
     692             :   }
     693             : 
     694          13 :   return NS_OK;
     695             : }
     696             : 
     697             : nsresult
     698           0 : LookupCacheV2::ClearPrefixes()
     699             : {
     700           0 :   return mPrefixSet->SetPrefixes(nullptr, 0);
     701             : }
     702             : 
     703             : nsresult
     704           6 : LookupCacheV2::StoreToFile(nsIFile* aFile)
     705             : {
     706           6 :   return mPrefixSet->StoreToFile(aFile);
     707             : }
     708             : 
     709             : nsresult
     710          12 : LookupCacheV2::LoadFromFile(nsIFile* aFile)
     711             : {
     712          12 :   return mPrefixSet->LoadFromFile(aFile);
     713             : }
     714             : 
     715             : size_t
     716          12 : LookupCacheV2::SizeOfPrefixSet()
     717             : {
     718          12 :   return mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
     719             : }
     720             : 
     721             : #ifdef DEBUG
     722             : template <class T>
     723           6 : static void EnsureSorted(T* aArray)
     724             : {
     725           6 :   typename T::elem_type* start = aArray->Elements();
     726           6 :   typename T::elem_type* end = aArray->Elements() + aArray->Length();
     727           6 :   typename T::elem_type* iter = start;
     728           6 :   typename T::elem_type* previous = start;
     729             : 
     730           6 :   while (iter != end) {
     731           0 :     previous = iter;
     732           0 :     ++iter;
     733           0 :     if (iter != end) {
     734           0 :       MOZ_ASSERT(*previous <= *iter);
     735             :     }
     736             :   }
     737           6 :   return;
     738             : }
     739             : #endif
     740             : 
     741             : nsresult
     742           6 : LookupCacheV2::ConstructPrefixSet(AddPrefixArray& aAddPrefixes)
     743             : {
     744          12 :   Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_CONSTRUCT_TIME> timer;
     745             : 
     746          12 :   nsTArray<uint32_t> array;
     747           6 :   if (!array.SetCapacity(aAddPrefixes.Length(), fallible)) {
     748           0 :     return NS_ERROR_OUT_OF_MEMORY;
     749             :   }
     750             : 
     751           6 :   for (uint32_t i = 0; i < aAddPrefixes.Length(); i++) {
     752           0 :     array.AppendElement(aAddPrefixes[i].PrefixHash().ToUint32());
     753             :   }
     754           6 :   aAddPrefixes.Clear();
     755             : 
     756             : #ifdef DEBUG
     757             :   // PrefixSet requires sorted order
     758           6 :   EnsureSorted(&array);
     759             : #endif
     760             : 
     761             :   // construct new one, replace old entries
     762           6 :   nsresult rv = mPrefixSet->SetPrefixes(array.Elements(), array.Length());
     763           6 :   NS_ENSURE_SUCCESS(rv, rv);
     764             : 
     765             : #ifdef DEBUG
     766             :   uint32_t size;
     767           6 :   size = mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
     768           6 :   LOG(("SB tree done, size = %d bytes\n", size));
     769             : #endif
     770             : 
     771           6 :   mPrimed = true;
     772             : 
     773           6 :   return NS_OK;
     774             : }
     775             : 
     776             : #if defined(DEBUG)
     777             : void
     778           6 : LookupCacheV2::DumpCompletions()
     779             : {
     780           6 :   if (!LOG_ENABLED())
     781           6 :     return;
     782             : 
     783           0 :   for (uint32_t i = 0; i < mUpdateCompletions.Length(); i++) {
     784           0 :     nsAutoCString str;
     785           0 :     mUpdateCompletions[i].ToHexString(str);
     786           0 :     LOG(("Update: %s", str.get()));
     787             :   }
     788             : }
     789             : #endif
     790             : 
     791             : } // namespace safebrowsing
     792             : } // namespace mozilla

Generated by: LCOV version 1.13