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 : #ifndef LookupCache_h__
7 : #define LookupCache_h__
8 :
9 : #include "Entries.h"
10 : #include "nsString.h"
11 : #include "nsTArray.h"
12 : #include "nsCOMPtr.h"
13 : #include "nsIFile.h"
14 : #include "nsIFileStreams.h"
15 : #include "mozilla/RefPtr.h"
16 : #include "nsUrlClassifierPrefixSet.h"
17 : #include "VariableLengthPrefixSet.h"
18 : #include "mozilla/Logging.h"
19 : #include "mozilla/TypedEnumBits.h"
20 : #include "nsIUrlClassifierInfo.h"
21 :
22 : namespace mozilla {
23 : namespace safebrowsing {
24 :
25 : #define MAX_HOST_COMPONENTS 5
26 : #define MAX_PATH_COMPONENTS 4
27 :
28 0 : class LookupResult {
29 : public:
30 0 : LookupResult() : mNoise(false), mProtocolConfirmed(false),
31 : mPartialHashLength(0), mConfirmed(false),
32 0 : mProtocolV2(true) {}
33 :
34 : // The fragment that matched in the LookupCache
35 : union {
36 : Prefix fixedLengthPrefix;
37 : Completion complete;
38 : } hash;
39 :
40 0 : const Completion &CompleteHash() {
41 0 : MOZ_ASSERT(!mNoise);
42 0 : return hash.complete;
43 : }
44 :
45 0 : nsCString PartialHash() {
46 0 : MOZ_ASSERT(mPartialHashLength <= COMPLETE_SIZE);
47 0 : if (mNoise) {
48 : return nsCString(reinterpret_cast<char*>(hash.fixedLengthPrefix.buf),
49 0 : PREFIX_SIZE);
50 : } else {
51 : return nsCString(reinterpret_cast<char*>(hash.complete.buf),
52 0 : mPartialHashLength);
53 : }
54 : }
55 :
56 0 : nsCString PartialHashHex() {
57 0 : nsAutoCString hex;
58 0 : for (size_t i = 0; i < mPartialHashLength; i++) {
59 0 : hex.AppendPrintf("%.2X", hash.complete.buf[i]);
60 : }
61 0 : return hex;
62 : }
63 :
64 0 : bool Confirmed() const { return mConfirmed || mProtocolConfirmed; }
65 :
66 : // True if we have a complete match for this hash in the table.
67 0 : bool Complete() const { return mPartialHashLength == COMPLETE_SIZE; }
68 :
69 : // True if this is a noise entry, i.e. an extra entry
70 : // that is inserted to mask the true URL we are requesting.
71 : // Noise entries will not have a complete 256-bit hash as
72 : // they are fetched from the local 32-bit database and we
73 : // don't know the corresponding full URL.
74 : bool mNoise;
75 :
76 : bool mProtocolConfirmed;
77 :
78 : nsCString mTableName;
79 :
80 : uint32_t mPartialHashLength;
81 :
82 : // True as long as this lookup is complete and hasn't expired.
83 : bool mConfirmed;
84 :
85 : // TODO : Is this necessary
86 : bool mProtocolV2;
87 : };
88 :
89 : typedef nsTArray<LookupResult> LookupResultArray;
90 :
91 0 : class CacheResult {
92 : public:
93 : enum { V2, V4 };
94 :
95 : virtual int Ver() const = 0;
96 : virtual bool findCompletion(const Completion& aCompletion) const = 0;
97 :
98 0 : virtual ~CacheResult() {}
99 :
100 : template<typename T>
101 0 : static T* Cast(CacheResult* aThat) {
102 0 : return ((aThat && T::VER == aThat->Ver()) ?
103 0 : reinterpret_cast<T*>(aThat) : nullptr);
104 : }
105 :
106 : nsCString table;
107 : Prefix prefix;
108 : };
109 :
110 0 : class CacheResultV2 final : public CacheResult
111 : {
112 : public:
113 : static const int VER;
114 :
115 : // True when 'prefix' in CacheResult indicates a prefix that
116 : // cannot be completed.
117 : bool miss = false;
118 :
119 : // 'completion' and 'addChunk' are used when 'miss' field is false.
120 : Completion completion;
121 : uint32_t addChunk;
122 :
123 0 : bool operator==(const CacheResultV2& aOther) const {
124 0 : if (table != aOther.table ||
125 0 : prefix != aOther.prefix ||
126 0 : miss != aOther.miss) {
127 0 : return false;
128 : }
129 :
130 0 : if (miss) {
131 0 : return true;
132 : }
133 0 : return completion == aOther.completion && addChunk == aOther.addChunk;
134 : }
135 :
136 0 : bool findCompletion(const Completion& aCompletion) const override {
137 0 : return completion == aCompletion;
138 : }
139 :
140 0 : virtual int Ver() const override { return VER; }
141 : };
142 :
143 0 : class CacheResultV4 final : public CacheResult
144 : {
145 : public:
146 : static const int VER;
147 :
148 : CachedFullHashResponse response;
149 :
150 0 : bool operator==(const CacheResultV4& aOther) const {
151 0 : return table == aOther.table &&
152 0 : prefix == aOther.prefix &&
153 0 : response == aOther.response;
154 : }
155 :
156 0 : bool findCompletion(const Completion& aCompletion) const override {
157 : nsDependentCSubstring completion(
158 0 : reinterpret_cast<const char*>(aCompletion.buf), COMPLETE_SIZE);
159 0 : return response.fullHashes.Contains(completion);
160 : }
161 :
162 0 : virtual int Ver() const override { return VER; }
163 : };
164 :
165 : typedef nsTArray<UniquePtr<CacheResult>> CacheResultArray;
166 :
167 : class LookupCache {
168 : public:
169 : // Check for a canonicalized IP address.
170 : static bool IsCanonicalizedIP(const nsACString& aHost);
171 :
172 : // take a lookup string (www.hostname.com/path/to/resource.html) and
173 : // expand it into the set of fragments that should be searched for in an
174 : // entry
175 : static nsresult GetLookupFragments(const nsACString& aSpec,
176 : nsTArray<nsCString>* aFragments);
177 : // Similar to GetKey(), but if the domain contains three or more components,
178 : // two keys will be returned:
179 : // hostname.com/foo/bar -> [hostname.com]
180 : // mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com]
181 : // www.mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com]
182 : static nsresult GetHostKeys(const nsACString& aSpec,
183 : nsTArray<nsCString>* aHostKeys);
184 :
185 : LookupCache(const nsACString& aTableName,
186 : const nsACString& aProvider,
187 : nsIFile* aStoreFile);
188 6 : virtual ~LookupCache() {}
189 :
190 145 : const nsCString &TableName() const { return mTableName; }
191 :
192 : // The directory handle where we operate will
193 : // be moved away when a backup is made.
194 : nsresult UpdateRootDirHandle(nsIFile* aRootStoreDirectory);
195 :
196 : // Write data stored in lookup cache to disk.
197 : nsresult WriteFile();
198 :
199 12 : bool IsPrimed() const { return mPrimed; };
200 :
201 : // Called when update to clear expired entries.
202 : void InvalidateExpiredCacheEntries();
203 :
204 : // Copy fullhash cache from another LookupCache.
205 : void CopyFullHashCache(const LookupCache* aSource);
206 :
207 : // Clear fullhash cache from fullhash/gethash response.
208 : void ClearCache();
209 :
210 : // Check if completions can be found in cache.
211 : // Currently this is only used by testcase.
212 : bool IsInCache(uint32_t key) { return mFullHashCache.Get(key); };
213 :
214 : #if DEBUG
215 : void DumpCache();
216 : #endif
217 :
218 : void GetCacheInfo(nsIUrlClassifierCacheInfo** aCache);
219 :
220 : virtual nsresult Open();
221 : virtual nsresult Init() = 0;
222 : virtual nsresult ClearPrefixes() = 0;
223 : virtual nsresult Has(const Completion& aCompletion,
224 : bool* aHas,
225 : uint32_t* aMatchLength,
226 : bool* aConfirmed) = 0;
227 :
228 : virtual bool IsEmpty() = 0;
229 :
230 : virtual void ClearAll();
231 :
232 : template<typename T>
233 18 : static T* Cast(LookupCache* aThat) {
234 18 : return ((aThat && T::VER == aThat->Ver()) ? reinterpret_cast<T*>(aThat) : nullptr);
235 : }
236 :
237 : private:
238 : nsresult LoadPrefixSet();
239 :
240 : virtual nsresult StoreToFile(nsIFile* aFile) = 0;
241 : virtual nsresult LoadFromFile(nsIFile* aFile) = 0;
242 : virtual size_t SizeOfPrefixSet() = 0;
243 :
244 : virtual int Ver() const = 0;
245 :
246 : protected:
247 : // Check completions in positive cache and prefix in negative cache.
248 : // 'aHas' and 'aConfirmed' are output parameters.
249 : nsresult CheckCache(const Completion& aCompletion,
250 : bool* aHas,
251 : bool* aConfirmed);
252 :
253 : bool mPrimed;
254 : nsCString mTableName;
255 : nsCString mProvider;
256 : nsCOMPtr<nsIFile> mRootStoreDirectory;
257 : nsCOMPtr<nsIFile> mStoreDirectory;
258 :
259 : // For gtest to inspect private members.
260 : friend class PerProviderDirectoryTestUtils;
261 :
262 : // Cache stores fullhash response(V4)/gethash response(V2)
263 : FullHashResponseMap mFullHashCache;
264 : };
265 :
266 : class LookupCacheV2 final : public LookupCache
267 : {
268 : public:
269 13 : explicit LookupCacheV2(const nsACString& aTableName,
270 : const nsACString& aProvider,
271 : nsIFile* aStoreFile)
272 13 : : LookupCache(aTableName, aProvider, aStoreFile) {}
273 18 : ~LookupCacheV2() {}
274 :
275 : virtual nsresult Init() override;
276 : virtual nsresult Open() override;
277 : virtual void ClearAll() override;
278 : virtual nsresult Has(const Completion& aCompletion,
279 : bool* aHas,
280 : uint32_t* aMatchLength,
281 : bool* aConfirmed) override;
282 :
283 : virtual bool IsEmpty() override;
284 :
285 : nsresult Build(AddPrefixArray& aAddPrefixes,
286 : AddCompleteArray& aAddCompletes);
287 :
288 : nsresult GetPrefixes(FallibleTArray<uint32_t>& aAddPrefixes);
289 :
290 : // This will Clear() the passed arrays when done.
291 : // 'aExpirySec' is used by testcase to config an expired time.
292 : void AddGethashResultToCache(AddCompleteArray& aAddCompletes,
293 : MissPrefixArray& aMissPrefixes,
294 : int64_t aExpirySec = 0);
295 :
296 : #if DEBUG
297 : void DumpCompletions();
298 : #endif
299 :
300 : static const int VER;
301 :
302 : protected:
303 : nsresult ReadCompletions();
304 :
305 : virtual nsresult ClearPrefixes() override;
306 : virtual nsresult StoreToFile(nsIFile* aFile) override;
307 : virtual nsresult LoadFromFile(nsIFile* aFile) override;
308 : virtual size_t SizeOfPrefixSet() override;
309 :
310 : private:
311 18 : virtual int Ver() const override { return VER; }
312 :
313 : // Construct a Prefix Set with known prefixes.
314 : // This will Clear() aAddPrefixes when done.
315 : nsresult ConstructPrefixSet(AddPrefixArray& aAddPrefixes);
316 :
317 : // Full length hashes obtained in update request
318 : CompletionArray mUpdateCompletions;
319 :
320 : // Set of prefixes known to be in the database
321 : RefPtr<nsUrlClassifierPrefixSet> mPrefixSet;
322 : };
323 :
324 : } // namespace safebrowsing
325 : } // namespace mozilla
326 :
327 : #endif
|