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