//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef LookupCache_h__
#define LookupCache_h__
#include "Entries.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsCOMPtr.h"
#include "nsIFile.h"
#include "mozilla/RefPtr.h"
#include "nsUrlClassifierPrefixSet.h"
#include "VariableLengthPrefixSet.h"
#include "mozilla/Logging.h"
#include "mozilla/TypedEnumBits.h"
#include "nsIUrlClassifierInfo.h"
namespace mozilla {
namespace safebrowsing {
#define MAX_HOST_COMPONENTS 5
#define MAX_PATH_COMPONENTS 4
class LookupResult {
public:
LookupResult()
: mNoise(
false),
mProtocolConfirmed(
false),
mPartialHashLength(0),
mConfirmed(
false),
mProtocolV2(
true) {}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LookupResult);
// The fragment that matched in the LookupCache
union {
Prefix fixedLengthPrefix;
Completion complete;
} hash;
const Completion& CompleteHash()
const {
MOZ_ASSERT(!mNoise);
return hash.complete;
}
nsCString PartialHash()
const {
MOZ_ASSERT(mPartialHashLength <= COMPLETE_SIZE);
if (mNoise) {
return nsCString(
reinterpret_cast<
const char*>(hash.fixedLengthPrefix.buf),
PREFIX_SIZE);
}
else {
return nsCString(
reinterpret_cast<
const char*>(hash.complete.buf),
mPartialHashLength);
}
}
nsAutoCString PartialHashHex()
const {
nsAutoCString hex;
for (size_t i = 0; i < mPartialHashLength; i++) {
hex.AppendPrintf(
"%.2X", hash.complete.buf[i]);
}
return hex;
}
bool Confirmed()
const {
return mConfirmed || mProtocolConfirmed; }
// True if we have a complete match for this hash in the table.
bool Complete()
const {
return mPartialHashLength == COMPLETE_SIZE; }
// True if this is a noise entry, i.e. an extra entry
// that is inserted to mask the true URL we are requesting.
// Noise entries will not have a complete 256-bit hash as
// they are fetched from the local 32-bit database and we
// don't know the corresponding full URL.
bool mNoise;
bool mProtocolConfirmed;
nsCString mTableName;
uint32_t mPartialHashLength;
// True as long as this lookup is complete and hasn't expired.
bool mConfirmed;
bool mProtocolV2;
private:
~LookupResult() =
default;
};
typedef nsTArray<RefPtr<LookupResult>> LookupResultArray;
class CacheResult {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CacheResult);
enum { V2, V4 };
virtual int Ver()
const = 0;
virtual bool findCompletion(
const Completion& aCompletion)
const = 0;
template <
typename T>
static const T* Cast(
const CacheResult* aThat) {
return ((aThat && T::VER == aThat->Ver())
?
reinterpret_cast<
const T*>(aThat)
: nullptr);
}
nsCString table;
Prefix prefix;
protected:
virtual ~CacheResult() =
default;
};
class CacheResultV2 final :
public CacheResult {
public:
static const int VER;
// True when 'prefix' in CacheResult indicates a prefix that
// cannot be completed.
bool miss =
false;
// 'completion' and 'addChunk' are used when 'miss' field is false.
Completion completion;
uint32_t addChunk;
bool operator==(
const CacheResultV2& aOther)
const {
if (table != aOther.table || prefix != aOther.prefix ||
miss != aOther.miss) {
return false;
}
if (miss) {
return true;
}
return completion == aOther.completion && addChunk == aOther.addChunk;
}
bool findCompletion(
const Completion& aCompletion)
const override {
return completion == aCompletion;
}
virtual int Ver()
const override {
return VER; }
};
class CacheResultV4 final :
public CacheResult {
public:
static const int VER;
CachedFullHashResponse response;
bool operator==(
const CacheResultV4& aOther)
const {
return table == aOther.table && prefix == aOther.prefix &&
response == aOther.response;
}
bool findCompletion(
const Completion& aCompletion)
const override {
nsDependentCSubstring completion(
reinterpret_cast<
const char*>(aCompletion.buf), COMPLETE_SIZE);
return response.fullHashes.Contains(completion);
}
virtual int Ver()
const override {
return VER; }
};
typedef nsTArray<RefPtr<
const CacheResult>> ConstCacheResultArray;
class LookupCache {
public:
// Check for a canonicalized IP address.
static bool IsCanonicalizedIP(
const nsACString& aHost);
// take a lookup string (www.hostname.com/path/to/resource.html) and
// expand it into the set of fragments that should be searched for in an
// entry
static nsresult GetLookupFragments(
const nsACString& aSpec,
nsTArray<nsCString>* aFragments);
static nsresult GetLookupEntitylistFragments(
const nsACString& aSpec,
nsTArray<nsCString>* aFragments);
LookupCache(
const nsACString& aTableName,
const nsACString& aProvider,
nsCOMPtr<nsIFile>& aStoreFile);
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LookupCache);
const nsCString& TableName()
const {
return mTableName; }
// The directory handle where we operate will
// be moved away when a backup is made.
nsresult UpdateRootDirHandle(nsCOMPtr<nsIFile>& aRootStoreDirectory);
// Write data stored in lookup cache to disk.
nsresult WriteFile();
bool IsPrimed()
const {
return mPrimed; };
// Called when update to clear expired entries.
void InvalidateExpiredCacheEntries();
// Copy fullhash cache from another LookupCache.
void CopyFullHashCache(
const LookupCache* aSource);
// Clear fullhash cache from fullhash/gethash response.
void ClearCache();
// Check if completions can be found in cache.
// Currently this is only used by testcase.
bool IsInCache(uint32_t key)
const {
return mFullHashCache.Get(key); };
uint32_t PrefixLength()
const {
return mVLPrefixSet->FixedLengthPrefixLength();
}
#if DEBUG
void DumpCache()
const;
#endif
void GetCacheInfo(nsIUrlClassifierCacheInfo** aCache)
const;
nsresult VerifyCRC32(nsCOMPtr<nsIInputStream>& aIn);
virtual nsresult Open();
virtual nsresult Init();
;
virtual nsresult ClearPrefixes();
virtual nsresult Has(
const Completion& aCompletion,
bool* aHas,
uint32_t* aMatchLength,
bool* aConfirmed) = 0;
// Prefix files file header
struct Header {
uint32_t magic;
uint32_t version;
};
virtual nsresult StoreToFile(nsCOMPtr<nsIFile>& aFile);
virtual nsresult LoadFromFile(nsCOMPtr<nsIFile>& aFile);
virtual bool IsEmpty()
const;
virtual void ClearAll();
virtual nsresult LoadMozEntries() = 0;
template <
typename T>
static T* Cast(LookupCache* aThat) {
return ((aThat && T::VER == aThat->Ver()) ?
reinterpret_cast<T*>(aThat)
: nullptr);
}
template <
typename T>
static const T* Cast(
const LookupCache* aThat) {
return ((aThat && T::VER == aThat->Ver())
?
reinterpret_cast<
const T*>(aThat)
: nullptr);
}
private:
nsresult LoadPrefixSet();
virtual size_t SizeOfPrefixSet()
const;
virtual nsCString GetPrefixSetSuffix()
const = 0;
virtual int Ver()
const = 0;
virtual void GetHeader(Header& aHeader) = 0;
virtual nsresult SanityCheck(
const Header& aHeader) = 0;
virtual nsresult LoadLegacyFile() = 0;
virtual nsresult ClearLegacyFile() = 0;
protected:
virtual ~LookupCache() =
default;
// Buffer size for file read/write
static const uint32_t MAX_BUFFER_SIZE;
// Check completions in positive cache and prefix in negative cache.
// 'aHas' and 'aConfirmed' are output parameters.
nsresult CheckCache(
const Completion& aCompletion,
bool* aHas,
bool* aConfirmed);
bool mPrimed;
// true when the PrefixSet has been loaded (or constructed)
const nsCString mTableName;
const nsCString mProvider;
nsCOMPtr<nsIFile> mRootStoreDirectory;
nsCOMPtr<nsIFile> mStoreDirectory;
// For gtest to inspect private members.
friend class PerProviderDirectoryTestUtils;
// Cache stores fullhash response(V4)/gethash response(V2)
FullHashResponseMap mFullHashCache;
RefPtr<VariableLengthPrefixSet> mVLPrefixSet;
template <
typename T>
static nsresult WriteValue(nsIOutputStream* aOutputStream,
const T& aValue);
template <
typename T>
static nsresult ReadValue(nsIInputStream* aInputStream, T& aValue);
};
typedef nsTArray<RefPtr<LookupCache>> LookupCacheArray;
class LookupCacheV2 final :
public LookupCache {
public:
explicit LookupCacheV2(
const nsACString& aTableName,
const nsACString& aProvider,
nsCOMPtr<nsIFile>& aStoreFile)
: LookupCache(aTableName, aProvider, aStoreFile) {}
virtual nsresult Has(
const Completion& aCompletion,
bool* aHas,
uint32_t* aMatchLength,
bool* aConfirmed) override;
nsresult Build(AddPrefixArray& aAddPrefixes, AddCompleteArray& aAddCompletes
);
nsresult GetPrefixes(FallibleTArray<uint32_t>& aAddPrefixes);
nsresult GetPrefixes(FallibleTArray<uint32_t>& aAddPrefixes,
FallibleTArray<nsCString>& aAddCompletes);
nsresult GetPrefixByIndex(uint32_t aIndex, uint32_t* aOutPrefix) const;
// This will Clear() the passed arrays when done.
// 'aExpirySec' is used by testcase to config an expired time.
void AddGethashResultToCache(const AddCompleteArray& aAddCompletes,
const MissPrefixArray& aMissPrefixes,
int64_t aExpirySec = 0);
virtual nsresult LoadMozEntries() override;
static const int VER;
static const uint32_t VLPSET_MAGIC;
static const uint32_t VLPSET_VERSION;
protected:
virtual nsCString GetPrefixSetSuffix() const override;
private:
~LookupCacheV2() = default;
virtual int Ver() const override { return VER; }
virtual void GetHeader(Header& aHeader) override;
virtual nsresult SanityCheck(const Header& aHeader) override;
virtual nsresult LoadLegacyFile() override;
virtual nsresult ClearLegacyFile() override;
};
} // namespace safebrowsing
} // namespace mozilla
#endif