mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Backout 44a0dc4fb9ff (Bug 673470) for Talos regression.
This commit is contained in:
parent
01ef79496d
commit
99a251d9ae
@ -67,6 +67,7 @@ struct Histograms {
|
||||
|
||||
Histograms gHistograms[] = {
|
||||
SQLITE_TELEMETRY("places.sqlite", PLACES),
|
||||
SQLITE_TELEMETRY("urlclassifier3.sqlite", URLCLASSIFIER),
|
||||
SQLITE_TELEMETRY("cookies.sqlite", COOKIES),
|
||||
SQLITE_TELEMETRY("webappsstore.sqlite", WEBAPPS),
|
||||
SQLITE_TELEMETRY(NULL, OTHER)
|
||||
|
@ -163,9 +163,9 @@
|
||||
#define NS_TYPEAHEADFIND_CID \
|
||||
{ 0xe7f70966, 0x9a37, 0x48d7, { 0x8a, 0xeb, 0x35, 0x99, 0x8f, 0x31, 0x09, 0x0e} }
|
||||
|
||||
// {b21b0fa1-20d2-422a-b2cc-b289c9325811}
|
||||
#define NS_URLCLASSIFIERPREFIXSET_CID \
|
||||
{ 0xb21b0fa1, 0x20d2, 0x422a, { 0xb2, 0xcc, 0xb2, 0x89, 0xc9, 0x32, 0x58, 0x11} }
|
||||
// {15a892dd-cb0f-4a9f-a27f-8291d5e16653}
|
||||
#define NS_URLCLASSIFIERPREFIXSET_CID \
|
||||
{ 0x15a892dd, 0xcb0f, 0x4a9f, { 0xa2, 0x7f, 0x82, 0x91, 0xd5, 0xe1, 0x66, 0x53} }
|
||||
|
||||
// {5eb7c3c1-ec1f-4007-87cc-eefb37d68ce6}
|
||||
#define NS_URLCLASSIFIERDBSERVICE_CID \
|
||||
|
@ -225,6 +225,7 @@ HISTOGRAM(CHECK_JAVA_ENABLED, 1, 3000, 10, EXPONENTIAL, "Time spent checking if
|
||||
SQLITE_TIME_SPENT(OTHER_ ## NAME, DESC) \
|
||||
SQLITE_TIME_SPENT(PLACES_ ## NAME, DESC) \
|
||||
SQLITE_TIME_SPENT(COOKIES_ ## NAME, DESC) \
|
||||
SQLITE_TIME_SPENT(URLCLASSIFIER_ ## NAME, DESC) \
|
||||
SQLITE_TIME_SPENT(WEBAPPS_ ## NAME, DESC)
|
||||
|
||||
SQLITE_TIME_SPENT(OPEN, "Time spent on SQLite open() (ms)")
|
||||
@ -237,9 +238,11 @@ SQLITE_TIME_PER_FILE(SYNC, "Time spent on SQLite fsync() (ms)")
|
||||
HISTOGRAM(MOZ_SQLITE_OTHER_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)")
|
||||
HISTOGRAM(MOZ_SQLITE_PLACES_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)")
|
||||
HISTOGRAM(MOZ_SQLITE_COOKIES_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)")
|
||||
HISTOGRAM(MOZ_SQLITE_URLCLASSIFIER_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)")
|
||||
HISTOGRAM(MOZ_SQLITE_WEBAPPS_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)")
|
||||
HISTOGRAM(MOZ_SQLITE_PLACES_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)")
|
||||
HISTOGRAM(MOZ_SQLITE_COOKIES_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)")
|
||||
HISTOGRAM(MOZ_SQLITE_URLCLASSIFIER_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)")
|
||||
HISTOGRAM(MOZ_SQLITE_WEBAPPS_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)")
|
||||
HISTOGRAM(MOZ_SQLITE_OTHER_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)")
|
||||
HISTOGRAM(MOZ_STORAGE_ASYNC_REQUESTS_MS, 1, 32768, 20, EXPONENTIAL, "mozStorage async requests completion (ms)")
|
||||
@ -260,14 +263,10 @@ HISTOGRAM(NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE_INTERNAL_MAIN_THREAD, 1, 10000,
|
||||
* Url-Classifier telemetry
|
||||
*/
|
||||
#ifdef MOZ_URL_CLASSIFIER
|
||||
HISTOGRAM(URLCLASSIFIER_LOOKUP_TIME, 1, 500, 10, EXPONENTIAL, "Time spent per dbservice lookup (ms)")
|
||||
HISTOGRAM(URLCLASSIFIER_CL_CHECK_TIME, 1, 500, 10, EXPONENTIAL, "Time spent per classifier lookup (ms)")
|
||||
HISTOGRAM(URLCLASSIFIER_CL_UPDATE_TIME, 20, 15000, 15, EXPONENTIAL, "Time spent per classifier update (ms)")
|
||||
HISTOGRAM(URLCLASSIFIER_PS_FILELOAD_TIME, 1, 1000, 10, EXPONENTIAL, "Time spent loading PrefixSet from file (ms)")
|
||||
HISTOGRAM(URLCLASSIFIER_PS_FALLOCATE_TIME, 1, 1000, 10, EXPONENTIAL, "Time spent fallocating PrefixSet (ms)")
|
||||
HISTOGRAM(URLCLASSIFIER_PS_CONSTRUCT_TIME, 1, 5000, 15, EXPONENTIAL, "Time spent constructing PrefixSet from DB (ms)")
|
||||
HISTOGRAM(URLCLASSIFIER_LC_PREFIXES, 1, 1500000, 15, LINEAR, "Size of the prefix cache in entries")
|
||||
HISTOGRAM(URLCLASSIFIER_LC_COMPLETIONS, 1, 200, 10, EXPONENTIAL, "Size of the completion cache in entries")
|
||||
HISTOGRAM(URLCLASSIFIER_PS_LOOKUP_TIME, 1, 500, 10, EXPONENTIAL, "Time spent per PrefixSet lookup (ms)")
|
||||
HISTOGRAM_BOOLEAN(URLCLASSIFIER_PS_OOM, "Did UrlClassifier run out of memory during PrefixSet construction?")
|
||||
#endif
|
||||
|
||||
|
@ -1,136 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Url Classifier code
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dave Camp <dcamp@mozilla.com>
|
||||
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "ChunkSet.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
nsresult
|
||||
ChunkSet::Serialize(nsACString& aChunkStr)
|
||||
{
|
||||
aChunkStr.Truncate();
|
||||
|
||||
PRUint32 i = 0;
|
||||
while (i < mChunks.Length()) {
|
||||
if (i != 0) {
|
||||
aChunkStr.Append(',');
|
||||
}
|
||||
aChunkStr.AppendInt((PRInt32)mChunks[i]);
|
||||
|
||||
PRUint32 first = i;
|
||||
PRUint32 last = first;
|
||||
i++;
|
||||
while (i < mChunks.Length() && (mChunks[i] == mChunks[i - 1] + 1 || mChunks[i] == mChunks[i - 1])) {
|
||||
last = i++;
|
||||
}
|
||||
|
||||
if (last != first) {
|
||||
aChunkStr.Append('-');
|
||||
aChunkStr.AppendInt((PRInt32)mChunks[last]);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ChunkSet::Set(PRUint32 aChunk)
|
||||
{
|
||||
PRUint32 idx = mChunks.BinaryIndexOf(aChunk);
|
||||
if (idx == nsTArray<uint32>::NoIndex) {
|
||||
mChunks.InsertElementSorted(aChunk);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ChunkSet::Unset(PRUint32 aChunk)
|
||||
{
|
||||
mChunks.RemoveElementSorted(aChunk);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
ChunkSet::Has(PRUint32 aChunk) const
|
||||
{
|
||||
return mChunks.BinaryIndexOf(aChunk) != nsTArray<uint32>::NoIndex;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ChunkSet::Merge(const ChunkSet& aOther)
|
||||
{
|
||||
const uint32 *dupIter = aOther.mChunks.Elements();
|
||||
const uint32 *end = aOther.mChunks.Elements() + aOther.mChunks.Length();
|
||||
|
||||
for (const uint32 *iter = dupIter; iter != end; iter++) {
|
||||
nsresult rv = Set(*iter);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ChunkSet::Remove(const ChunkSet& aOther)
|
||||
{
|
||||
uint32 *addIter = mChunks.Elements();
|
||||
uint32 *end = mChunks.Elements() + mChunks.Length();
|
||||
|
||||
for (uint32 *iter = addIter; iter != end; iter++) {
|
||||
if (!aOther.Has(*iter)) {
|
||||
*addIter = *iter;
|
||||
addIter++;
|
||||
}
|
||||
}
|
||||
|
||||
mChunks.SetLength(addIter - mChunks.Elements());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ChunkSet::Clear()
|
||||
{
|
||||
mChunks.Clear();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Url Classifier code
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dave Camp <dcamp@mozilla.com>
|
||||
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef ChunkSet_h__
|
||||
#define ChunkSet_h__
|
||||
|
||||
|
||||
#include "Entries.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
/**
|
||||
* Store the chunks as an array of uint32.
|
||||
* XXX: We should optimize this further to compress the
|
||||
* many consecutive numbers.
|
||||
*/
|
||||
class ChunkSet {
|
||||
public:
|
||||
ChunkSet() {}
|
||||
~ChunkSet() {}
|
||||
|
||||
nsresult Serialize(nsACString& aStr);
|
||||
nsresult Set(PRUint32 aChunk);
|
||||
nsresult Unset(PRUint32 aChunk);
|
||||
void Clear();
|
||||
nsresult Merge(const ChunkSet& aOther);
|
||||
nsresult Remove(const ChunkSet& aOther);
|
||||
|
||||
bool Has(PRUint32 chunk) const;
|
||||
|
||||
uint32 Length() const { return mChunks.Length(); }
|
||||
|
||||
nsresult Write(nsIOutputStream* aOut) {
|
||||
return WriteTArray(aOut, mChunks);
|
||||
}
|
||||
|
||||
nsresult Read(nsIInputStream* aIn, PRUint32 aNumElements) {
|
||||
return ReadTArray(aIn, &mChunks, aNumElements);
|
||||
}
|
||||
|
||||
uint32 *Begin() { return mChunks.Elements(); }
|
||||
uint32 *End() { return mChunks.Elements() + mChunks.Length(); }
|
||||
|
||||
private:
|
||||
nsTArray<uint32> mChunks;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,653 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Url Classifier code
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dave Camp <dcamp@mozilla.com>
|
||||
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "Classifier.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "nsIRandomGenerator.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "prlog.h"
|
||||
|
||||
// NSPR_LOG_MODULES=UrlClassifierDbService:5
|
||||
extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
|
||||
#if defined(PR_LOGGING)
|
||||
#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
|
||||
#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#define LOG_ENABLED() (PR_FALSE)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
Classifier::Classifier()
|
||||
: mFreshTime(45 * 60)
|
||||
{
|
||||
}
|
||||
|
||||
Classifier::~Classifier()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a unique 32-bit key for this user, which we will
|
||||
* use to rehash all prefixes. This ensures that different users
|
||||
* will get hash collisions on different prefixes, which in turn
|
||||
* avoids that "unlucky" URLs get mysterious slowdowns, and that
|
||||
* the servers get spammed if any such URL should get slashdotted.
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=669407#c10
|
||||
*/
|
||||
nsresult
|
||||
Classifier::InitKey()
|
||||
{
|
||||
nsCOMPtr<nsIFile> storeFile;
|
||||
nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = storeFile->AppendNative(NS_LITERAL_CSTRING("classifier.hashkey"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool exists;
|
||||
rv = storeFile->Exists(&exists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!exists) {
|
||||
// generate and store key
|
||||
nsCOMPtr<nsIRandomGenerator> rg =
|
||||
do_GetService("@mozilla.org/security/random-generator;1");
|
||||
NS_ENSURE_STATE(rg);
|
||||
|
||||
PRUint8 *temp;
|
||||
nsresult rv = rg->GenerateRandomBytes(sizeof(mHashKey), &temp);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
memcpy(&mHashKey, temp, sizeof(mHashKey));
|
||||
NS_Free(temp);
|
||||
|
||||
nsCOMPtr<nsIOutputStream> out;
|
||||
rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out), storeFile,
|
||||
-1, -1, 0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRUint32 written;
|
||||
rv = out->Write(reinterpret_cast<char*>(&mHashKey), sizeof(PRUint32), &written);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsISafeOutputStream> safeOut = do_QueryInterface(out);
|
||||
rv = safeOut->Finish();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Initialized classifier, key = %X", mHashKey));
|
||||
} else {
|
||||
// read key
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), storeFile,
|
||||
-1, -1, 0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(inputStream);
|
||||
nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
void *buffer = &mHashKey;
|
||||
rv = NS_ReadInputStreamToBuffer(inputStream,
|
||||
&buffer,
|
||||
sizeof(PRUint32));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Loaded classifier key = %X", mHashKey));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::Open(nsIFile& aCacheDirectory)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Ensure the safebrowsing directory exists.
|
||||
rv = aCacheDirectory.Clone(getter_AddRefs(mStoreDirectory));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mStoreDirectory->AppendNative(NS_LITERAL_CSTRING("safebrowsing"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool storeExists;
|
||||
rv = mStoreDirectory->Exists(&storeExists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!storeExists) {
|
||||
rv = mStoreDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
bool storeIsDir;
|
||||
rv = mStoreDirectory->IsDirectory(&storeIsDir);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!storeIsDir)
|
||||
return NS_ERROR_FILE_DESTINATION_NOT_DIR;
|
||||
}
|
||||
|
||||
rv = InitKey();
|
||||
if (NS_FAILED(rv)) {
|
||||
// Without a usable key the database is useless
|
||||
Reset();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mTableFreshness.Init()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::Close()
|
||||
{
|
||||
DropStores();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::Reset()
|
||||
{
|
||||
DropStores();
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator> entries;
|
||||
nsresult rv = mStoreDirectory->GetDirectoryEntries(getter_AddRefs(entries));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool hasMore;
|
||||
while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) {
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = entries->GetNext(getter_AddRefs(file));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = file->Remove(PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mTableFreshness.Clear();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
Classifier::TableRequest(nsACString& aResult)
|
||||
{
|
||||
nsTArray<nsCString> tables;
|
||||
ActiveTables(tables);
|
||||
for (uint32 i = 0; i < tables.Length(); i++) {
|
||||
nsAutoPtr<HashStore> store(new HashStore(tables[i], mStoreDirectory));
|
||||
if (!store)
|
||||
continue;
|
||||
|
||||
nsresult rv = store->Open();
|
||||
if (NS_FAILED(rv))
|
||||
continue;
|
||||
|
||||
aResult.Append(store->TableName());
|
||||
aResult.Append(";");
|
||||
|
||||
ChunkSet &adds = store->AddChunks();
|
||||
ChunkSet &subs = store->SubChunks();
|
||||
|
||||
if (adds.Length() > 0) {
|
||||
aResult.Append("a:");
|
||||
nsCAutoString addList;
|
||||
adds.Serialize(addList);
|
||||
aResult.Append(addList);
|
||||
}
|
||||
|
||||
if (subs.Length() > 0) {
|
||||
if (adds.Length() > 0)
|
||||
aResult.Append(':');
|
||||
aResult.Append("s:");
|
||||
nsCAutoString subList;
|
||||
subs.Serialize(subList);
|
||||
aResult.Append(subList);
|
||||
}
|
||||
|
||||
aResult.Append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::Check(const nsACString& aSpec, LookupResultArray& aResults)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_CL_CHECK_TIME> timer;
|
||||
|
||||
// Get the set of fragments to look up.
|
||||
nsTArray<nsCString> fragments;
|
||||
nsresult rv = LookupCache::GetLookupFragments(aSpec, &fragments);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsTArray<nsCString> activeTables;
|
||||
ActiveTables(activeTables);
|
||||
|
||||
nsTArray<LookupCache*> cacheArray;
|
||||
for (PRUint32 i = 0; i < activeTables.Length(); i++) {
|
||||
LookupCache *cache = GetLookupCache(activeTables[i]);
|
||||
if (cache) {
|
||||
cacheArray.AppendElement(cache);
|
||||
} else {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// Now check each lookup fragment against the entries in the DB.
|
||||
for (PRUint32 i = 0; i < fragments.Length(); i++) {
|
||||
Completion lookupHash;
|
||||
lookupHash.FromPlaintext(fragments[i], mCryptoHash);
|
||||
|
||||
// Get list of host keys to look up
|
||||
Completion hostKey;
|
||||
rv = LookupCache::GetKey(fragments[i], &hostKey, mCryptoHash);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Local host on the network
|
||||
continue;
|
||||
}
|
||||
|
||||
#if DEBUG && defined(PR_LOGGING)
|
||||
if (LOG_ENABLED()) {
|
||||
nsCAutoString checking;
|
||||
lookupHash.ToString(checking);
|
||||
LOG(("Checking %s (%X)", checking.get(), lookupHash.ToUint32()));
|
||||
}
|
||||
#endif
|
||||
for (PRUint32 i = 0; i < cacheArray.Length(); i++) {
|
||||
LookupCache *cache = cacheArray[i];
|
||||
bool has, complete;
|
||||
Prefix codedPrefix;
|
||||
rv = cache->Has(lookupHash, hostKey, mHashKey,
|
||||
&has, &complete, &codedPrefix);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (has) {
|
||||
LookupResult *result = aResults.AppendElement();
|
||||
if (!result)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
PRInt64 age;
|
||||
bool found = mTableFreshness.Get(cache->TableName(), &age);
|
||||
if (!found) {
|
||||
age = 24 * 60 * 60; // just a large number
|
||||
} else {
|
||||
PRInt64 now = (PR_Now() / PR_USEC_PER_SEC);
|
||||
age = now - age;
|
||||
}
|
||||
|
||||
LOG(("Found a result in %s: %s (Age: %Lds)",
|
||||
cache->TableName().get(),
|
||||
complete ? "complete." : "Not complete.",
|
||||
age));
|
||||
|
||||
result->hash.complete = lookupHash;
|
||||
result->mCodedPrefix = codedPrefix;
|
||||
result->mComplete = complete;
|
||||
result->mFresh = (age < mFreshTime);
|
||||
result->mTableName.Assign(cache->TableName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::ApplyUpdates(nsTArray<TableUpdate*>* aUpdates)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_CL_UPDATE_TIME> timer;
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
PRIntervalTime clockStart = 0;
|
||||
if (LOG_ENABLED() || true) {
|
||||
clockStart = PR_IntervalNow();
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG(("Applying table updates."));
|
||||
|
||||
nsresult rv;
|
||||
|
||||
for (uint32 i = 0; i < aUpdates->Length(); i++) {
|
||||
// Previous ApplyTableUpdates() may have consumed this update..
|
||||
if ((*aUpdates)[i]) {
|
||||
// Run all updates for one table
|
||||
rv = ApplyTableUpdates(aUpdates, aUpdates->ElementAt(i)->TableName());
|
||||
if (NS_FAILED(rv)) {
|
||||
Reset();
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
aUpdates->Clear();
|
||||
LOG(("Done applying updates."));
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
if (LOG_ENABLED() || true) {
|
||||
PRIntervalTime clockEnd = PR_IntervalNow();
|
||||
LOG(("update took %dms\n",
|
||||
PR_IntervalToMilliseconds(clockEnd - clockStart)));
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::MarkSpoiled(nsTArray<nsCString>& aTables)
|
||||
{
|
||||
for (uint32 i = 0; i < aTables.Length(); i++) {
|
||||
LOG(("Spoiling table: %s", aTables[i].get()));
|
||||
// Spoil this table by marking it as no known freshness
|
||||
mTableFreshness.Remove(aTables[i]);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
Classifier::DropStores()
|
||||
{
|
||||
for (uint32 i = 0; i < mHashStores.Length(); i++) {
|
||||
delete mHashStores[i];
|
||||
}
|
||||
mHashStores.Clear();
|
||||
for (uint32 i = 0; i < mLookupCaches.Length(); i++) {
|
||||
delete mLookupCaches[i];
|
||||
}
|
||||
mLookupCaches.Clear();
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::ScanStoreDir(nsTArray<nsCString>& aTables)
|
||||
{
|
||||
nsCOMPtr<nsISimpleEnumerator> entries;
|
||||
nsresult rv = mStoreDirectory->GetDirectoryEntries(getter_AddRefs(entries));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool hasMore;
|
||||
while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) {
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = entries->GetNext(getter_AddRefs(file));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCString leafName;
|
||||
rv = file->GetNativeLeafName(leafName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCString suffix(NS_LITERAL_CSTRING(".sbstore"));
|
||||
|
||||
PRInt32 dot = leafName.RFind(suffix, 0);
|
||||
if (dot != -1) {
|
||||
leafName.Cut(dot, suffix.Length());
|
||||
aTables.AppendElement(leafName);
|
||||
}
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::ActiveTables(nsTArray<nsCString>& aTables)
|
||||
{
|
||||
aTables.Clear();
|
||||
|
||||
nsTArray<nsCString> foundTables;
|
||||
ScanStoreDir(foundTables);
|
||||
|
||||
for (uint32 i = 0; i < foundTables.Length(); i++) {
|
||||
nsAutoPtr<HashStore> store(new HashStore(nsCString(foundTables[i]), mStoreDirectory));
|
||||
if (!store)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
nsresult rv = store->Open();
|
||||
if (NS_FAILED(rv))
|
||||
continue;
|
||||
|
||||
LookupCache *lookupCache = GetLookupCache(store->TableName());
|
||||
if (!lookupCache) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ChunkSet &adds = store->AddChunks();
|
||||
const ChunkSet &subs = store->SubChunks();
|
||||
|
||||
if (adds.Length() == 0 && subs.Length() == 0)
|
||||
continue;
|
||||
|
||||
LOG(("Active table: %s", store->TableName().get()));
|
||||
aTables.AppendElement(store->TableName());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* This will consume+delete updates from the passed nsTArray.
|
||||
*/
|
||||
nsresult
|
||||
Classifier::ApplyTableUpdates(nsTArray<TableUpdate*>* aUpdates,
|
||||
const nsACString& aTable)
|
||||
{
|
||||
LOG(("Classifier::ApplyTableUpdates(%s)",
|
||||
PromiseFlatCString(aTable).get()));
|
||||
|
||||
nsAutoPtr<HashStore> store(new HashStore(aTable, mStoreDirectory));
|
||||
|
||||
if (!store)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// take the quick exit if there is no valid update for us
|
||||
// (common case)
|
||||
uint32 validupdates = 0;
|
||||
|
||||
for (uint32 i = 0; i < aUpdates->Length(); i++) {
|
||||
TableUpdate *update = aUpdates->ElementAt(i);
|
||||
if (!update || !update->TableName().Equals(store->TableName()))
|
||||
continue;
|
||||
if (update->Empty()) {
|
||||
aUpdates->ElementAt(i) = nsnull;
|
||||
delete update;
|
||||
continue;
|
||||
}
|
||||
validupdates++;
|
||||
}
|
||||
|
||||
if (!validupdates) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = store->Open();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = store->BeginUpdate();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Read the part of the store that is (only) in the cache
|
||||
LookupCache *prefixSet = GetLookupCache(store->TableName());
|
||||
if (!prefixSet) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsTArray<PRUint32> AddPrefixHashes;
|
||||
rv = prefixSet->GetPrefixes(&AddPrefixHashes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = store->AugmentAdds(AddPrefixHashes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
uint32 applied = 0;
|
||||
bool updateFreshness = false;
|
||||
|
||||
for (uint32 i = 0; i < aUpdates->Length(); i++) {
|
||||
TableUpdate *update = aUpdates->ElementAt(i);
|
||||
if (!update || !update->TableName().Equals(store->TableName()))
|
||||
continue;
|
||||
|
||||
rv = store->ApplyUpdate(*update);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
applied++;
|
||||
|
||||
LOG(("Applied update to table %s:", PromiseFlatCString(store->TableName()).get()));
|
||||
LOG((" %d add chunks", update->AddChunks().Length()));
|
||||
LOG((" %d add prefixes", update->AddPrefixes().Length()));
|
||||
LOG((" %d add completions", update->AddCompletes().Length()));
|
||||
LOG((" %d sub chunks", update->SubChunks().Length()));
|
||||
LOG((" %d sub prefixes", update->SubPrefixes().Length()));
|
||||
LOG((" %d sub completions", update->SubCompletes().Length()));
|
||||
LOG((" %d add expirations", update->AddExpirations().Length()));
|
||||
LOG((" %d sub expirations", update->SubExpirations().Length()));
|
||||
|
||||
if (!update->IsLocalUpdate()) {
|
||||
updateFreshness = true;
|
||||
LOG(("Remote update, updating freshness"));
|
||||
}
|
||||
|
||||
aUpdates->ElementAt(i) = nsnull;
|
||||
delete update;
|
||||
}
|
||||
|
||||
LOG(("Applied %d update(s) to %s.", applied, PromiseFlatCString(store->TableName()).get()));
|
||||
|
||||
rv = store->Rebuild();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Table %s now has:", PromiseFlatCString(store->TableName()).get()));
|
||||
LOG((" %d add chunks", store->AddChunks().Length()));
|
||||
LOG((" %d add prefixes", store->AddPrefixes().Length()));
|
||||
LOG((" %d add completions", store->AddCompletes().Length()));
|
||||
LOG((" %d sub chunks", store->SubChunks().Length()));
|
||||
LOG((" %d sub prefixes", store->SubPrefixes().Length()));
|
||||
LOG((" %d sub completions", store->SubCompletes().Length()));
|
||||
|
||||
rv = store->WriteFile();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// At this point the store is updated and written out to disk, but
|
||||
// the data is still in memory. Build our quick-lookup table here.
|
||||
rv = prefixSet->Build(store->AddPrefixes(), store->AddCompletes());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
#if defined(DEBUG) && defined(PR_LOGGING)
|
||||
prefixSet->Dump();
|
||||
#endif
|
||||
prefixSet->WriteFile();
|
||||
|
||||
// This will drop all the temporary storage used during the update.
|
||||
rv = store->FinishUpdate();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (updateFreshness) {
|
||||
PRInt64 now = (PR_Now() / PR_USEC_PER_SEC);
|
||||
LOG(("Successfully updated %s", PromiseFlatCString(store->TableName()).get()));
|
||||
rv = (mTableFreshness.Put(store->TableName(), now) ? NS_OK : NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
LookupCache *
|
||||
Classifier::GetLookupCache(const nsACString& aTable)
|
||||
{
|
||||
for (uint32 i = 0; i < mLookupCaches.Length(); i++) {
|
||||
if (mLookupCaches[i]->TableName().Equals(aTable)) {
|
||||
return mLookupCaches[i];
|
||||
}
|
||||
}
|
||||
|
||||
LookupCache *cache = new LookupCache(aTable, mStoreDirectory);
|
||||
nsresult rv = cache->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
return nsnull;
|
||||
}
|
||||
rv = cache->Open();
|
||||
if (NS_FAILED(rv)) {
|
||||
if (rv == NS_ERROR_FILE_CORRUPTED) {
|
||||
Reset();
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
mLookupCaches.AppendElement(cache);
|
||||
return cache;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Classifier::ReadNoiseEntries(const Prefix& aPrefix,
|
||||
const nsACString& aTableName,
|
||||
PRInt32 aCount,
|
||||
PrefixArray* aNoiseEntries)
|
||||
{
|
||||
LookupCache *cache = GetLookupCache(aTableName);
|
||||
if (!cache) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsTArray<PRUint32> prefixes;
|
||||
nsresult rv = cache->GetPrefixes(&prefixes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRInt32 idx = prefixes.BinaryIndexOf(aPrefix.ToUint32());
|
||||
|
||||
if (idx == nsTArray<PRUint32>::NoIndex) {
|
||||
NS_WARNING("Could not find prefix in PrefixSet during noise lookup");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
idx -= idx % aCount;
|
||||
|
||||
for (PRInt32 i = 0; (i < aCount) && ((idx+i) < prefixes.Length()); i++) {
|
||||
Prefix newPref;
|
||||
newPref.FromUint32(prefixes[idx+i]);
|
||||
aNoiseEntries->AppendElement(newPref);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Url Classifier code
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dave Camp <dcamp@mozilla.com>
|
||||
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef Classifier_h__
|
||||
#define Classifier_h__
|
||||
|
||||
#include "Entries.h"
|
||||
#include "HashStore.h"
|
||||
#include "ProtocolParser.h"
|
||||
#include "LookupCache.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
/**
|
||||
* Maintains the stores and LookupCaches for the url classifier.
|
||||
*/
|
||||
class Classifier {
|
||||
public:
|
||||
Classifier();
|
||||
~Classifier();
|
||||
|
||||
nsresult Open(nsIFile& aCacheDirectory);
|
||||
nsresult Close();
|
||||
nsresult Reset();
|
||||
|
||||
/**
|
||||
* Get the list of active tables and their chunks in a format
|
||||
* suitable for an update request.
|
||||
*/
|
||||
void TableRequest(nsACString& aResult);
|
||||
|
||||
/*
|
||||
* Get all tables that we know about.
|
||||
*/
|
||||
nsresult ActiveTables(nsTArray<nsCString>& aTables);
|
||||
|
||||
/**
|
||||
* Check a URL against the database.
|
||||
*/
|
||||
nsresult Check(const nsACString& aSpec, LookupResultArray& aResults);
|
||||
|
||||
/**
|
||||
* Apply the table updates in the array. Takes ownership of
|
||||
* the updates in the array and clears it. Wacky!
|
||||
*/
|
||||
nsresult ApplyUpdates(nsTArray<TableUpdate*>* aUpdates);
|
||||
/**
|
||||
* Failed update. Spoil the entries so we don't block hosts
|
||||
* unnecessarily
|
||||
*/
|
||||
nsresult MarkSpoiled(nsTArray<nsCString>& aTables);
|
||||
nsresult CacheCompletions(const CacheResultArray& aResults);
|
||||
PRUint32 GetHashKey(void) { return mHashKey; };
|
||||
void SetFreshTime(PRUint32 aTime) { mFreshTime = aTime; };
|
||||
/*
|
||||
* Get a bunch of extra prefixes to query for completion
|
||||
* and mask the real entry being requested
|
||||
*/
|
||||
nsresult ReadNoiseEntries(const Prefix& aPrefix,
|
||||
const nsACString& aTableName,
|
||||
PRInt32 aCount,
|
||||
PrefixArray* aNoiseEntries);
|
||||
private:
|
||||
void DropStores();
|
||||
nsresult ScanStoreDir(nsTArray<nsCString>& aTables);
|
||||
|
||||
nsresult ApplyTableUpdates(nsTArray<TableUpdate*>* aUpdates,
|
||||
const nsACString& aTable);
|
||||
|
||||
LookupCache *GetLookupCache(const nsACString& aTable);
|
||||
nsresult InitKey();
|
||||
|
||||
nsCOMPtr<nsICryptoHash> mCryptoHash;
|
||||
nsCOMPtr<nsIFile> mStoreDirectory;
|
||||
nsTArray<HashStore*> mHashStores;
|
||||
nsTArray<LookupCache*> mLookupCaches;
|
||||
PRUint32 mHashKey;
|
||||
// Stores the last time a given table was updated (seconds).
|
||||
nsDataHashtable<nsCStringHashKey, PRInt64> mTableFreshness;
|
||||
PRUint32 mFreshTime;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,335 +0,0 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Url Classifier code
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dave Camp <dcamp@mozilla.com>
|
||||
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
#ifndef SBEntries_h__
|
||||
#define SBEntries_h__
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsString.h"
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "prlog.h"
|
||||
|
||||
extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
|
||||
#if defined(PR_LOGGING)
|
||||
#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
|
||||
#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#define LOG_ENABLED() (PR_FALSE)
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
#include "plbase64.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
#define PREFIX_SIZE 4
|
||||
#define COMPLETE_SIZE 32
|
||||
|
||||
template <uint32 S, class Comparator>
|
||||
struct SafebrowsingHash
|
||||
{
|
||||
static const uint32 sHashSize = S;
|
||||
typedef SafebrowsingHash<S, Comparator> self_type;
|
||||
uint8 buf[S];
|
||||
|
||||
nsresult FromPlaintext(const nsACString& aPlainText, nsICryptoHash* aHash) {
|
||||
// From the protocol doc:
|
||||
// Each entry in the chunk is composed
|
||||
// of the SHA 256 hash of a suffix/prefix expression.
|
||||
|
||||
nsresult rv = aHash->Init(nsICryptoHash::SHA256);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aHash->Update
|
||||
(reinterpret_cast<const uint8*>(aPlainText.BeginReading()),
|
||||
aPlainText.Length());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString hashed;
|
||||
rv = aHash->Finish(PR_FALSE, hashed);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ASSERTION(hashed.Length() >= sHashSize,
|
||||
"not enough characters in the hash");
|
||||
|
||||
memcpy(buf, hashed.BeginReading(), sHashSize);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Assign(const nsACString& aStr) {
|
||||
NS_ASSERTION(aStr.Length() >= sHashSize,
|
||||
"string must be at least sHashSize characters long");
|
||||
memcpy(buf, aStr.BeginReading(), sHashSize);
|
||||
}
|
||||
|
||||
int Compare(const self_type& aOther) const {
|
||||
return Comparator::Compare(buf, aOther.buf);
|
||||
}
|
||||
|
||||
bool operator==(const self_type& aOther) const {
|
||||
return Comparator::Compare(buf, aOther.buf) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const self_type& aOther) const {
|
||||
return Comparator::Compare(buf, aOther.buf) != 0;
|
||||
}
|
||||
|
||||
bool operator<(const self_type& aOther) const {
|
||||
return Comparator::Compare(buf, aOther.buf) < 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void ToString(nsACString& aStr) const {
|
||||
uint32 len = ((sHashSize + 2) / 3) * 4;
|
||||
aStr.SetCapacity(len + 1);
|
||||
PL_Base64Encode((char*)buf, sHashSize, aStr.BeginWriting());
|
||||
aStr.BeginWriting()[len] = '\0';
|
||||
}
|
||||
#endif
|
||||
PRUint32 ToUint32() const {
|
||||
PRUint32 res = 0;
|
||||
memcpy(&res, buf, NS_MIN<size_t>(4, S));
|
||||
return res;
|
||||
}
|
||||
void FromUint32(PRUint32 aHash) {
|
||||
memcpy(buf, &aHash, NS_MIN<size_t>(4, S));
|
||||
}
|
||||
};
|
||||
|
||||
class PrefixComparator {
|
||||
public:
|
||||
static int Compare(const PRUint8* a, const PRUint8* b) {
|
||||
return *((uint32*)a) - *((uint32*)b);
|
||||
}
|
||||
};
|
||||
typedef SafebrowsingHash<PREFIX_SIZE, PrefixComparator> Prefix;
|
||||
typedef nsTArray<Prefix> PrefixArray;
|
||||
|
||||
class CompletionComparator {
|
||||
public:
|
||||
static int Compare(const PRUint8* a, const PRUint8* b) {
|
||||
return memcmp(a, b, COMPLETE_SIZE);
|
||||
}
|
||||
};
|
||||
typedef SafebrowsingHash<COMPLETE_SIZE, CompletionComparator> Completion;
|
||||
typedef nsTArray<Completion> CompletionArray;
|
||||
|
||||
struct AddPrefix {
|
||||
Prefix prefix;
|
||||
uint32 addChunk;
|
||||
|
||||
AddPrefix() : addChunk(0) {}
|
||||
|
||||
uint32 Chunk() const { return addChunk; }
|
||||
const Prefix &PrefixHash() const { return prefix; }
|
||||
|
||||
template<class T>
|
||||
int Compare(const T& other) const {
|
||||
int cmp = prefix.Compare(other.PrefixHash());
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
return addChunk - other.addChunk;
|
||||
}
|
||||
};
|
||||
|
||||
struct AddComplete {
|
||||
union {
|
||||
Prefix prefix;
|
||||
Completion complete;
|
||||
} hash;
|
||||
uint32 addChunk;
|
||||
|
||||
AddComplete() : addChunk(0) {}
|
||||
|
||||
uint32 Chunk() const { return addChunk; }
|
||||
const Prefix &PrefixHash() const { return hash.prefix; }
|
||||
const Completion &CompleteHash() const { return hash.complete; }
|
||||
|
||||
template<class T>
|
||||
int Compare(const T& other) const {
|
||||
int cmp = hash.complete.Compare(other.CompleteHash());
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
return addChunk - other.addChunk;
|
||||
}
|
||||
};
|
||||
|
||||
struct SubPrefix {
|
||||
Prefix prefix;
|
||||
uint32 addChunk;
|
||||
uint32 subChunk;
|
||||
|
||||
SubPrefix(): addChunk(0), subChunk(0) {}
|
||||
|
||||
uint32 Chunk() const { return subChunk; }
|
||||
uint32 AddChunk() const { return addChunk; }
|
||||
const Prefix &PrefixHash() const { return prefix; }
|
||||
|
||||
template<class T>
|
||||
int Compare(const T& aOther) const {
|
||||
int cmp = prefix.Compare(aOther.PrefixHash());
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
if (addChunk != aOther.addChunk)
|
||||
return addChunk - aOther.addChunk;
|
||||
return subChunk - aOther.subChunk;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
int CompareAlt(const T& aOther) const {
|
||||
int cmp = prefix.Compare(aOther.PrefixHash());
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
return addChunk - aOther.addChunk;
|
||||
}
|
||||
};
|
||||
|
||||
struct SubComplete {
|
||||
union {
|
||||
Prefix prefix;
|
||||
Completion complete;
|
||||
} hash;
|
||||
uint32 addChunk;
|
||||
uint32 subChunk;
|
||||
|
||||
SubComplete() : addChunk(0), subChunk(0) {}
|
||||
|
||||
uint32 Chunk() const { return subChunk; }
|
||||
uint32 AddChunk() const { return addChunk; }
|
||||
const Prefix &PrefixHash() const { return hash.prefix; }
|
||||
const Completion &CompleteHash() const { return hash.complete; }
|
||||
|
||||
int Compare(const SubComplete& aOther) const {
|
||||
int cmp = hash.complete.Compare(aOther.hash.complete);
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
if (addChunk != aOther.addChunk)
|
||||
return addChunk - aOther.addChunk;
|
||||
return subChunk - aOther.subChunk;
|
||||
}
|
||||
};
|
||||
|
||||
typedef nsTArray<AddPrefix> AddPrefixArray;
|
||||
typedef nsTArray<AddComplete> AddCompleteArray;
|
||||
typedef nsTArray<SubPrefix> SubPrefixArray;
|
||||
typedef nsTArray<SubComplete> SubCompleteArray;
|
||||
|
||||
/**
|
||||
* Compares chunks by their add chunk, then their prefix.
|
||||
*/
|
||||
template<class T>
|
||||
class EntryCompare {
|
||||
public:
|
||||
typedef T elem_type;
|
||||
static int Compare(const void* e1, const void* e2, void* data) {
|
||||
const elem_type* a = static_cast<const elem_type*>(e1);
|
||||
const elem_type* b = static_cast<const elem_type*>(e2);
|
||||
return a->Compare(*b);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class EntryCompare<SubPrefix> {
|
||||
public:
|
||||
typedef SubPrefix elem_type;
|
||||
static int Compare(const void* e1, const void* e2, void* data) {
|
||||
const elem_type* a = static_cast<const elem_type*>(e1);
|
||||
const elem_type* b = static_cast<const elem_type*>(e2);
|
||||
return a->Compare(*b);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class EntryCompare<SubComplete> {
|
||||
public:
|
||||
typedef SubComplete elem_type;
|
||||
static int Compare(const void* e1, const void* e2, void* data) {
|
||||
const elem_type *a = static_cast<const elem_type*>(e1);
|
||||
const elem_type *b = static_cast<const elem_type*>(e2);
|
||||
return a->Compare(*b);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort an array of store entries. nsTArray::Sort uses Equal/LessThan
|
||||
* to sort, this does a single Compare so it's a bit quicker over the
|
||||
* large sorts we do.
|
||||
*/
|
||||
template<class T>
|
||||
void
|
||||
EntrySort(nsTArray<T>& aArray)
|
||||
{
|
||||
NS_QuickSort(aArray.Elements(), aArray.Length(), sizeof(T),
|
||||
EntryCompare<T>::Compare, 0);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
nsresult
|
||||
ReadTArray(nsIInputStream* aStream, nsTArray<T>* aArray, PRUint32 aNumElements)
|
||||
{
|
||||
if (!aArray->SetLength(aNumElements))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
void *buffer = aArray->Elements();
|
||||
nsresult rv = NS_ReadInputStreamToBuffer(aStream, &buffer,
|
||||
(aNumElements * sizeof(T)));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
nsresult
|
||||
WriteTArray(nsIOutputStream* aStream, nsTArray<T>& aArray)
|
||||
{
|
||||
PRUint32 written;
|
||||
return aStream->Write(reinterpret_cast<char*>(aArray.Elements()),
|
||||
aArray.Length() * sizeof(T),
|
||||
&written);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,950 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
// Originally based on Chrome sources:
|
||||
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
#include "HashStore.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsIStreamConverterService.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsCheckSummedOutputStream.h"
|
||||
#include "prlog.h"
|
||||
#include "zlib.h"
|
||||
|
||||
// Main store for SafeBrowsing protocol data. We store
|
||||
// known add/sub chunks, prefixe and completions s in memory
|
||||
// during an update, and serialize to disk.
|
||||
// We do not store the add prefixes, those are retrieved by
|
||||
// decompressing the PrefixSet cache whenever we need to apply
|
||||
// an update.
|
||||
|
||||
// Data format:
|
||||
// uint32 magic
|
||||
// uint32 version
|
||||
// uint32 numAddChunks
|
||||
// uint32 numSubChunks
|
||||
// uint32 numAddPrefixes
|
||||
// uint32 numSubPrefixes
|
||||
// uint32 numAddCompletes
|
||||
// uint32 numSubCompletes
|
||||
// 0...numAddChunks uint32 addChunk
|
||||
// 0...numSubChunks uint32 subChunk
|
||||
// uint32 compressed-size
|
||||
// compressed-size bytes zlib inflate data
|
||||
// 0...numAddPrefixes uint32 addChunk
|
||||
// uint32 compressed-size
|
||||
// compressed-size bytes zlib inflate data
|
||||
// 0...numSubPrefixes uint32 addChunk
|
||||
// uint32 compressed-size
|
||||
// compressed-size bytes zlib inflate data
|
||||
// 0...numSubPrefixes uint32 subChunk
|
||||
// 0...numSubPrefixes uint32 subPrefix
|
||||
// 0...numAddCompletes 32-byte Completions
|
||||
// 0...numSubCompletes 32-byte Completions
|
||||
// 16-byte MD5 of all preceding data
|
||||
|
||||
// NSPR_LOG_MODULES=UrlClassifierDbService:5
|
||||
extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
|
||||
#if defined(PR_LOGGING)
|
||||
#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
|
||||
#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#define LOG_ENABLED() (PR_FALSE)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
const uint32 STORE_MAGIC = 0x1231af3b;
|
||||
const uint32 CURRENT_VERSION = 1;
|
||||
|
||||
void
|
||||
TableUpdate::NewAddPrefix(PRUint32 aAddChunk, const Prefix& aHash)
|
||||
{
|
||||
AddPrefix *add = mAddPrefixes.AppendElement();
|
||||
add->addChunk = aAddChunk;
|
||||
add->prefix = aHash;
|
||||
}
|
||||
|
||||
void
|
||||
TableUpdate::NewSubPrefix(PRUint32 aAddChunk, const Prefix& aHash, PRUint32 aSubChunk)
|
||||
{
|
||||
SubPrefix *sub = mSubPrefixes.AppendElement();
|
||||
sub->addChunk = aAddChunk;
|
||||
sub->prefix = aHash;
|
||||
sub->subChunk = aSubChunk;
|
||||
}
|
||||
|
||||
void
|
||||
TableUpdate::NewAddComplete(PRUint32 aAddChunk, const Completion& aHash)
|
||||
{
|
||||
AddComplete *add = mAddCompletes.AppendElement();
|
||||
add->addChunk = aAddChunk;
|
||||
add->hash.complete = aHash;
|
||||
}
|
||||
|
||||
void
|
||||
TableUpdate::NewSubComplete(PRUint32 aAddChunk, const Completion& aHash, PRUint32 aSubChunk)
|
||||
{
|
||||
SubComplete *sub = mSubCompletes.AppendElement();
|
||||
sub->addChunk = aAddChunk;
|
||||
sub->hash.complete = aHash;
|
||||
sub->subChunk = aSubChunk;
|
||||
}
|
||||
|
||||
|
||||
HashStore::HashStore(const nsACString& aTableName, nsIFile* aStoreDir)
|
||||
: mTableName(aTableName)
|
||||
, mStoreDirectory(aStoreDir)
|
||||
, mInUpdate(false)
|
||||
{
|
||||
}
|
||||
|
||||
HashStore::~HashStore()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::Reset()
|
||||
{
|
||||
LOG(("HashStore resetting"));
|
||||
|
||||
nsCOMPtr<nsIFile> storeFile;
|
||||
nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = storeFile->Remove(PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
Clear();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::CheckChecksum(nsIFile* aStoreFile)
|
||||
{
|
||||
// Check for file corruption by
|
||||
// comparing the stored checksum to actual checksum of data
|
||||
nsCAutoString hash;
|
||||
nsCAutoString compareHash;
|
||||
char *data;
|
||||
PRUint32 read;
|
||||
|
||||
PRInt64 fileSize;
|
||||
nsresult rv = aStoreFile->GetFileSize(&fileSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (fileSize < 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
rv = CalculateChecksum(hash, true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
compareHash.GetMutableData(&data, hash.Length());
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekIn = do_QueryInterface(mInputStream);
|
||||
rv = seekIn->Seek(nsISeekableStream::NS_SEEK_SET, fileSize-hash.Length());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mInputStream->Read(data, hash.Length(), &read);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ASSERTION(read == hash.Length(), "Could not read hash bytes");
|
||||
|
||||
if (!hash.Equals(compareHash)) {
|
||||
NS_WARNING("Safebrowing file failed checksum.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::Open()
|
||||
{
|
||||
nsCOMPtr<nsIFile> storeFile;
|
||||
nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIInputStream> origStream;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(origStream), storeFile,
|
||||
PR_RDONLY);
|
||||
|
||||
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
|
||||
Reset();
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (rv == NS_ERROR_FILE_NOT_FOUND) {
|
||||
Clear();
|
||||
UpdateHeader();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream), origStream,
|
||||
BUFFER_SIZE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = CheckChecksum(storeFile);
|
||||
if (NS_FAILED(rv)) {
|
||||
Reset();
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = ReadHeader();
|
||||
if (NS_FAILED(rv)) {
|
||||
Reset();
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = SanityCheck(storeFile);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Safebrowsing file failed sanity check. probably out of date.");
|
||||
Reset();
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = ReadChunkNumbers();
|
||||
if (NS_FAILED(rv)) {
|
||||
Reset();
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
HashStore::Clear()
|
||||
{
|
||||
mAddChunks.Clear();
|
||||
mSubChunks.Clear();
|
||||
mAddExpirations.Clear();
|
||||
mSubExpirations.Clear();
|
||||
mAddPrefixes.Clear();
|
||||
mSubPrefixes.Clear();
|
||||
mAddCompletes.Clear();
|
||||
mSubCompletes.Clear();
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::ReadEntireStore()
|
||||
{
|
||||
Clear();
|
||||
|
||||
nsresult rv = ReadHeader();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = ReadChunkNumbers();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = ReadHashes();
|
||||
if (NS_FAILED(rv)) {
|
||||
// we are the only one reading this so it's up to us to detect corruption
|
||||
Reset();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::ReadHeader()
|
||||
{
|
||||
if (!mInputStream) {
|
||||
Clear();
|
||||
UpdateHeader();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
|
||||
nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
void *buffer = &mHeader;
|
||||
rv = NS_ReadInputStreamToBuffer(mInputStream,
|
||||
&buffer,
|
||||
sizeof(Header));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::SanityCheck(nsIFile *storeFile)
|
||||
{
|
||||
if (mHeader.magic != STORE_MAGIC || mHeader.version != CURRENT_VERSION) {
|
||||
NS_WARNING("Unexpected header data in the store.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::CalculateChecksum(nsCAutoString& aChecksum, bool aChecksumPresent)
|
||||
{
|
||||
aChecksum.Truncate();
|
||||
|
||||
nsCOMPtr<nsIFile> storeFile;
|
||||
nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIInputStream> hashStream;
|
||||
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(hashStream), storeFile,
|
||||
PR_RDONLY);
|
||||
|
||||
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
|
||||
Reset();
|
||||
return rv;
|
||||
}
|
||||
|
||||
PRInt64 fileSize;
|
||||
rv = storeFile->GetFileSize(&fileSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (fileSize < 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsICryptoHash> hash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Size of MD5 hash in bytes
|
||||
const uint32 CHECKSUM_SIZE = 16;
|
||||
|
||||
rv = hash->Init(nsICryptoHash::MD5);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!aChecksumPresent) {
|
||||
// Hash entire file
|
||||
rv = hash->UpdateFromStream(hashStream, PR_UINT32_MAX);
|
||||
} else {
|
||||
// Hash everything but last checksum bytes
|
||||
rv = hash->UpdateFromStream(hashStream, fileSize-CHECKSUM_SIZE);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = hash->Finish(PR_FALSE, aChecksum);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
HashStore::UpdateHeader()
|
||||
{
|
||||
mHeader.magic = STORE_MAGIC;
|
||||
mHeader.version = CURRENT_VERSION;
|
||||
|
||||
mHeader.numAddChunks = mAddChunks.Length();
|
||||
mHeader.numSubChunks = mSubChunks.Length();
|
||||
mHeader.numAddPrefixes = mAddPrefixes.Length();
|
||||
mHeader.numSubPrefixes = mSubPrefixes.Length();
|
||||
mHeader.numAddCompletes = mAddCompletes.Length();
|
||||
mHeader.numSubCompletes = mSubCompletes.Length();
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::ReadChunkNumbers()
|
||||
{
|
||||
if (!mInputStream) {
|
||||
LOG(("Clearing."));
|
||||
Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
|
||||
nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
|
||||
sizeof(Header));
|
||||
|
||||
rv = mAddChunks.Read(mInputStream, mHeader.numAddChunks);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ASSERTION(mAddChunks.Length() == mHeader.numAddChunks, "Read the right amount of add chunks.");
|
||||
|
||||
rv = mSubChunks.Read(mInputStream, mHeader.numSubChunks);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ASSERTION(mSubChunks.Length() == mHeader.numSubChunks, "Read the right amount of sub chunks.");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::ReadHashes()
|
||||
{
|
||||
if (!mInputStream) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
|
||||
|
||||
uint32 offset = sizeof(Header);
|
||||
offset += (mHeader.numAddChunks + mHeader.numSubChunks) * sizeof(uint32);
|
||||
nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
|
||||
|
||||
rv = ReadAddPrefixes();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = ReadSubPrefixes();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = ReadTArray(mInputStream, &mAddCompletes, mHeader.numAddCompletes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = ReadTArray(mInputStream, &mSubCompletes, mHeader.numSubCompletes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::BeginUpdate()
|
||||
{
|
||||
mInUpdate = true;
|
||||
|
||||
nsresult rv = ReadEntireStore();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static nsresult
|
||||
Merge(ChunkSet* aStoreChunks,
|
||||
nsTArray<T>* aStorePrefixes,
|
||||
ChunkSet& aUpdateChunks,
|
||||
nsTArray<T>& aUpdatePrefixes)
|
||||
{
|
||||
EntrySort(aUpdatePrefixes);
|
||||
|
||||
T* updateIter = aUpdatePrefixes.Elements();
|
||||
T* updateEnd = aUpdatePrefixes.Elements() + aUpdatePrefixes.Length();
|
||||
|
||||
T* storeIter = aStorePrefixes->Elements();
|
||||
T* storeEnd = aStorePrefixes->Elements() + aStorePrefixes->Length();
|
||||
|
||||
// use a separate array so we can keep the iterators valid
|
||||
// if the nsTArray grows
|
||||
nsTArray<T> adds;
|
||||
|
||||
for (; updateIter != updateEnd; updateIter++) {
|
||||
// XXX: binary search for insertion point might be faster in common
|
||||
// case?
|
||||
while (storeIter < storeEnd && (storeIter->Compare(*updateIter) < 0)) {
|
||||
// skip forward to matching element (or not...)
|
||||
storeIter++;
|
||||
}
|
||||
// no match, add
|
||||
if (storeIter == storeEnd
|
||||
|| storeIter->Compare(*updateIter) != 0) {
|
||||
if (!adds.AppendElement(*updateIter))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
// chunks can be empty, but we should still report we have them
|
||||
// to make the chunkranges continuous
|
||||
aStoreChunks->Merge(aUpdateChunks);
|
||||
|
||||
aStorePrefixes->AppendElements(adds);
|
||||
EntrySort(*aStorePrefixes);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::ApplyUpdate(TableUpdate &update)
|
||||
{
|
||||
nsresult rv = mAddExpirations.Merge(update.AddExpirations());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mSubExpirations.Merge(update.SubExpirations());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = Expire();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = Merge(&mAddChunks, &mAddPrefixes,
|
||||
update.AddChunks(), update.AddPrefixes());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = Merge(&mAddChunks, &mAddCompletes,
|
||||
update.AddChunks(), update.AddCompletes());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = Merge(&mSubChunks, &mSubPrefixes,
|
||||
update.SubChunks(), update.SubPrefixes());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = Merge(&mSubChunks, &mSubCompletes,
|
||||
update.SubChunks(), update.SubCompletes());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::Rebuild()
|
||||
{
|
||||
NS_ASSERTION(mInUpdate, "Must be in update to rebuild.");
|
||||
|
||||
nsresult rv = ProcessSubs();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
UpdateHeader();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void
|
||||
ExpireEntries(nsTArray<T>* aEntries, ChunkSet& aExpirations)
|
||||
{
|
||||
T* addIter = aEntries->Elements();
|
||||
T* end = aEntries->Elements() + aEntries->Length();
|
||||
|
||||
for (T *iter = addIter; iter != end; iter++) {
|
||||
if (!aExpirations.Has(iter->Chunk())) {
|
||||
*addIter = *iter;
|
||||
addIter++;
|
||||
}
|
||||
}
|
||||
|
||||
aEntries->SetLength(addIter - aEntries->Elements());
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::Expire()
|
||||
{
|
||||
ExpireEntries(&mAddPrefixes, mAddExpirations);
|
||||
ExpireEntries(&mAddCompletes, mAddExpirations);
|
||||
ExpireEntries(&mSubPrefixes, mSubExpirations);
|
||||
ExpireEntries(&mSubCompletes, mSubExpirations);
|
||||
|
||||
mAddChunks.Remove(mAddExpirations);
|
||||
mSubChunks.Remove(mSubExpirations);
|
||||
|
||||
mAddExpirations.Clear();
|
||||
mSubExpirations.Clear();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
nsresult DeflateWriteTArray(nsIOutputStream* aStream, nsTArray<T>& aIn)
|
||||
{
|
||||
uLongf insize = aIn.Length() * sizeof(T);
|
||||
uLongf outsize = compressBound(insize);
|
||||
nsTArray<char> outBuff;
|
||||
outBuff.SetLength(outsize);
|
||||
|
||||
int zerr = compress(reinterpret_cast<Bytef*>(outBuff.Elements()),
|
||||
&outsize,
|
||||
reinterpret_cast<const Bytef*>(aIn.Elements()),
|
||||
insize);
|
||||
if (zerr != Z_OK) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
LOG(("DeflateWriteTArray: %d in %d out", insize, outsize));
|
||||
|
||||
outBuff.TruncateLength(outsize);
|
||||
|
||||
// Length of compressed data stream
|
||||
PRUint32 dataLen = outBuff.Length();
|
||||
PRUint32 written;
|
||||
nsresult rv = aStream->Write(reinterpret_cast<char*>(&dataLen), sizeof(dataLen), &written);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ASSERTION(written == sizeof(dataLen), "Error writing deflate length");
|
||||
|
||||
// Store to stream
|
||||
rv = WriteTArray(aStream, outBuff);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
nsresult InflateReadTArray(nsIInputStream* aStream, nsTArray<T>* aOut,
|
||||
PRUint32 aExpectedSize)
|
||||
{
|
||||
|
||||
PRUint32 inLen;
|
||||
PRUint32 read;
|
||||
nsresult rv = aStream->Read(reinterpret_cast<char*>(&inLen), sizeof(inLen), &read);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ASSERTION(read == sizeof(inLen), "Error reading inflate length");
|
||||
|
||||
nsTArray<char> inBuff;
|
||||
inBuff.SetLength(inLen);
|
||||
|
||||
rv = ReadTArray(aStream, &inBuff, inLen);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
uLongf insize = inLen;
|
||||
uLongf outsize = aExpectedSize * sizeof(T);
|
||||
aOut->SetLength(aExpectedSize);
|
||||
|
||||
int zerr = uncompress(reinterpret_cast<Bytef*>(aOut->Elements()),
|
||||
&outsize,
|
||||
reinterpret_cast<const Bytef*>(inBuff.Elements()),
|
||||
insize);
|
||||
if (zerr != Z_OK) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
LOG(("InflateReadTArray: %d in %d out", insize, outsize));
|
||||
|
||||
NS_ASSERTION(outsize == aExpectedSize * sizeof(T), "Decompression size mismatch");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::ReadAddPrefixes()
|
||||
{
|
||||
nsTArray<uint32> chunks;
|
||||
PRUint32 count = mHeader.numAddPrefixes;
|
||||
|
||||
nsresult rv = InflateReadTArray(mInputStream, &chunks, count);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mAddPrefixes.SetCapacity(count);
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
AddPrefix *add = mAddPrefixes.AppendElement();
|
||||
add->prefix.FromUint32(0);
|
||||
add->addChunk = chunks[i];
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::ReadSubPrefixes()
|
||||
{
|
||||
nsTArray<PRUint32> addchunks;
|
||||
nsTArray<PRUint32> subchunks;
|
||||
nsTArray<Prefix> prefixes;
|
||||
PRUint32 count = mHeader.numSubPrefixes;
|
||||
|
||||
nsresult rv = InflateReadTArray(mInputStream, &addchunks, count);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = InflateReadTArray(mInputStream, &subchunks, count);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = ReadTArray(mInputStream, &prefixes, count);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mSubPrefixes.SetCapacity(count);
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
SubPrefix *sub = mSubPrefixes.AppendElement();
|
||||
sub->addChunk = addchunks[i];
|
||||
sub->prefix = prefixes[i];
|
||||
sub->subChunk = subchunks[i];
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Split up PrefixArray back into the constituents
|
||||
nsresult
|
||||
HashStore::WriteAddPrefixes(nsIOutputStream* aOut)
|
||||
{
|
||||
nsTArray<uint32> chunks;
|
||||
PRUint32 count = mAddPrefixes.Length();
|
||||
chunks.SetCapacity(count);
|
||||
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
chunks.AppendElement(mAddPrefixes[i].Chunk());
|
||||
}
|
||||
|
||||
nsresult rv = DeflateWriteTArray(aOut, chunks);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::WriteSubPrefixes(nsIOutputStream* aOut)
|
||||
{
|
||||
nsTArray<uint32> addchunks;
|
||||
nsTArray<uint32> subchunks;
|
||||
nsTArray<Prefix> prefixes;
|
||||
PRUint32 count = mSubPrefixes.Length();
|
||||
addchunks.SetCapacity(count);
|
||||
subchunks.SetCapacity(count);
|
||||
prefixes.SetCapacity(count);
|
||||
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
addchunks.AppendElement(mSubPrefixes[i].AddChunk());
|
||||
prefixes.AppendElement(mSubPrefixes[i].PrefixHash());
|
||||
subchunks.AppendElement(mSubPrefixes[i].Chunk());
|
||||
}
|
||||
|
||||
nsresult rv = DeflateWriteTArray(aOut, addchunks);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = DeflateWriteTArray(aOut, subchunks);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// chunk-ordered prefixes are not compressible
|
||||
rv = WriteTArray(aOut, prefixes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::WriteFile()
|
||||
{
|
||||
nsCOMPtr<nsIFile> storeFile;
|
||||
nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Need to close the inputstream here *before* rewriting its file.
|
||||
// Windows will fail with an access violation if we don't.
|
||||
if (mInputStream) {
|
||||
rv = mInputStream->Close();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIOutputStream> out;
|
||||
rv = NS_NewCheckSummedOutputStream(getter_AddRefs(out), storeFile,
|
||||
PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRUint32 written;
|
||||
rv = out->Write(reinterpret_cast<char*>(&mHeader), sizeof(mHeader), &written);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Write chunk numbers...
|
||||
rv = mAddChunks.Write(out);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mSubChunks.Write(out);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Write hashes..
|
||||
rv = WriteAddPrefixes(out);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = WriteSubPrefixes(out);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = WriteTArray(out, mAddCompletes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = WriteTArray(out, mSubCompletes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsISafeOutputStream> safeOut = do_QueryInterface(out, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = safeOut->Finish();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Reopen the file now that we've rewritten it.
|
||||
nsCOMPtr<nsIInputStream> origStream;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(origStream), storeFile,
|
||||
PR_RDONLY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream), origStream,
|
||||
BUFFER_SIZE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::FinishUpdate()
|
||||
{
|
||||
// Drop add/sub data, it's only used during updates.
|
||||
mAddPrefixes.Clear();
|
||||
mSubPrefixes.Clear();
|
||||
mAddCompletes.Clear();
|
||||
mSubCompletes.Clear();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void
|
||||
Erase(nsTArray<T>* array, T* iterStart, T* iterEnd)
|
||||
{
|
||||
uint32 start = iterStart - array->Elements();
|
||||
uint32 count = iterEnd - iterStart;
|
||||
|
||||
if (count > 0) {
|
||||
array->RemoveElementsAt(start, count);
|
||||
}
|
||||
}
|
||||
|
||||
// Find items matching between |subs| and |adds|, and remove them,
|
||||
// recording the item from |adds| in |adds_removed|. To minimize
|
||||
// copies, the inputs are processing in parallel, so |subs| and |adds|
|
||||
// should be compatibly ordered (either by SBAddPrefixLess or
|
||||
// SBAddPrefixHashLess).
|
||||
//
|
||||
// |predAS| provides add < sub, |predSA| provides sub < add, for the
|
||||
// tightest compare appropriate (see calls in SBProcessSubs).
|
||||
template<class TSub, class TAdd>
|
||||
static void
|
||||
KnockoutSubs(nsTArray<TSub>* aSubs, nsTArray<TAdd>* aAdds)
|
||||
{
|
||||
// Keep a pair of output iterators for writing kept items. Due to
|
||||
// deletions, these may lag the main iterators. Using erase() on
|
||||
// individual items would result in O(N^2) copies. Using a list
|
||||
// would work around that, at double or triple the memory cost.
|
||||
TAdd* addOut = aAdds->Elements();
|
||||
TAdd* addIter = aAdds->Elements();
|
||||
|
||||
TSub* subOut = aSubs->Elements();
|
||||
TSub* subIter = aSubs->Elements();
|
||||
|
||||
TAdd* addEnd = addIter + aAdds->Length();
|
||||
TSub* subEnd = subIter + aSubs->Length();
|
||||
|
||||
while (addIter != addEnd && subIter != subEnd) {
|
||||
// additer compare, so it compares on add chunk
|
||||
int32 cmp = addIter->Compare(*subIter);
|
||||
if (cmp > 0) {
|
||||
// If |*sub_iter| < |*add_iter|, retain the sub.
|
||||
*subOut = *subIter;
|
||||
++subOut;
|
||||
++subIter;
|
||||
} else if (cmp < 0) {
|
||||
// If |*add_iter| < |*sub_iter|, retain the add.
|
||||
*addOut = *addIter;
|
||||
++addOut;
|
||||
++addIter;
|
||||
} else {
|
||||
// Drop equal items
|
||||
++addIter;
|
||||
++subIter;
|
||||
}
|
||||
}
|
||||
|
||||
Erase(aAdds, addOut, addIter);
|
||||
Erase(aSubs, subOut, subIter);
|
||||
}
|
||||
|
||||
// Remove items in |removes| from |fullHashes|. |fullHashes| and
|
||||
// |removes| should be ordered by SBAddPrefix component.
|
||||
template <class T>
|
||||
static void
|
||||
RemoveMatchingPrefixes(const SubPrefixArray& aSubs, nsTArray<T>* aFullHashes)
|
||||
{
|
||||
// Where to store kept items.
|
||||
T* out = aFullHashes->Elements();
|
||||
T* hashIter = out;
|
||||
T* hashEnd = aFullHashes->Elements() + aFullHashes->Length();
|
||||
|
||||
SubPrefix const * removeIter = aSubs.Elements();
|
||||
SubPrefix const * removeEnd = aSubs.Elements() + aSubs.Length();
|
||||
|
||||
while (hashIter != hashEnd && removeIter != removeEnd) {
|
||||
int32 cmp = removeIter->CompareAlt(*hashIter);
|
||||
if (cmp > 0) {
|
||||
// Keep items less than |*removeIter|.
|
||||
*out = *hashIter;
|
||||
++out;
|
||||
++hashIter;
|
||||
} else if (cmp < 0) {
|
||||
// No hit for |*removeIter|, bump it forward.
|
||||
++removeIter;
|
||||
} else {
|
||||
// Drop equal items, there may be multiple hits.
|
||||
do {
|
||||
++hashIter;
|
||||
} while (hashIter != hashEnd &&
|
||||
!(removeIter->CompareAlt(*hashIter) < 0));
|
||||
++removeIter;
|
||||
}
|
||||
}
|
||||
Erase(aFullHashes, out, hashIter);
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::ProcessSubs()
|
||||
{
|
||||
EntrySort(mAddPrefixes);
|
||||
EntrySort(mSubPrefixes);
|
||||
EntrySort(mAddCompletes);
|
||||
EntrySort(mSubCompletes);
|
||||
|
||||
KnockoutSubs(&mSubPrefixes, &mAddPrefixes);
|
||||
|
||||
RemoveMatchingPrefixes(mSubPrefixes, &mAddCompletes);
|
||||
RemoveMatchingPrefixes(mSubPrefixes, &mSubCompletes);
|
||||
|
||||
KnockoutSubs(&mSubCompletes, &mAddCompletes);
|
||||
|
||||
// Clean up temporary subs used for knocking out completes
|
||||
ChunkSet dummyChunks;
|
||||
dummyChunks.Set(0);
|
||||
ExpireEntries(&mSubPrefixes, dummyChunks);
|
||||
ExpireEntries(&mSubCompletes, dummyChunks);
|
||||
mSubChunks.Remove(dummyChunks);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HashStore::AugmentAdds(const nsTArray<PRUint32>& aPrefixes)
|
||||
{
|
||||
uint32 cnt = aPrefixes.Length();
|
||||
if (cnt != mAddPrefixes.Length()) {
|
||||
LOG(("Amount of prefixes in cache not consistent with store (%d vs %d)",
|
||||
aPrefixes.Length(), mAddPrefixes.Length()));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
for (uint32 i = 0; i < cnt; i++) {
|
||||
mAddPrefixes[i].prefix.FromUint32(aPrefixes[i]);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,213 +0,0 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Url Classifier code
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dave Camp <dcamp@mozilla.com>
|
||||
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef HashStore_h__
|
||||
#define HashStore_h__
|
||||
|
||||
#include "Entries.h"
|
||||
#include "ChunkSet.h"
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
class TableUpdate {
|
||||
public:
|
||||
TableUpdate(const nsACString& aTable)
|
||||
: mTable(aTable), mLocalUpdate(false) {}
|
||||
const nsCString& TableName() const { return mTable; }
|
||||
|
||||
bool Empty() const {
|
||||
return mAddChunks.Length() == 0 &&
|
||||
mSubChunks.Length() == 0 &&
|
||||
mAddExpirations.Length() == 0 &&
|
||||
mSubExpirations.Length() == 0 &&
|
||||
mAddPrefixes.Length() == 0 &&
|
||||
mSubPrefixes.Length() == 0 &&
|
||||
mAddCompletes.Length() == 0 &&
|
||||
mSubCompletes.Length() == 0;
|
||||
}
|
||||
|
||||
void NewAddChunk(PRUint32 aChunk) { mAddChunks.Set(aChunk); }
|
||||
void NewSubChunk(PRUint32 aChunk) { mSubChunks.Set(aChunk); }
|
||||
|
||||
void NewAddExpiration(PRUint32 aChunk) { mAddExpirations.Set(aChunk); }
|
||||
void NewSubExpiration(PRUint32 aChunk) { mSubExpirations.Set(aChunk); }
|
||||
|
||||
void NewAddPrefix(PRUint32 aAddChunk, const Prefix& aPrefix);
|
||||
void NewSubPrefix(PRUint32 aAddChunk, const Prefix& aPprefix, PRUint32 aSubChunk);
|
||||
void NewAddComplete(PRUint32 aChunk, const Completion& aCompletion);
|
||||
void NewSubComplete(PRUint32 aAddChunk, const Completion& aCompletion,
|
||||
PRUint32 aSubChunk);
|
||||
void SetLocalUpdate(void) { mLocalUpdate = true; };
|
||||
bool IsLocalUpdate(void) { return mLocalUpdate; };
|
||||
|
||||
ChunkSet& AddChunks() { return mAddChunks; }
|
||||
ChunkSet& SubChunks() { return mSubChunks; }
|
||||
|
||||
ChunkSet& AddExpirations() { return mAddExpirations; }
|
||||
ChunkSet& SubExpirations() { return mSubExpirations; }
|
||||
|
||||
AddPrefixArray& AddPrefixes() { return mAddPrefixes; }
|
||||
SubPrefixArray& SubPrefixes() { return mSubPrefixes; }
|
||||
AddCompleteArray& AddCompletes() { return mAddCompletes; }
|
||||
SubCompleteArray& SubCompletes() { return mSubCompletes; }
|
||||
|
||||
private:
|
||||
nsCString mTable;
|
||||
// Update not from the remote server (no freshness)
|
||||
bool mLocalUpdate;
|
||||
|
||||
ChunkSet mAddChunks;
|
||||
ChunkSet mSubChunks;
|
||||
ChunkSet mAddExpirations;
|
||||
ChunkSet mSubExpirations;
|
||||
AddPrefixArray mAddPrefixes;
|
||||
SubPrefixArray mSubPrefixes;
|
||||
AddCompleteArray mAddCompletes;
|
||||
SubCompleteArray mSubCompletes;
|
||||
};
|
||||
|
||||
class HashStore {
|
||||
public:
|
||||
HashStore(const nsACString& aTableName, nsIFile* aStoreFile);
|
||||
~HashStore();
|
||||
|
||||
const nsCString& TableName() const { return mTableName; };
|
||||
|
||||
nsresult Open();
|
||||
nsresult AugmentAdds(const nsTArray<PRUint32>& aPrefixes);
|
||||
|
||||
ChunkSet& AddChunks() { return mAddChunks; }
|
||||
ChunkSet& SubChunks() { return mSubChunks; }
|
||||
const AddPrefixArray& AddPrefixes() const { return mAddPrefixes; }
|
||||
const AddCompleteArray& AddCompletes() const { return mAddCompletes; }
|
||||
const SubPrefixArray& SubPrefixes() const { return mSubPrefixes; }
|
||||
const SubCompleteArray& SubCompletes() const { return mSubCompletes; }
|
||||
|
||||
// =======
|
||||
// Updates
|
||||
// =======
|
||||
// Begin the update process. Reads the store into memory.
|
||||
nsresult BeginUpdate();
|
||||
|
||||
// Imports the data from a TableUpdate.
|
||||
nsresult ApplyUpdate(TableUpdate &aUpdate);
|
||||
|
||||
// Process expired chunks
|
||||
nsresult Expire();
|
||||
|
||||
// Rebuild the store, Incorporating all the applied updates.
|
||||
nsresult Rebuild();
|
||||
|
||||
// Write the current state of the store to disk.
|
||||
// If you call between ApplyUpdate() and Rebuild(), you'll
|
||||
// have a mess on your hands.
|
||||
nsresult WriteFile();
|
||||
|
||||
// Drop memory used during the update process.
|
||||
nsresult FinishUpdate();
|
||||
|
||||
// Force the entire store in memory
|
||||
nsresult ReadEntireStore();
|
||||
|
||||
private:
|
||||
static const int BUFFER_SIZE = 6 * 1024 * 1024;
|
||||
|
||||
void Clear();
|
||||
nsresult Reset();
|
||||
|
||||
nsresult ReadHeader();
|
||||
nsresult SanityCheck(nsIFile* aStoreFile);
|
||||
nsresult CalculateChecksum(nsCAutoString& aChecksum, bool aChecksumPresent);
|
||||
nsresult CheckChecksum(nsIFile* aStoreFile);
|
||||
void UpdateHeader();
|
||||
|
||||
nsresult EnsureChunkNumbers();
|
||||
nsresult ReadChunkNumbers();
|
||||
nsresult ReadHashes();
|
||||
nsresult ReadAddPrefixes();
|
||||
nsresult ReadSubPrefixes();
|
||||
|
||||
nsresult WriteAddPrefixes(nsIOutputStream* aOut);
|
||||
nsresult WriteSubPrefixes(nsIOutputStream* aOut);
|
||||
|
||||
nsresult ProcessSubs();
|
||||
|
||||
struct Header {
|
||||
uint32 magic;
|
||||
uint32 version;
|
||||
uint32 numAddChunks;
|
||||
uint32 numSubChunks;
|
||||
uint32 numAddPrefixes;
|
||||
uint32 numSubPrefixes;
|
||||
uint32 numAddCompletes;
|
||||
uint32 numSubCompletes;
|
||||
};
|
||||
|
||||
Header mHeader;
|
||||
|
||||
nsCString mTableName;
|
||||
nsCOMPtr<nsIFile> mStoreDirectory;
|
||||
|
||||
bool mInUpdate;
|
||||
|
||||
nsCOMPtr<nsIInputStream> mInputStream;
|
||||
|
||||
bool haveChunks;
|
||||
ChunkSet mAddChunks;
|
||||
ChunkSet mSubChunks;
|
||||
|
||||
ChunkSet mAddExpirations;
|
||||
ChunkSet mSubExpirations;
|
||||
|
||||
AddPrefixArray mAddPrefixes;
|
||||
AddCompleteArray mAddCompletes;
|
||||
SubPrefixArray mSubPrefixes;
|
||||
SubCompleteArray mSubCompletes;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,776 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Url Classifier code
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dave Camp <dcamp@mozilla.com>
|
||||
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "LookupCache.h"
|
||||
#include "HashStore.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "prlog.h"
|
||||
#include "prprf.h"
|
||||
|
||||
// We act as the main entry point for all the real lookups,
|
||||
// so note that those are not done to the actual HashStore.
|
||||
// The latter solely exists to store the data needed to handle
|
||||
// the updates from the protocol.
|
||||
|
||||
// This module has its own store, which stores the Completions,
|
||||
// mostly caching lookups that have happened over the net.
|
||||
// The prefixes are cached/checked by looking them up in the
|
||||
// PrefixSet.
|
||||
|
||||
// Data format for the ".cache" files:
|
||||
// uint32 magic Identify the file type
|
||||
// uint32 version Version identifier for file format
|
||||
// uint32 numCompletions Amount of completions stored
|
||||
// 0...numCompletions 256-bit Completions
|
||||
|
||||
// Name of the lookupcomplete cache
|
||||
#define CACHE_SUFFIX ".cache"
|
||||
|
||||
// Name of the persistent PrefixSet storage
|
||||
#define PREFIXSET_SUFFIX ".pset"
|
||||
|
||||
// NSPR_LOG_MODULES=UrlClassifierDbService:5
|
||||
extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
|
||||
#if defined(PR_LOGGING)
|
||||
#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
|
||||
#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#define LOG_ENABLED() (false)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
const uint32 LOOKUPCACHE_MAGIC = 0x1231af3e;
|
||||
const uint32 CURRENT_VERSION = 1;
|
||||
|
||||
LookupCache::LookupCache(const nsACString& aTableName, nsIFile* aStoreDir)
|
||||
: mPrimed(false)
|
||||
, mTableName(aTableName)
|
||||
, mStoreDirectory(aStoreDir)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::Init()
|
||||
{
|
||||
mPrefixSet = new nsUrlClassifierPrefixSet();
|
||||
nsresult rv = mPrefixSet->Init(mTableName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
LookupCache::~LookupCache()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::Open()
|
||||
{
|
||||
nsCOMPtr<nsIFile> storeFile;
|
||||
|
||||
nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(mInputStream), storeFile,
|
||||
PR_RDONLY);
|
||||
|
||||
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
|
||||
Reset();
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (rv == NS_ERROR_FILE_NOT_FOUND) {
|
||||
Clear();
|
||||
UpdateHeader();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
rv = ReadHeader();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("ReadCompletions"));
|
||||
rv = ReadCompletions();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Loading PrefixSet"));
|
||||
rv = LoadPrefixSet();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::Reset()
|
||||
{
|
||||
LOG(("LookupCache resetting"));
|
||||
|
||||
nsCOMPtr<nsIFile> storeFile;
|
||||
nsCOMPtr<nsIFile> prefixsetFile;
|
||||
nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mStoreDirectory->Clone(getter_AddRefs(prefixsetFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = prefixsetFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = storeFile->Remove(false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = prefixsetFile->Remove(false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
Clear();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
LookupCache::Build(const AddPrefixArray& aAddPrefixes,
|
||||
const AddCompleteArray& aAddCompletes)
|
||||
{
|
||||
mCompletions.Clear();
|
||||
mCompletions.SetCapacity(aAddCompletes.Length());
|
||||
for (uint32 i = 0; i < aAddCompletes.Length(); i++) {
|
||||
mCompletions.AppendElement(aAddCompletes[i].CompleteHash());
|
||||
}
|
||||
mCompletions.Sort();
|
||||
|
||||
Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_COMPLETIONS,
|
||||
static_cast<PRUint32>(mCompletions.Length()));
|
||||
|
||||
nsresult rv = ConstructPrefixSet(aAddPrefixes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mPrimed = true;
|
||||
|
||||
Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_PREFIXES,
|
||||
static_cast<PRUint32>(aAddPrefixes.Length()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#if defined(DEBUG) && defined(PR_LOGGING)
|
||||
void
|
||||
LookupCache::Dump()
|
||||
{
|
||||
if (!LOG_ENABLED())
|
||||
return;
|
||||
|
||||
for (uint32 i = 0; i < mCompletions.Length(); i++) {
|
||||
nsCAutoString str;
|
||||
mCompletions[i].ToString(str);
|
||||
LOG(("Completion: %s", str.get()));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
LookupCache::Has(const Completion& aCompletion,
|
||||
const Completion& aHostkey,
|
||||
const PRUint32 aHashKey,
|
||||
bool* aHas, bool* aComplete,
|
||||
Prefix* aOrigPrefix)
|
||||
{
|
||||
*aHas = *aComplete = false;
|
||||
|
||||
// check completion store first
|
||||
if (mCompletions.BinaryIndexOf(aCompletion) != nsTArray<Completion>::NoIndex) {
|
||||
LOG(("Complete in %s", mTableName.get()));
|
||||
*aComplete = true;
|
||||
*aHas = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRUint32 prefix = aCompletion.ToUint32();
|
||||
PRUint32 hostkey = aHostkey.ToUint32();
|
||||
PRUint32 codedkey;
|
||||
nsresult rv = KeyedHash(prefix, hostkey, aHashKey, &codedkey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
Prefix codedPrefix;
|
||||
codedPrefix.FromUint32(codedkey);
|
||||
*aOrigPrefix = codedPrefix;
|
||||
|
||||
bool ready = true;
|
||||
bool found;
|
||||
rv = mPrefixSet->Probe(codedkey, &ready, &found);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Probe in %s: %X, ready: %d found %d", mTableName.get(), prefix, ready, found));
|
||||
|
||||
if (found) {
|
||||
*aHas = true;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::WriteFile()
|
||||
{
|
||||
nsCOMPtr<nsIFile> storeFile;
|
||||
nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIOutputStream> out;
|
||||
rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out), storeFile,
|
||||
PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
UpdateHeader();
|
||||
LOG(("Writing %d completions", mHeader.numCompletions));
|
||||
|
||||
PRUint32 written;
|
||||
rv = out->Write(reinterpret_cast<char*>(&mHeader), sizeof(mHeader), &written);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = WriteTArray(out, mCompletions);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsISafeOutputStream> safeOut = do_QueryInterface(out);
|
||||
rv = safeOut->Finish();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = EnsureSizeConsistent();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Reopen the file now that we've rewritten it.
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(mInputStream), storeFile,
|
||||
PR_RDONLY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIFile> psFile;
|
||||
rv = mStoreDirectory->Clone(getter_AddRefs(psFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = psFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mPrefixSet->StoreToFile(psFile);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store the prefixset");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
LookupCache::Clear()
|
||||
{
|
||||
mCompletions.Clear();
|
||||
mPrefixSet->SetPrefixes(nsnull, 0);
|
||||
mPrimed = false;
|
||||
}
|
||||
|
||||
void
|
||||
LookupCache::UpdateHeader()
|
||||
{
|
||||
mHeader.magic = LOOKUPCACHE_MAGIC;
|
||||
mHeader.version = CURRENT_VERSION;
|
||||
mHeader.numCompletions = mCompletions.Length();
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::EnsureSizeConsistent()
|
||||
{
|
||||
nsCOMPtr<nsIFile> storeFile;
|
||||
nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRInt64 fileSize;
|
||||
rv = storeFile->GetFileSize(&fileSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (fileSize < 0) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
PRInt64 expectedSize = sizeof(mHeader)
|
||||
+ mHeader.numCompletions*sizeof(Completion);
|
||||
if (expectedSize != fileSize) {
|
||||
NS_WARNING("File length does not match. Probably corrupted.");
|
||||
Reset();
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::ReadHeader()
|
||||
{
|
||||
if (!mInputStream) {
|
||||
Clear();
|
||||
UpdateHeader();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
|
||||
nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
void *buffer = &mHeader;
|
||||
rv = NS_ReadInputStreamToBuffer(mInputStream,
|
||||
&buffer,
|
||||
sizeof(Header));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (mHeader.magic != LOOKUPCACHE_MAGIC || mHeader.version != CURRENT_VERSION) {
|
||||
NS_WARNING("Unexpected header data in the store.");
|
||||
Reset();
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
}
|
||||
LOG(("%d completions present", mHeader.numCompletions));
|
||||
|
||||
rv = EnsureSizeConsistent();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::ReadCompletions()
|
||||
{
|
||||
if (!mHeader.numCompletions) {
|
||||
mCompletions.Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
|
||||
nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, sizeof(Header));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = ReadTArray(mInputStream, &mCompletions, mHeader.numCompletions);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LOG(("Read %d completions", mCompletions.Length()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
LookupCache::IsCanonicalizedIP(const nsACString& aHost)
|
||||
{
|
||||
// The canonicalization process will have left IP addresses in dotted
|
||||
// decimal with no surprises.
|
||||
PRUint32 i1, i2, i3, i4;
|
||||
char c;
|
||||
if (PR_sscanf(PromiseFlatCString(aHost).get(), "%u.%u.%u.%u%c",
|
||||
&i1, &i2, &i3, &i4, &c) == 4) {
|
||||
return (i1 <= 0xFF && i2 <= 0xFF && i3 <= 0xFF && i4 <= 0xFF);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
LookupCache::GetKey(const nsACString& aSpec,
|
||||
Completion* aHash,
|
||||
nsCOMPtr<nsICryptoHash>& aCryptoHash)
|
||||
{
|
||||
nsACString::const_iterator begin, end, iter;
|
||||
aSpec.BeginReading(begin);
|
||||
aSpec.EndReading(end);
|
||||
|
||||
iter = begin;
|
||||
if (!FindCharInReadable('/', iter, end)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const nsCSubstring& host = Substring(begin, iter);
|
||||
|
||||
if (IsCanonicalizedIP(host)) {
|
||||
nsCAutoString key;
|
||||
key.Assign(host);
|
||||
key.Append("/");
|
||||
return aHash->FromPlaintext(key, aCryptoHash);
|
||||
}
|
||||
|
||||
nsTArray<nsCString> hostComponents;
|
||||
ParseString(PromiseFlatCString(host), '.', hostComponents);
|
||||
|
||||
if (hostComponents.Length() < 2)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PRInt32 last = PRInt32(hostComponents.Length()) - 1;
|
||||
nsCAutoString lookupHost;
|
||||
|
||||
if (hostComponents.Length() > 2) {
|
||||
lookupHost.Append(hostComponents[last - 2]);
|
||||
lookupHost.Append(".");
|
||||
}
|
||||
|
||||
lookupHost.Append(hostComponents[last - 1]);
|
||||
lookupHost.Append(".");
|
||||
lookupHost.Append(hostComponents[last]);
|
||||
lookupHost.Append("/");
|
||||
|
||||
return aHash->FromPlaintext(lookupHost, aCryptoHash);
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
LookupCache::GetLookupFragments(const nsACString& aSpec,
|
||||
nsTArray<nsCString>* aFragments)
|
||||
|
||||
{
|
||||
aFragments->Clear();
|
||||
|
||||
nsACString::const_iterator begin, end, iter;
|
||||
aSpec.BeginReading(begin);
|
||||
aSpec.EndReading(end);
|
||||
|
||||
iter = begin;
|
||||
if (!FindCharInReadable('/', iter, end)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const nsCSubstring& host = Substring(begin, iter++);
|
||||
nsCAutoString path;
|
||||
path.Assign(Substring(iter, end));
|
||||
|
||||
/**
|
||||
* From the protocol doc:
|
||||
* For the hostname, the client will try at most 5 different strings. They
|
||||
* are:
|
||||
* a) The exact hostname of the url
|
||||
* b) The 4 hostnames formed by starting with the last 5 components and
|
||||
* successivly removing the leading component. The top-level component
|
||||
* can be skipped. This is not done if the hostname is a numerical IP.
|
||||
*/
|
||||
nsTArray<nsCString> hosts;
|
||||
hosts.AppendElement(host);
|
||||
|
||||
if (!IsCanonicalizedIP(host)) {
|
||||
host.BeginReading(begin);
|
||||
host.EndReading(end);
|
||||
int numHostComponents = 0;
|
||||
while (RFindInReadable(NS_LITERAL_CSTRING("."), begin, end) &&
|
||||
numHostComponents < MAX_HOST_COMPONENTS) {
|
||||
// don't bother checking toplevel domains
|
||||
if (++numHostComponents >= 2) {
|
||||
host.EndReading(iter);
|
||||
hosts.AppendElement(Substring(end, iter));
|
||||
}
|
||||
end = begin;
|
||||
host.BeginReading(begin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* From the protocol doc:
|
||||
* For the path, the client will also try at most 6 different strings.
|
||||
* They are:
|
||||
* a) the exact path of the url, including query parameters
|
||||
* b) the exact path of the url, without query parameters
|
||||
* c) the 4 paths formed by starting at the root (/) and
|
||||
* successively appending path components, including a trailing
|
||||
* slash. This behavior should only extend up to the next-to-last
|
||||
* path component, that is, a trailing slash should never be
|
||||
* appended that was not present in the original url.
|
||||
*/
|
||||
nsTArray<nsCString> paths;
|
||||
nsCAutoString pathToAdd;
|
||||
|
||||
path.BeginReading(begin);
|
||||
path.EndReading(end);
|
||||
iter = begin;
|
||||
if (FindCharInReadable('?', iter, end)) {
|
||||
pathToAdd = Substring(begin, iter);
|
||||
paths.AppendElement(pathToAdd);
|
||||
end = iter;
|
||||
}
|
||||
|
||||
int numPathComponents = 1;
|
||||
iter = begin;
|
||||
while (FindCharInReadable('/', iter, end) &&
|
||||
numPathComponents < MAX_PATH_COMPONENTS) {
|
||||
iter++;
|
||||
pathToAdd.Assign(Substring(begin, iter));
|
||||
paths.AppendElement(pathToAdd);
|
||||
numPathComponents++;
|
||||
}
|
||||
|
||||
// If we haven't already done so, add the full path
|
||||
if (!pathToAdd.Equals(path)) {
|
||||
paths.AppendElement(path);
|
||||
}
|
||||
// Check an empty path (for whole-domain blacklist entries)
|
||||
paths.AppendElement(EmptyCString());
|
||||
|
||||
for (PRUint32 hostIndex = 0; hostIndex < hosts.Length(); hostIndex++) {
|
||||
for (PRUint32 pathIndex = 0; pathIndex < paths.Length(); pathIndex++) {
|
||||
nsCString key;
|
||||
key.Assign(hosts[hostIndex]);
|
||||
key.Append('/');
|
||||
key.Append(paths[pathIndex]);
|
||||
LOG(("Chking %s", key.get()));
|
||||
|
||||
aFragments->AppendElement(key);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
LookupCache::GetHostKeys(const nsACString& aSpec,
|
||||
nsTArray<nsCString>* aHostKeys)
|
||||
{
|
||||
nsACString::const_iterator begin, end, iter;
|
||||
aSpec.BeginReading(begin);
|
||||
aSpec.EndReading(end);
|
||||
|
||||
iter = begin;
|
||||
if (!FindCharInReadable('/', iter, end)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const nsCSubstring& host = Substring(begin, iter);
|
||||
|
||||
if (IsCanonicalizedIP(host)) {
|
||||
nsCString *key = aHostKeys->AppendElement();
|
||||
if (!key)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
key->Assign(host);
|
||||
key->Append("/");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsTArray<nsCString> hostComponents;
|
||||
ParseString(PromiseFlatCString(host), '.', hostComponents);
|
||||
|
||||
if (hostComponents.Length() < 2) {
|
||||
// no host or toplevel host, this won't match anything in the db
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// First check with two domain components
|
||||
PRInt32 last = PRInt32(hostComponents.Length()) - 1;
|
||||
nsCString *lookupHost = aHostKeys->AppendElement();
|
||||
if (!lookupHost)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
lookupHost->Assign(hostComponents[last - 1]);
|
||||
lookupHost->Append(".");
|
||||
lookupHost->Append(hostComponents[last]);
|
||||
lookupHost->Append("/");
|
||||
|
||||
// Now check with three domain components
|
||||
if (hostComponents.Length() > 2) {
|
||||
nsCString *lookupHost2 = aHostKeys->AppendElement();
|
||||
if (!lookupHost2)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
lookupHost2->Assign(hostComponents[last - 2]);
|
||||
lookupHost2->Append(".");
|
||||
lookupHost2->Append(*lookupHost);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* We have both a prefix and a domain. Drop the domain, but
|
||||
hash the domain, the prefix and a random value together,
|
||||
ensuring any collisions happens at a different points for
|
||||
different users.
|
||||
*/
|
||||
/* static */ nsresult LookupCache::KeyedHash(PRUint32 aPref, PRUint32 aDomain,
|
||||
PRUint32 aKey, PRUint32* aOut)
|
||||
{
|
||||
/* This is a reimplementation of MurmurHash3 32-bit
|
||||
based on the public domain C++ sources.
|
||||
http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp
|
||||
for nblocks = 2
|
||||
*/
|
||||
PRUint32 c1 = 0xCC9E2D51;
|
||||
PRUint32 c2 = 0x1B873593;
|
||||
PRUint32 c3 = 0xE6546B64;
|
||||
PRUint32 c4 = 0x85EBCA6B;
|
||||
PRUint32 c5 = 0xC2B2AE35;
|
||||
PRUint32 h1 = aPref; // seed
|
||||
PRUint32 k1;
|
||||
PRUint32 karr[2];
|
||||
|
||||
karr[0] = aDomain;
|
||||
karr[1] = aKey;
|
||||
|
||||
for (PRUint32 i = 0; i < 2; i++) {
|
||||
k1 = karr[i];
|
||||
k1 *= c1;
|
||||
k1 = (k1 << 15) | (k1 >> (32-15));
|
||||
k1 *= c2;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = (h1 << 13) | (h1 >> (32-13));
|
||||
h1 *= 5;
|
||||
h1 += c3;
|
||||
}
|
||||
|
||||
h1 ^= 2; // len
|
||||
// fmix
|
||||
h1 ^= h1 >> 16;
|
||||
h1 *= c4;
|
||||
h1 ^= h1 >> 13;
|
||||
h1 *= c5;
|
||||
h1 ^= h1 >> 16;
|
||||
|
||||
*aOut = h1;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool LookupCache::IsPrimed()
|
||||
{
|
||||
return mPrimed;
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::ConstructPrefixSet(const AddPrefixArray& aAddPrefixes)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_CONSTRUCT_TIME> timer;
|
||||
|
||||
nsTArray<PRUint32> array;
|
||||
array.SetCapacity(aAddPrefixes.Length());
|
||||
|
||||
for (uint32 i = 0; i < aAddPrefixes.Length(); i++) {
|
||||
array.AppendElement(aAddPrefixes[i].PrefixHash().ToUint32());
|
||||
}
|
||||
|
||||
// clear old tree
|
||||
if (array.IsEmpty()) {
|
||||
// DB is empty, but put a sentinel to show that we looked
|
||||
array.AppendElement(0);
|
||||
}
|
||||
// PrefixSet requires sorted order
|
||||
array.Sort();
|
||||
|
||||
// construct new one, replace old entries
|
||||
nsresult rv = mPrefixSet->SetPrefixes(array.Elements(), array.Length());
|
||||
if (NS_FAILED(rv)) {
|
||||
goto error_bailout;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
PRUint32 size;
|
||||
size = mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
|
||||
LOG(("SB tree done, size = %d bytes\n", size));
|
||||
#endif
|
||||
|
||||
mPrimed = true;
|
||||
|
||||
return NS_OK;
|
||||
|
||||
error_bailout:
|
||||
// load an empty prefixset so the browser can work
|
||||
nsAutoTArray<PRUint32, 1> sentinel;
|
||||
sentinel.Clear();
|
||||
sentinel.AppendElement(0);
|
||||
mPrefixSet->SetPrefixes(sentinel.Elements(), sentinel.Length());
|
||||
if (rv == NS_ERROR_OUT_OF_MEMORY) {
|
||||
Telemetry::Accumulate(Telemetry::URLCLASSIFIER_PS_OOM, 1);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::LoadPrefixSet()
|
||||
{
|
||||
nsCOMPtr<nsIFile> psFile;
|
||||
nsresult rv = mStoreDirectory->Clone(getter_AddRefs(psFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = psFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool exists;
|
||||
rv = psFile->Exists(&exists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (exists) {
|
||||
LOG(("stored PrefixSet exists, loading from disk"));
|
||||
rv = mPrefixSet->LoadFromFile(psFile);
|
||||
}
|
||||
if (!exists || NS_FAILED(rv)) {
|
||||
LOG(("no (usable) stored PrefixSet found"));
|
||||
} else {
|
||||
mPrimed = true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (mPrimed) {
|
||||
PRUint32 size = mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
|
||||
LOG(("SB tree done, size = %d bytes\n", size));
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
LookupCache::GetPrefixes(nsTArray<PRUint32>* aAddPrefixes)
|
||||
{
|
||||
if (!mPrimed) {
|
||||
// This can happen if its a new table, so no error.
|
||||
LOG(("GetPrefixes from empty LookupCache"));
|
||||
return NS_OK;
|
||||
}
|
||||
PRUint32 cnt;
|
||||
PRUint32 *arr;
|
||||
nsresult rv = mPrefixSet->GetPrefixes(&cnt, &arr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!aAddPrefixes->AppendElements(arr, cnt))
|
||||
return NS_ERROR_FAILURE;
|
||||
nsMemory::Free(arr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Url Classifier code
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dave Camp <dcamp@mozilla.com>
|
||||
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef LookupCache_h__
|
||||
#define LookupCache_h__
|
||||
|
||||
#include "Entries.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsUrlClassifierPrefixSet.h"
|
||||
#include "prlog.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
#define MAX_HOST_COMPONENTS 5
|
||||
#define MAX_PATH_COMPONENTS 4
|
||||
|
||||
class LookupResult {
|
||||
public:
|
||||
LookupResult() : mComplete(false), mNoise(false), mFresh(false), mProtocolConfirmed(false) {}
|
||||
|
||||
// The fragment that matched in the LookupCache
|
||||
union {
|
||||
Prefix prefix;
|
||||
Completion complete;
|
||||
} hash;
|
||||
|
||||
const Prefix &PrefixHash() { return hash.prefix; }
|
||||
const Completion &CompleteHash() { return hash.complete; }
|
||||
|
||||
bool Confirmed() const { return (mComplete && mFresh) || mProtocolConfirmed; }
|
||||
bool Complete() const { return mComplete; }
|
||||
|
||||
// True if we have a complete match for this hash in the table.
|
||||
bool mComplete;
|
||||
|
||||
// True if this is a noise entry, i.e. an extra entry
|
||||
// that is inserted to mask the true URL we are requesting
|
||||
bool mNoise;
|
||||
|
||||
// Value of actual key looked up in the prefixset (coded with client key)
|
||||
Prefix mCodedPrefix;
|
||||
|
||||
// True if we've updated this table recently-enough.
|
||||
bool mFresh;
|
||||
|
||||
bool mProtocolConfirmed;
|
||||
|
||||
nsCString mTableName;
|
||||
};
|
||||
|
||||
typedef nsTArray<LookupResult> LookupResultArray;
|
||||
|
||||
struct CacheResult {
|
||||
AddComplete entry;
|
||||
nsCString table;
|
||||
};
|
||||
typedef nsTArray<CacheResult> CacheResultArray;
|
||||
|
||||
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);
|
||||
// Similar to GetKey(), but if the domain contains three or more components,
|
||||
// two keys will be returned:
|
||||
// hostname.com/foo/bar -> [hostname.com]
|
||||
// mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com]
|
||||
// www.mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com]
|
||||
static nsresult GetHostKeys(const nsACString& aSpec,
|
||||
nsTArray<nsCString>* aHostKeys);
|
||||
// Get the database key for a given URI. This is the top three
|
||||
// domain components if they exist, otherwise the top two.
|
||||
// hostname.com/foo/bar -> hostname.com
|
||||
// mail.hostname.com/foo/bar -> mail.hostname.com
|
||||
// www.mail.hostname.com/foo/bar -> mail.hostname.com
|
||||
static nsresult GetKey(const nsACString& aSpec, Completion* aHash,
|
||||
nsCOMPtr<nsICryptoHash>& aCryptoHash);
|
||||
|
||||
/* We have both a prefix and a domain. Drop the domain, but
|
||||
hash the domain, the prefix and a random value together,
|
||||
ensuring any collisions happens at a different points for
|
||||
different users.
|
||||
*/
|
||||
static nsresult KeyedHash(PRUint32 aPref, PRUint32 aDomain,
|
||||
PRUint32 aKey, PRUint32* aOut);
|
||||
|
||||
LookupCache(const nsACString& aTableName, nsIFile* aStoreFile);
|
||||
~LookupCache();
|
||||
|
||||
const nsCString &TableName() const { return mTableName; }
|
||||
|
||||
nsresult Init();
|
||||
nsresult Open();
|
||||
nsresult Build(const AddPrefixArray& aAddPrefixes,
|
||||
const AddCompleteArray& aAddCompletes);
|
||||
nsresult GetPrefixes(nsTArray<PRUint32>* aAddPrefixes);
|
||||
|
||||
#if DEBUG && defined(PR_LOGGING)
|
||||
void Dump();
|
||||
#endif
|
||||
nsresult WriteFile();
|
||||
nsresult Has(const Completion& aCompletion,
|
||||
const Completion& aHostkey,
|
||||
PRUint32 aHashKey,
|
||||
bool* aHas, bool* aComplete,
|
||||
Prefix* aOrigPrefix);
|
||||
bool IsPrimed();
|
||||
|
||||
private:
|
||||
|
||||
void Clear();
|
||||
nsresult Reset();
|
||||
void UpdateHeader();
|
||||
nsresult ReadHeader();
|
||||
nsresult EnsureSizeConsistent();
|
||||
nsresult ReadCompletions();
|
||||
// Construct a Prefix Set with known prefixes
|
||||
nsresult LoadPrefixSet();
|
||||
nsresult ConstructPrefixSet(const AddPrefixArray& aAddPrefixes);
|
||||
|
||||
struct Header {
|
||||
uint32 magic;
|
||||
uint32 version;
|
||||
uint32 numCompletions;
|
||||
};
|
||||
Header mHeader;
|
||||
|
||||
bool mPrimed;
|
||||
nsCString mTableName;
|
||||
nsCOMPtr<nsIFile> mStoreDirectory;
|
||||
nsCOMPtr<nsIInputStream> mInputStream;
|
||||
CompletionArray mCompletions;
|
||||
// Set of prefixes known to be in the database
|
||||
nsRefPtr<nsUrlClassifierPrefixSet> mPrefixSet;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -59,17 +59,11 @@ XPIDLSRCS = \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
ChunkSet.cpp \
|
||||
Classifier.cpp \
|
||||
HashStore.cpp \
|
||||
ProtocolParser.cpp \
|
||||
LookupCache.cpp \
|
||||
nsUrlClassifierDBService.cpp \
|
||||
nsUrlClassifierStreamUpdater.cpp \
|
||||
nsUrlClassifierUtils.cpp \
|
||||
nsUrlClassifierPrefixSet.cpp \
|
||||
nsUrlClassifierProxies.cpp \
|
||||
nsCheckSummedOutputStream.cpp \
|
||||
$(NULL)
|
||||
|
||||
LOCAL_INCLUDES = \
|
||||
|
@ -1,777 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Url Classifier code
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dave Camp <dcamp@mozilla.com>
|
||||
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
#include "ProtocolParser.h"
|
||||
#include "LookupCache.h"
|
||||
#include "nsIKeyModule.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "prlog.h"
|
||||
#include "prnetdb.h"
|
||||
#include "prprf.h"
|
||||
|
||||
#include "nsUrlClassifierUtils.h"
|
||||
|
||||
// NSPR_LOG_MODULES=UrlClassifierDbService:5
|
||||
extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
|
||||
#if defined(PR_LOGGING)
|
||||
#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
|
||||
#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#define LOG_ENABLED() (PR_FALSE)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
// Updates will fail if fed chunks larger than this
|
||||
const uint32 MAX_CHUNK_SIZE = (1024 * 1024);
|
||||
|
||||
const uint32 DOMAIN_SIZE = 4;
|
||||
|
||||
// Parse one stringified range of chunks of the form "n" or "n-m" from a
|
||||
// comma-separated list of chunks. Upon return, 'begin' will point to the
|
||||
// next range of chunks in the list of chunks.
|
||||
static bool
|
||||
ParseChunkRange(nsACString::const_iterator& aBegin,
|
||||
const nsACString::const_iterator& aEnd,
|
||||
PRUint32* aFirst, PRUint32* aLast)
|
||||
{
|
||||
nsACString::const_iterator iter = aBegin;
|
||||
FindCharInReadable(',', iter, aEnd);
|
||||
|
||||
nsCAutoString element(Substring(aBegin, iter));
|
||||
aBegin = iter;
|
||||
if (aBegin != aEnd)
|
||||
aBegin++;
|
||||
|
||||
PRUint32 numRead = PR_sscanf(element.get(), "%u-%u", aFirst, aLast);
|
||||
if (numRead == 2) {
|
||||
if (*aFirst > *aLast) {
|
||||
PRUint32 tmp = *aFirst;
|
||||
*aFirst = *aLast;
|
||||
*aLast = tmp;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (numRead == 1) {
|
||||
*aLast = *aFirst;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ProtocolParser::ProtocolParser(PRUint32 aHashKey)
|
||||
: mState(PROTOCOL_STATE_CONTROL)
|
||||
, mHashKey(aHashKey)
|
||||
, mUpdateStatus(NS_OK)
|
||||
, mUpdateWait(0)
|
||||
, mResetRequested(false)
|
||||
, mRekeyRequested(false)
|
||||
{
|
||||
}
|
||||
|
||||
ProtocolParser::~ProtocolParser()
|
||||
{
|
||||
CleanupUpdates();
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::Init(nsICryptoHash* aHasher)
|
||||
{
|
||||
mCryptoHash = aHasher;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize HMAC for the stream.
|
||||
*
|
||||
* If serverMAC is empty, the update stream will need to provide a
|
||||
* server MAC.
|
||||
*/
|
||||
nsresult
|
||||
ProtocolParser::InitHMAC(const nsACString& aClientKey,
|
||||
const nsACString& aServerMAC)
|
||||
{
|
||||
mServerMAC = aServerMAC;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIKeyObjectFactory> keyObjectFactory(
|
||||
do_GetService("@mozilla.org/security/keyobjectfactory;1", &rv));
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to get nsIKeyObjectFactory service");
|
||||
mUpdateStatus = rv;
|
||||
return mUpdateStatus;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIKeyObject> keyObject;
|
||||
rv = keyObjectFactory->KeyFromString(nsIKeyObject::HMAC, aClientKey,
|
||||
getter_AddRefs(keyObject));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to create key object, maybe not FIPS compliant?");
|
||||
mUpdateStatus = rv;
|
||||
return mUpdateStatus;
|
||||
}
|
||||
|
||||
mHMAC = do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to create nsICryptoHMAC instance");
|
||||
mUpdateStatus = rv;
|
||||
return mUpdateStatus;
|
||||
}
|
||||
|
||||
rv = mHMAC->Init(nsICryptoHMAC::SHA1, keyObject);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to initialize nsICryptoHMAC instance");
|
||||
mUpdateStatus = rv;
|
||||
return mUpdateStatus;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::FinishHMAC()
|
||||
{
|
||||
if (NS_FAILED(mUpdateStatus)) {
|
||||
return mUpdateStatus;
|
||||
}
|
||||
|
||||
if (mRekeyRequested) {
|
||||
mUpdateStatus = NS_ERROR_FAILURE;
|
||||
return mUpdateStatus;
|
||||
}
|
||||
|
||||
if (!mHMAC) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCAutoString clientMAC;
|
||||
mHMAC->Finish(PR_TRUE, clientMAC);
|
||||
|
||||
if (clientMAC != mServerMAC) {
|
||||
NS_WARNING("Invalid update MAC!");
|
||||
LOG(("Invalid update MAC: expected %s, got %s",
|
||||
clientMAC.get(), mServerMAC.get()));
|
||||
mUpdateStatus = NS_ERROR_FAILURE;
|
||||
}
|
||||
return mUpdateStatus;
|
||||
}
|
||||
|
||||
void
|
||||
ProtocolParser::SetCurrentTable(const nsACString& aTable)
|
||||
{
|
||||
mTableUpdate = GetTableUpdate(aTable);
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::AppendStream(const nsACString& aData)
|
||||
{
|
||||
if (NS_FAILED(mUpdateStatus))
|
||||
return mUpdateStatus;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
// Digest the data if we have a server MAC.
|
||||
if (mHMAC && !mServerMAC.IsEmpty()) {
|
||||
rv = mHMAC->Update(reinterpret_cast<const PRUint8*>(aData.BeginReading()),
|
||||
aData.Length());
|
||||
if (NS_FAILED(rv)) {
|
||||
mUpdateStatus = rv;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
mPending.Append(aData);
|
||||
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
if (mState == PROTOCOL_STATE_CONTROL) {
|
||||
rv = ProcessControl(&done);
|
||||
} else if (mState == PROTOCOL_STATE_CHUNK) {
|
||||
rv = ProcessChunk(&done);
|
||||
} else {
|
||||
NS_ERROR("Unexpected protocol state");
|
||||
rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
mUpdateStatus = rv;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessControl(bool* aDone)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCAutoString line;
|
||||
*aDone = true;
|
||||
while (NextLine(line)) {
|
||||
//LOG(("Processing %s\n", line.get()));
|
||||
|
||||
if (line.EqualsLiteral("e:pleaserekey")) {
|
||||
mRekeyRequested = true;
|
||||
return NS_OK;
|
||||
} else if (mHMAC && mServerMAC.IsEmpty()) {
|
||||
rv = ProcessMAC(line);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (StringBeginsWith(line, NS_LITERAL_CSTRING("i:"))) {
|
||||
SetCurrentTable(Substring(line, 2));
|
||||
} else if (StringBeginsWith(line, NS_LITERAL_CSTRING("n:"))) {
|
||||
if (PR_sscanf(line.get(), "n:%d", &mUpdateWait) != 1) {
|
||||
LOG(("Error parsing n: '%s' (%d)", line.get(), mUpdateWait));
|
||||
mUpdateWait = 0;
|
||||
}
|
||||
} else if (line.EqualsLiteral("r:pleasereset")) {
|
||||
mResetRequested = true;
|
||||
} else if (StringBeginsWith(line, NS_LITERAL_CSTRING("u:"))) {
|
||||
rv = ProcessForward(line);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (StringBeginsWith(line, NS_LITERAL_CSTRING("a:")) ||
|
||||
StringBeginsWith(line, NS_LITERAL_CSTRING("s:"))) {
|
||||
rv = ProcessChunkControl(line);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
*aDone = false;
|
||||
return NS_OK;
|
||||
} else if (StringBeginsWith(line, NS_LITERAL_CSTRING("ad:")) ||
|
||||
StringBeginsWith(line, NS_LITERAL_CSTRING("sd:"))) {
|
||||
rv = ProcessExpirations(line);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
*aDone = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessMAC(const nsCString& aLine)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
LOG(("line: %s", aLine.get()));
|
||||
|
||||
if (StringBeginsWith(aLine, NS_LITERAL_CSTRING("m:"))) {
|
||||
mServerMAC = Substring(aLine, 2);
|
||||
nsUrlClassifierUtils::UnUrlsafeBase64(mServerMAC);
|
||||
|
||||
// The remainder of the pending update wasn't digested, digest it now.
|
||||
rv = mHMAC->Update(reinterpret_cast<const PRUint8*>(mPending.BeginReading()),
|
||||
mPending.Length());
|
||||
return rv;
|
||||
}
|
||||
|
||||
LOG(("No MAC specified!"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessExpirations(const nsCString& aLine)
|
||||
{
|
||||
if (!mTableUpdate) {
|
||||
NS_WARNING("Got an expiration without a table.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
const nsCSubstring &list = Substring(aLine, 3);
|
||||
nsACString::const_iterator begin, end;
|
||||
list.BeginReading(begin);
|
||||
list.EndReading(end);
|
||||
while (begin != end) {
|
||||
PRUint32 first, last;
|
||||
if (ParseChunkRange(begin, end, &first, &last)) {
|
||||
for (PRUint32 num = first; num <= last; num++) {
|
||||
if (aLine[0] == 'a')
|
||||
mTableUpdate->NewAddExpiration(num);
|
||||
else
|
||||
mTableUpdate->NewSubExpiration(num);
|
||||
}
|
||||
} else {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessChunkControl(const nsCString& aLine)
|
||||
{
|
||||
if (!mTableUpdate) {
|
||||
NS_WARNING("Got a chunk before getting a table.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mState = PROTOCOL_STATE_CHUNK;
|
||||
char command;
|
||||
|
||||
mChunkState.Clear();
|
||||
|
||||
if (PR_sscanf(aLine.get(),
|
||||
"%c:%d:%d:%d",
|
||||
&command,
|
||||
&mChunkState.num, &mChunkState.hashSize, &mChunkState.length)
|
||||
!= 4)
|
||||
{
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (mChunkState.length > MAX_CHUNK_SIZE) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!(mChunkState.hashSize == PREFIX_SIZE || mChunkState.hashSize == COMPLETE_SIZE)) {
|
||||
NS_WARNING("Invalid hash size specified in update.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mChunkState.type = (command == 'a') ? CHUNK_ADD : CHUNK_SUB;
|
||||
|
||||
if (mChunkState.type == CHUNK_ADD) {
|
||||
mTableUpdate->NewAddChunk(mChunkState.num);
|
||||
} else {
|
||||
mTableUpdate->NewSubChunk(mChunkState.num);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessForward(const nsCString& aLine)
|
||||
{
|
||||
const nsCSubstring &forward = Substring(aLine, 2);
|
||||
if (mHMAC) {
|
||||
// We're expecting MACs alongside any url forwards.
|
||||
nsCSubstring::const_iterator begin, end, sepBegin, sepEnd;
|
||||
forward.BeginReading(begin);
|
||||
sepBegin = begin;
|
||||
|
||||
forward.EndReading(end);
|
||||
sepEnd = end;
|
||||
|
||||
if (!RFindInReadable(NS_LITERAL_CSTRING(","), sepBegin, sepEnd)) {
|
||||
NS_WARNING("No MAC specified for a redirect in a request that expects a MAC");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCString serverMAC(Substring(sepEnd, end));
|
||||
nsUrlClassifierUtils::UnUrlsafeBase64(serverMAC);
|
||||
return AddForward(Substring(begin, sepBegin), serverMAC);
|
||||
}
|
||||
return AddForward(forward, mServerMAC);
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::AddForward(const nsACString& aUrl, const nsACString& aMac)
|
||||
{
|
||||
if (!mTableUpdate) {
|
||||
NS_WARNING("Forward without a table name.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
ForwardedUpdate *forward = mForwards.AppendElement();
|
||||
forward->table = mTableUpdate->TableName();
|
||||
forward->url.Assign(aUrl);
|
||||
forward->mac.Assign(aMac);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessChunk(bool* aDone)
|
||||
{
|
||||
if (!mTableUpdate) {
|
||||
NS_WARNING("Processing chunk without an active table.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_ASSERTION(mChunkState.num != 0, "Must have a chunk number.");
|
||||
|
||||
if (mPending.Length() < mChunkState.length) {
|
||||
*aDone = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Pull the chunk out of the pending stream data.
|
||||
nsCAutoString chunk;
|
||||
chunk.Assign(Substring(mPending, 0, mChunkState.length));
|
||||
mPending = Substring(mPending, mChunkState.length);
|
||||
|
||||
*aDone = false;
|
||||
mState = PROTOCOL_STATE_CONTROL;
|
||||
|
||||
//LOG(("Handling a %d-byte chunk", chunk.Length()));
|
||||
if (StringEndsWith(mTableUpdate->TableName(), NS_LITERAL_CSTRING("-shavar"))) {
|
||||
return ProcessShaChunk(chunk);
|
||||
} else {
|
||||
return ProcessPlaintextChunk(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a plaintext chunk (currently only used in unit tests).
|
||||
*/
|
||||
nsresult
|
||||
ProtocolParser::ProcessPlaintextChunk(const nsACString& aChunk)
|
||||
{
|
||||
if (!mTableUpdate) {
|
||||
NS_WARNING("Chunk received with no table.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsTArray<nsCString> lines;
|
||||
ParseString(PromiseFlatCString(aChunk), '\n', lines);
|
||||
|
||||
// non-hashed tables need to be hashed
|
||||
for (uint32 i = 0; i < lines.Length(); i++) {
|
||||
nsCString& line = lines[i];
|
||||
|
||||
if (mChunkState.type == CHUNK_ADD) {
|
||||
if (mChunkState.hashSize == COMPLETE_SIZE) {
|
||||
Completion hash;
|
||||
hash.FromPlaintext(line, mCryptoHash);
|
||||
mTableUpdate->NewAddComplete(mChunkState.num, hash);
|
||||
} else {
|
||||
NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks.");
|
||||
Completion hash;
|
||||
Completion domHash;
|
||||
Prefix newHash;
|
||||
rv = LookupCache::GetKey(line, &domHash, mCryptoHash);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
hash.FromPlaintext(line, mCryptoHash);
|
||||
PRUint32 codedHash;
|
||||
rv = LookupCache::KeyedHash(hash.ToUint32(), domHash.ToUint32(), mHashKey, &codedHash);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
newHash.FromUint32(codedHash);
|
||||
mTableUpdate->NewAddPrefix(mChunkState.num, newHash);
|
||||
}
|
||||
} else {
|
||||
nsCString::const_iterator begin, iter, end;
|
||||
line.BeginReading(begin);
|
||||
line.EndReading(end);
|
||||
iter = begin;
|
||||
uint32 addChunk;
|
||||
if (!FindCharInReadable(':', iter, end) ||
|
||||
PR_sscanf(lines[i].get(), "%d:", &addChunk) != 1) {
|
||||
NS_WARNING("Received sub chunk without associated add chunk.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
iter++;
|
||||
|
||||
if (mChunkState.hashSize == COMPLETE_SIZE) {
|
||||
Completion hash;
|
||||
hash.FromPlaintext(Substring(iter, end), mCryptoHash);
|
||||
mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num);
|
||||
} else {
|
||||
NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks.");
|
||||
Prefix hash;
|
||||
Completion domHash;
|
||||
Prefix newHash;
|
||||
rv = LookupCache::GetKey(Substring(iter, end), &domHash, mCryptoHash);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
hash.FromPlaintext(Substring(iter, end), mCryptoHash);
|
||||
PRUint32 codedHash;
|
||||
rv = LookupCache::KeyedHash(hash.ToUint32(), domHash.ToUint32(), mHashKey, &codedHash);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
newHash.FromUint32(codedHash);
|
||||
mTableUpdate->NewSubPrefix(addChunk, newHash, mChunkState.num);
|
||||
// Needed to knock out completes
|
||||
// Fake chunk nr, will cause it to be removed next update
|
||||
mTableUpdate->NewSubPrefix(addChunk, hash, 0);
|
||||
mTableUpdate->NewSubChunk(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessShaChunk(const nsACString& aChunk)
|
||||
{
|
||||
PRUint32 start = 0;
|
||||
while (start < aChunk.Length()) {
|
||||
// First four bytes are the domain key.
|
||||
Prefix domain;
|
||||
domain.Assign(Substring(aChunk, start, DOMAIN_SIZE));
|
||||
start += DOMAIN_SIZE;
|
||||
|
||||
// Then a count of entries.
|
||||
uint8 numEntries = static_cast<uint8>(aChunk[start]);
|
||||
start++;
|
||||
|
||||
nsresult rv;
|
||||
if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == PREFIX_SIZE) {
|
||||
rv = ProcessHostAdd(domain, numEntries, aChunk, &start);
|
||||
} else if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == COMPLETE_SIZE) {
|
||||
rv = ProcessHostAddComplete(numEntries, aChunk, &start);
|
||||
} else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == PREFIX_SIZE) {
|
||||
rv = ProcessHostSub(domain, numEntries, aChunk, &start);
|
||||
} else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == COMPLETE_SIZE) {
|
||||
rv = ProcessHostSubComplete(numEntries, aChunk, &start);
|
||||
} else {
|
||||
NS_WARNING("Unexpected chunk type/hash size!");
|
||||
LOG(("Got an unexpected chunk type/hash size: %s:%d",
|
||||
mChunkState.type == CHUNK_ADD ? "add" : "sub",
|
||||
mChunkState.hashSize));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessHostAdd(const Prefix& aDomain, PRUint8 aNumEntries,
|
||||
const nsACString& aChunk, PRUint32* aStart)
|
||||
{
|
||||
NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE,
|
||||
"ProcessHostAdd should only be called for prefix hashes.");
|
||||
|
||||
PRUint32 codedHash;
|
||||
PRUint32 domHash = aDomain.ToUint32();
|
||||
|
||||
if (aNumEntries == 0) {
|
||||
nsresult rv = LookupCache::KeyedHash(domHash, domHash, mHashKey, &codedHash);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
Prefix newHash;
|
||||
newHash.FromUint32(codedHash);
|
||||
mTableUpdate->NewAddPrefix(mChunkState.num, newHash);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (*aStart + (PREFIX_SIZE * aNumEntries) > aChunk.Length()) {
|
||||
NS_WARNING("Chunk is not long enough to contain the expected entries.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
for (uint8 i = 0; i < aNumEntries; i++) {
|
||||
Prefix hash;
|
||||
hash.Assign(Substring(aChunk, *aStart, PREFIX_SIZE));
|
||||
nsresult rv = LookupCache::KeyedHash(domHash, hash.ToUint32(), mHashKey, &codedHash);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
Prefix newHash;
|
||||
newHash.FromUint32(codedHash);
|
||||
mTableUpdate->NewAddPrefix(mChunkState.num, newHash);
|
||||
*aStart += PREFIX_SIZE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessHostSub(const Prefix& aDomain, PRUint8 aNumEntries,
|
||||
const nsACString& aChunk, PRUint32 *aStart)
|
||||
{
|
||||
NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE,
|
||||
"ProcessHostSub should only be called for prefix hashes.");
|
||||
|
||||
PRUint32 codedHash;
|
||||
PRUint32 domHash = aDomain.ToUint32();
|
||||
|
||||
if (aNumEntries == 0) {
|
||||
if ((*aStart) + 4 > aChunk.Length()) {
|
||||
NS_WARNING("Received a zero-entry sub chunk without an associated add.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4);
|
||||
*aStart += 4;
|
||||
|
||||
uint32 addChunk;
|
||||
memcpy(&addChunk, addChunkStr.BeginReading(), 4);
|
||||
addChunk = PR_ntohl(addChunk);
|
||||
|
||||
nsresult rv = LookupCache::KeyedHash(domHash, domHash, mHashKey, &codedHash);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
Prefix newHash;
|
||||
newHash.FromUint32(codedHash);
|
||||
|
||||
mTableUpdate->NewSubPrefix(addChunk, newHash, mChunkState.num);
|
||||
// Needed to knock out completes
|
||||
// Fake chunk nr, will cause it to be removed next update
|
||||
mTableUpdate->NewSubPrefix(addChunk, aDomain, 0);
|
||||
mTableUpdate->NewSubChunk(0);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (*aStart + ((PREFIX_SIZE + 4) * aNumEntries) > aChunk.Length()) {
|
||||
NS_WARNING("Chunk is not long enough to contain the expected entries.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
for (uint8 i = 0; i < aNumEntries; i++) {
|
||||
const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4);
|
||||
*aStart += 4;
|
||||
|
||||
uint32 addChunk;
|
||||
memcpy(&addChunk, addChunkStr.BeginReading(), 4);
|
||||
addChunk = PR_ntohl(addChunk);
|
||||
|
||||
Prefix prefix;
|
||||
prefix.Assign(Substring(aChunk, *aStart, PREFIX_SIZE));
|
||||
*aStart += PREFIX_SIZE;
|
||||
|
||||
nsresult rv = LookupCache::KeyedHash(prefix.ToUint32(), domHash, mHashKey, &codedHash);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
Prefix newHash;
|
||||
newHash.FromUint32(codedHash);
|
||||
|
||||
mTableUpdate->NewSubPrefix(addChunk, newHash, mChunkState.num);
|
||||
// Needed to knock out completes
|
||||
// Fake chunk nr, will cause it to be removed next update
|
||||
mTableUpdate->NewSubPrefix(addChunk, prefix, 0);
|
||||
mTableUpdate->NewSubChunk(0);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessHostAddComplete(PRUint8 aNumEntries,
|
||||
const nsACString& aChunk, PRUint32* aStart)
|
||||
{
|
||||
NS_ASSERTION(mChunkState.hashSize == COMPLETE_SIZE,
|
||||
"ProcessHostAddComplete should only be called for complete hashes.");
|
||||
|
||||
if (aNumEntries == 0) {
|
||||
// this is totally comprehensible.
|
||||
NS_WARNING("Expected > 0 entries for a 32-byte hash add.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (*aStart + (COMPLETE_SIZE * aNumEntries) > aChunk.Length()) {
|
||||
NS_WARNING("Chunk is not long enough to contain the expected entries.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
for (uint8 i = 0; i < aNumEntries; i++) {
|
||||
Completion hash;
|
||||
hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE));
|
||||
mTableUpdate->NewAddComplete(mChunkState.num, hash);
|
||||
*aStart += COMPLETE_SIZE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ProtocolParser::ProcessHostSubComplete(PRUint8 aNumEntries,
|
||||
const nsACString& aChunk, PRUint32* aStart)
|
||||
{
|
||||
NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE,
|
||||
"ProcessHostSub should only be called for prefix hashes.");
|
||||
|
||||
if (aNumEntries == 0) {
|
||||
// this is totally comprehensible.
|
||||
NS_WARNING("Expected > 0 entries for a 32-byte hash add.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (*aStart + ((COMPLETE_SIZE + 4) * aNumEntries) > aChunk.Length()) {
|
||||
NS_WARNING("Chunk is not long enough to contain the expected entries.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
for (PRUint8 i = 0; i < aNumEntries; i++) {
|
||||
Completion hash;
|
||||
hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE));
|
||||
*aStart += COMPLETE_SIZE;
|
||||
|
||||
const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4);
|
||||
*aStart += 4;
|
||||
|
||||
uint32 addChunk;
|
||||
memcpy(&addChunk, addChunkStr.BeginReading(), 4);
|
||||
addChunk = PR_ntohl(addChunk);
|
||||
|
||||
mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
ProtocolParser::NextLine(nsACString& line)
|
||||
{
|
||||
int32 newline = mPending.FindChar('\n');
|
||||
if (newline == kNotFound) {
|
||||
return false;
|
||||
}
|
||||
line.Assign(Substring(mPending, 0, newline));
|
||||
mPending = Substring(mPending, newline + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ProtocolParser::CleanupUpdates()
|
||||
{
|
||||
for (uint32 i = 0; i < mTableUpdates.Length(); i++) {
|
||||
delete mTableUpdates[i];
|
||||
}
|
||||
mTableUpdates.Clear();
|
||||
}
|
||||
|
||||
TableUpdate *
|
||||
ProtocolParser::GetTableUpdate(const nsACString& aTable)
|
||||
{
|
||||
for (uint32 i = 0; i < mTableUpdates.Length(); i++) {
|
||||
if (aTable.Equals(mTableUpdates[i]->TableName())) {
|
||||
return mTableUpdates[i];
|
||||
}
|
||||
}
|
||||
|
||||
// We free automatically on destruction, ownership of these
|
||||
// updates can be transferred to DBServiceWorker, which passes
|
||||
// them back to Classifier when doing the updates, and that
|
||||
// will free them.
|
||||
TableUpdate *update = new TableUpdate(aTable);
|
||||
mTableUpdates.AppendElement(update);
|
||||
return update;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,151 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Url Classifier code
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dave Camp <dcamp@mozilla.com>
|
||||
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef ProtocolParser_h__
|
||||
#define ProtocolParser_h__
|
||||
|
||||
#include "HashStore.h"
|
||||
#include "nsICryptoHMAC.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace safebrowsing {
|
||||
|
||||
/**
|
||||
* Some helpers for parsing the safe
|
||||
*/
|
||||
class ProtocolParser {
|
||||
public:
|
||||
struct ForwardedUpdate {
|
||||
nsCString table;
|
||||
nsCString url;
|
||||
nsCString mac;
|
||||
};
|
||||
|
||||
ProtocolParser(PRUint32 aHashKey);
|
||||
~ProtocolParser();
|
||||
|
||||
nsresult Status() const { return mUpdateStatus; }
|
||||
|
||||
nsresult Init(nsICryptoHash* aHasher);
|
||||
|
||||
nsresult InitHMAC(const nsACString& aClientKey,
|
||||
const nsACString& aServerMAC);
|
||||
nsresult FinishHMAC();
|
||||
|
||||
void SetCurrentTable(const nsACString& aTable);
|
||||
|
||||
nsresult Begin();
|
||||
nsresult AppendStream(const nsACString& aData);
|
||||
|
||||
// Forget the table updates that were created by this pass. It
|
||||
// becomes the caller's responsibility to free them. This is shitty.
|
||||
TableUpdate *GetTableUpdate(const nsACString& aTable);
|
||||
void ForgetTableUpdates() { mTableUpdates.Clear(); }
|
||||
nsTArray<TableUpdate*> &GetTableUpdates() { return mTableUpdates; }
|
||||
|
||||
// Update information.
|
||||
const nsTArray<ForwardedUpdate> &Forwards() const { return mForwards; }
|
||||
int32 UpdateWait() { return mUpdateWait; }
|
||||
bool ResetRequested() { return mResetRequested; }
|
||||
bool RekeyRequested() { return mRekeyRequested; }
|
||||
|
||||
private:
|
||||
nsresult ProcessControl(bool* aDone);
|
||||
nsresult ProcessMAC(const nsCString& aLine);
|
||||
nsresult ProcessExpirations(const nsCString& aLine);
|
||||
nsresult ProcessChunkControl(const nsCString& aLine);
|
||||
nsresult ProcessForward(const nsCString& aLine);
|
||||
nsresult AddForward(const nsACString& aUrl, const nsACString& aMac);
|
||||
nsresult ProcessChunk(bool* done);
|
||||
nsresult ProcessPlaintextChunk(const nsACString& aChunk);
|
||||
nsresult ProcessShaChunk(const nsACString& aChunk);
|
||||
nsresult ProcessHostAdd(const Prefix& aDomain, PRUint8 aNumEntries,
|
||||
const nsACString& aChunk, PRUint32* aStart);
|
||||
nsresult ProcessHostSub(const Prefix& aDomain, PRUint8 aNumEntries,
|
||||
const nsACString& aChunk, PRUint32* aStart);
|
||||
nsresult ProcessHostAddComplete(PRUint8 aNumEntries, const nsACString& aChunk,
|
||||
PRUint32 *aStart);
|
||||
nsresult ProcessHostSubComplete(PRUint8 numEntries, const nsACString& aChunk,
|
||||
PRUint32* start);
|
||||
bool NextLine(nsACString& aLine);
|
||||
|
||||
void CleanupUpdates();
|
||||
|
||||
enum ParserState {
|
||||
PROTOCOL_STATE_CONTROL,
|
||||
PROTOCOL_STATE_CHUNK
|
||||
};
|
||||
ParserState mState;
|
||||
|
||||
enum ChunkType {
|
||||
CHUNK_ADD,
|
||||
CHUNK_SUB
|
||||
};
|
||||
|
||||
struct ChunkState {
|
||||
ChunkType type;
|
||||
uint32 num;
|
||||
uint32 hashSize;
|
||||
uint32 length;
|
||||
void Clear() { num = 0; hashSize = 0; length = 0; }
|
||||
};
|
||||
ChunkState mChunkState;
|
||||
|
||||
PRUint32 mHashKey;
|
||||
nsCOMPtr<nsICryptoHash> mCryptoHash;
|
||||
|
||||
nsresult mUpdateStatus;
|
||||
nsCString mPending;
|
||||
|
||||
nsCOMPtr<nsICryptoHMAC> mHMAC;
|
||||
nsCString mServerMAC;
|
||||
|
||||
uint32 mUpdateWait;
|
||||
bool mResetRequested;
|
||||
bool mRekeyRequested;
|
||||
|
||||
nsTArray<ForwardedUpdate> mForwards;
|
||||
nsTArray<TableUpdate*> mTableUpdates;
|
||||
TableUpdate *mTableUpdate;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -304,7 +304,7 @@ PROT_ListManager.prototype.maybeToggleUpdateChecking = function() {
|
||||
*/
|
||||
PROT_ListManager.prototype.startUpdateChecker = function() {
|
||||
this.stopUpdateChecker();
|
||||
|
||||
|
||||
// Schedule the first check for between 15 and 45 minutes.
|
||||
var repeatingUpdateDelay = this.updateInterval / 2;
|
||||
repeatingUpdateDelay += Math.floor(Math.random() * this.updateInterval);
|
||||
|
@ -1,92 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Url Classifier code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsILocalFile.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsCheckSummedOutputStream.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsCheckSummedOutputStream
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED3(nsCheckSummedOutputStream,
|
||||
nsSafeFileOutputStream,
|
||||
nsISafeOutputStream,
|
||||
nsIOutputStream,
|
||||
nsIFileOutputStream)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCheckSummedOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm,
|
||||
PRInt32 behaviorFlags)
|
||||
{
|
||||
nsresult rv;
|
||||
mHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mHash->Init(nsICryptoHash::MD5);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return nsSafeFileOutputStream::Init(file, ioFlags, perm, behaviorFlags);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCheckSummedOutputStream::Finish()
|
||||
{
|
||||
nsresult rv = mHash->Finish(false, mCheckSum);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRUint32 written;
|
||||
rv = nsSafeFileOutputStream::Write(reinterpret_cast<const char*>(mCheckSum.BeginReading()),
|
||||
mCheckSum.Length(), &written);
|
||||
NS_ASSERTION(written == mCheckSum.Length(), "Error writing stream checksum");
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return nsSafeFileOutputStream::Finish();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCheckSummedOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *result)
|
||||
{
|
||||
nsresult rv = mHash->Update(reinterpret_cast<const uint8*>(buf), count);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return nsSafeFileOutputStream::Write(buf, count, result);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
@ -1,86 +0,0 @@
|
||||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Url Classifier code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsCheckSummedOutputStream_h__
|
||||
#define nsCheckSummedOutputStream_h__
|
||||
|
||||
#include "nsILocalFile.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsString.h"
|
||||
#include "../../../netwerk/base/src/nsFileStreams.h"
|
||||
#include "nsToolkitCompsCID.h"
|
||||
|
||||
class nsCheckSummedOutputStream : public nsSafeFileOutputStream
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// Size of MD5 hash in bytes
|
||||
static const PRUint32 CHECKSUM_SIZE = 16;
|
||||
|
||||
nsCheckSummedOutputStream() {}
|
||||
virtual ~nsCheckSummedOutputStream() { nsSafeFileOutputStream::Close(); }
|
||||
|
||||
NS_IMETHOD Finish();
|
||||
NS_IMETHOD Write(const char *buf, PRUint32 count, PRUint32 *result);
|
||||
NS_IMETHOD Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, PRInt32 behaviorFlags);
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsICryptoHash> mHash;
|
||||
nsCAutoString mCheckSum;
|
||||
};
|
||||
|
||||
// returns a file output stream which can be QI'ed to nsIFileOutputStream.
|
||||
inline nsresult
|
||||
NS_NewCheckSummedOutputStream(nsIOutputStream **result,
|
||||
nsIFile *file,
|
||||
PRInt32 ioFlags = -1,
|
||||
PRInt32 perm = -1,
|
||||
PRInt32 behaviorFlags = 0)
|
||||
{
|
||||
nsCOMPtr<nsIFileOutputStream> out = new nsCheckSummedOutputStream();
|
||||
nsresult rv = out->Init(file, ioFlags, perm, behaviorFlags);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
NS_ADDREF(*result = out); // cannot use nsCOMPtr::swap
|
||||
return rv;
|
||||
}
|
||||
|
||||
#endif
|
@ -40,12 +40,10 @@
|
||||
|
||||
%{C++
|
||||
#include "nsTArray.h"
|
||||
#include "Entries.h"
|
||||
#include "LookupCache.h"
|
||||
class nsUrlClassifierLookupResult;
|
||||
%}
|
||||
[ptr] native ResultArray(nsTArray<mozilla::safebrowsing::LookupResult>);
|
||||
[ptr] native CacheCompletionArray(nsTArray<mozilla::safebrowsing::CacheResult>);
|
||||
[ptr] native PrefixArray(mozilla::safebrowsing::PrefixArray);
|
||||
[ptr] native ResultArray(nsTArray<nsUrlClassifierLookupResult>);
|
||||
|
||||
interface nsIUrlClassifierHashCompleter;
|
||||
|
||||
// Interface for JS function callbacks
|
||||
@ -233,14 +231,14 @@ interface nsIUrlClassifierDBService : nsISupports
|
||||
* Interface for the actual worker thread. Implementations of this need not
|
||||
* be thread aware and just work on the database.
|
||||
*/
|
||||
[scriptable, uuid(0445be75-b114-43ea-89dc-aa16af26e77e)]
|
||||
[scriptable, uuid(2af84c09-269e-4fc2-b28f-af56717db118)]
|
||||
interface nsIUrlClassifierDBServiceWorker : nsIUrlClassifierDBService
|
||||
{
|
||||
// Provide a way to forcibly close the db connection.
|
||||
void closeDb();
|
||||
|
||||
[noscript]void cacheCompletions(in CacheCompletionArray completions);
|
||||
[noscript]void cacheMisses(in PrefixArray misses);
|
||||
// Cache the results of a hash completion.
|
||||
[noscript]void cacheCompletions(in ResultArray entries);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -249,7 +247,7 @@ interface nsIUrlClassifierDBServiceWorker : nsIUrlClassifierDBService
|
||||
* lookup to provide a set of possible results, which the main thread
|
||||
* may need to expand using an nsIUrlClassifierCompleter.
|
||||
*/
|
||||
[uuid(b903dc8f-dff1-42fe-894b-36e7a59bb801)]
|
||||
[uuid(f1dc83c6-ad43-4f0f-a809-fd43de7de8a4)]
|
||||
interface nsIUrlClassifierLookupCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -39,26 +39,28 @@
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIFile.idl"
|
||||
|
||||
interface nsIArray;
|
||||
|
||||
// Note that the PrefixSet name is historical and we do properly support
|
||||
// duplicated values, so it's really a Prefix Trie.
|
||||
// All methods are thread-safe.
|
||||
[scriptable, uuid(b21b0fa1-20d2-422a-b2cc-b289c9325811)]
|
||||
|
||||
[scriptable, uuid(519c8519-0f30-426b-bb7b-c400ba0318e2)]
|
||||
interface nsIUrlClassifierPrefixSet : nsISupports
|
||||
{
|
||||
// Initialize the PrefixSet. Give it a name for memory reporting.
|
||||
void init(in ACString aName);
|
||||
// Fills the PrefixSet with the given array of prefixes.
|
||||
// Can send an empty Array to clear the tree. A truly "empty tree"
|
||||
// cannot be represented, so put a sentinel value if that is required
|
||||
// Requires array to be sorted.
|
||||
void setPrefixes([const, array, size_is(aLength)] in unsigned long aPrefixes,
|
||||
in unsigned long aLength);
|
||||
void getPrefixes(out unsigned long aCount,
|
||||
[array, size_is(aCount), retval] out unsigned long aPrefixes);
|
||||
// Do a lookup in the PrefixSet, return whether the value is present.
|
||||
// If aReady is set, we will block until there are any entries.
|
||||
// If not set, we will return in aReady whether we were ready or not.
|
||||
boolean probe(in unsigned long aPrefix, inout boolean aReady);
|
||||
boolean probe(in unsigned long aPrefix, in unsigned long aKey,
|
||||
inout boolean aReady);
|
||||
// Return the key that is used to randomize the collisions in the prefixes.
|
||||
PRUint32 getKey();
|
||||
boolean isEmpty();
|
||||
void loadFromFile(in nsIFile aFile);
|
||||
void storeToFile(in nsIFile aFile);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -53,8 +53,6 @@
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsICryptoHMAC.h"
|
||||
|
||||
#include "LookupCache.h"
|
||||
|
||||
// The hash length for a domain key.
|
||||
#define DOMAIN_LENGTH 4
|
||||
|
||||
@ -90,8 +88,7 @@ public:
|
||||
|
||||
bool GetCompleter(const nsACString& tableName,
|
||||
nsIUrlClassifierHashCompleter** completer);
|
||||
nsresult CacheCompletions(mozilla::safebrowsing::CacheResultArray *results);
|
||||
nsresult CacheMisses(mozilla::safebrowsing::PrefixArray *results);
|
||||
nsresult CacheCompletions(nsTArray<nsUrlClassifierLookupResult> *results);
|
||||
|
||||
static nsIThread* BackgroundThread();
|
||||
|
||||
@ -134,6 +131,10 @@ private:
|
||||
// The list of tables that can use the default hash completer object.
|
||||
nsTArray<nsCString> mGethashWhitelist;
|
||||
|
||||
// Set of prefixes known to be in the database
|
||||
nsRefPtr<nsUrlClassifierPrefixSet> mPrefixSet;
|
||||
nsCOMPtr<nsICryptoHash> mHash;
|
||||
|
||||
// Thread that we do the updates on.
|
||||
static nsIThread* gDbBackgroundThread;
|
||||
};
|
||||
|
@ -71,7 +71,7 @@ static const PRLogModuleInfo *gUrlClassifierPrefixSetLog = nsnull;
|
||||
class nsPrefixSetReporter : public nsIMemoryReporter
|
||||
{
|
||||
public:
|
||||
nsPrefixSetReporter(nsUrlClassifierPrefixSet* aParent, const nsACString& aName);
|
||||
nsPrefixSetReporter(nsUrlClassifierPrefixSet * aParent, const nsACString & aName);
|
||||
virtual ~nsPrefixSetReporter() {};
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
@ -79,7 +79,7 @@ public:
|
||||
|
||||
private:
|
||||
nsCString mPath;
|
||||
nsUrlClassifierPrefixSet* mParent;
|
||||
nsUrlClassifierPrefixSet * mParent;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(nsPrefixSetReporter, nsIMemoryReporter)
|
||||
@ -87,8 +87,8 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(nsPrefixSetReporter, nsIMemoryReporter)
|
||||
NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(StoragePrefixSetMallocSizeOf,
|
||||
"storage/prefixset")
|
||||
|
||||
nsPrefixSetReporter::nsPrefixSetReporter(nsUrlClassifierPrefixSet* aParent,
|
||||
const nsACString& aName)
|
||||
nsPrefixSetReporter::nsPrefixSetReporter(nsUrlClassifierPrefixSet * aParent,
|
||||
const nsACString & aName)
|
||||
: mParent(aParent)
|
||||
{
|
||||
mPath.Assign(NS_LITERAL_CSTRING("explicit/storage/prefixset"));
|
||||
@ -99,42 +99,42 @@ nsPrefixSetReporter::nsPrefixSetReporter(nsUrlClassifierPrefixSet* aParent,
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrefixSetReporter::GetProcess(nsACString& aProcess)
|
||||
nsPrefixSetReporter::GetProcess(nsACString & aProcess)
|
||||
{
|
||||
aProcess.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrefixSetReporter::GetPath(nsACString& aPath)
|
||||
nsPrefixSetReporter::GetPath(nsACString & aPath)
|
||||
{
|
||||
aPath.Assign(mPath);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrefixSetReporter::GetKind(PRInt32* aKind)
|
||||
nsPrefixSetReporter::GetKind(PRInt32 * aKind)
|
||||
{
|
||||
*aKind = nsIMemoryReporter::KIND_HEAP;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrefixSetReporter::GetUnits(PRInt32* aUnits)
|
||||
nsPrefixSetReporter::GetUnits(PRInt32 * aUnits)
|
||||
{
|
||||
*aUnits = nsIMemoryReporter::UNITS_BYTES;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrefixSetReporter::GetAmount(PRInt64* aAmount)
|
||||
nsPrefixSetReporter::GetAmount(PRInt64 * aAmount)
|
||||
{
|
||||
*aAmount = mParent->SizeOfIncludingThis(StoragePrefixSetMallocSizeOf);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPrefixSetReporter::GetDescription(nsACString& aDescription)
|
||||
nsPrefixSetReporter::GetDescription(nsACString & aDescription)
|
||||
{
|
||||
aDescription.Assign(NS_LITERAL_CSTRING("Memory used by a PrefixSet for "
|
||||
"UrlClassifier, in bytes."));
|
||||
@ -146,21 +146,21 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSe
|
||||
nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet()
|
||||
: mPrefixSetLock("mPrefixSetLock"),
|
||||
mSetIsReady(mPrefixSetLock, "mSetIsReady"),
|
||||
mHasPrefixes(false)
|
||||
mHasPrefixes(false),
|
||||
mRandomKey(0)
|
||||
{
|
||||
#if defined(PR_LOGGING)
|
||||
if (!gUrlClassifierPrefixSetLog)
|
||||
gUrlClassifierPrefixSetLog = PR_NewLogModule("UrlClassifierPrefixSet");
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::Init(const nsACString& aName)
|
||||
{
|
||||
mReporter = new nsPrefixSetReporter(this, aName);
|
||||
nsresult rv = InitKey();
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("Failed to initialize PrefixSet"));
|
||||
}
|
||||
|
||||
mReporter = new nsPrefixSetReporter(this, NS_LITERAL_CSTRING("all"));
|
||||
NS_RegisterMemoryReporter(mReporter);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsUrlClassifierPrefixSet::~nsUrlClassifierPrefixSet()
|
||||
@ -168,8 +168,26 @@ nsUrlClassifierPrefixSet::~nsUrlClassifierPrefixSet()
|
||||
NS_UnregisterMemoryReporter(mReporter);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierPrefixSet::InitKey()
|
||||
{
|
||||
nsCOMPtr<nsIRandomGenerator> rg =
|
||||
do_GetService("@mozilla.org/security/random-generator;1");
|
||||
NS_ENSURE_STATE(rg);
|
||||
|
||||
PRUint8 *temp;
|
||||
nsresult rv = rg->GenerateRandomBytes(sizeof(mRandomKey), &temp);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
memcpy(&mRandomKey, temp, sizeof(mRandomKey));
|
||||
NS_Free(temp);
|
||||
|
||||
LOG(("Initialized PrefixSet, key = %X", mRandomKey));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::SetPrefixes(const PRUint32* aArray, PRUint32 aLength)
|
||||
nsUrlClassifierPrefixSet::SetPrefixes(const PRUint32 * aArray, PRUint32 aLength)
|
||||
{
|
||||
if (aLength <= 0) {
|
||||
MutexAutoLock lock(mPrefixSetLock);
|
||||
@ -188,7 +206,7 @@ nsUrlClassifierPrefixSet::SetPrefixes(const PRUint32* aArray, PRUint32 aLength)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32* aPrefixes, PRUint32 aLength)
|
||||
nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32 * prefixes, PRUint32 aLength)
|
||||
{
|
||||
if (aLength == 0) {
|
||||
return NS_OK;
|
||||
@ -196,7 +214,7 @@ nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32* aPrefixes, PRUint32 aLen
|
||||
|
||||
#ifdef DEBUG
|
||||
for (PRUint32 i = 1; i < aLength; i++) {
|
||||
MOZ_ASSERT(aPrefixes[i] >= aPrefixes[i-1]);
|
||||
MOZ_ASSERT(prefixes[i] >= prefixes[i-1]);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -204,7 +222,7 @@ nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32* aPrefixes, PRUint32 aLen
|
||||
FallibleTArray<PRUint32> newIndexStarts;
|
||||
FallibleTArray<PRUint16> newDeltas;
|
||||
|
||||
if (!newIndexPrefixes.AppendElement(aPrefixes[0])) {
|
||||
if (!newIndexPrefixes.AppendElement(prefixes[0])) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
if (!newIndexStarts.AppendElement(newDeltas.Length())) {
|
||||
@ -212,25 +230,25 @@ nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32* aPrefixes, PRUint32 aLen
|
||||
}
|
||||
|
||||
PRUint32 numOfDeltas = 0;
|
||||
PRUint32 currentItem = aPrefixes[0];
|
||||
PRUint32 currentItem = prefixes[0];
|
||||
for (PRUint32 i = 1; i < aLength; i++) {
|
||||
if ((numOfDeltas >= DELTAS_LIMIT) ||
|
||||
(aPrefixes[i] - currentItem >= MAX_INDEX_DIFF)) {
|
||||
(prefixes[i] - currentItem >= MAX_INDEX_DIFF)) {
|
||||
if (!newIndexStarts.AppendElement(newDeltas.Length())) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
if (!newIndexPrefixes.AppendElement(aPrefixes[i])) {
|
||||
if (!newIndexPrefixes.AppendElement(prefixes[i])) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
numOfDeltas = 0;
|
||||
} else {
|
||||
PRUint16 delta = aPrefixes[i] - currentItem;
|
||||
PRUint16 delta = prefixes[i] - currentItem;
|
||||
if (!newDeltas.AppendElement(delta)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
numOfDeltas++;
|
||||
}
|
||||
currentItem = aPrefixes[i];
|
||||
currentItem = prefixes[i];
|
||||
}
|
||||
|
||||
newIndexPrefixes.Compact();
|
||||
@ -253,53 +271,6 @@ nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32* aPrefixes, PRUint32 aLen
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::GetPrefixes(PRUint32* aCount,
|
||||
PRUint32** aPrefixes)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aCount);
|
||||
*aCount = 0;
|
||||
NS_ENSURE_ARG_POINTER(aPrefixes);
|
||||
*aPrefixes = nsnull;
|
||||
|
||||
nsTArray<PRUint32> aArray;
|
||||
PRUint32 prefixLength = mIndexPrefixes.Length();
|
||||
|
||||
for (PRUint32 i = 0; i < prefixLength; i++) {
|
||||
PRUint32 prefix = mIndexPrefixes[i];
|
||||
PRUint32 start = mIndexStarts[i];
|
||||
PRUint32 end = (i == (prefixLength - 1)) ? mDeltas.Length()
|
||||
: mIndexStarts[i + 1];
|
||||
aArray.AppendElement(prefix);
|
||||
for (PRUint32 j = start; j < end; j++) {
|
||||
prefix += mDeltas[j];
|
||||
aArray.AppendElement(prefix);
|
||||
}
|
||||
}
|
||||
|
||||
NS_ASSERTION(mIndexStarts.Length() + mDeltas.Length() == aArray.Length(),
|
||||
"Lengths are inconsistent");
|
||||
|
||||
PRUint32 itemCount = aArray.Length();
|
||||
|
||||
if (itemCount == 1 && aArray[0] == 0) {
|
||||
/* sentinel for empty set */
|
||||
aArray.Clear();
|
||||
itemCount = 0;
|
||||
}
|
||||
|
||||
PRUint32* retval = static_cast<PRUint32*>(nsMemory::Alloc(itemCount * sizeof(PRUint32)));
|
||||
NS_ENSURE_TRUE(retval, NS_ERROR_OUT_OF_MEMORY);
|
||||
for (PRUint32 i = 0; i < itemCount; i++) {
|
||||
retval[i] = aArray[i];
|
||||
}
|
||||
|
||||
*aCount = itemCount;
|
||||
*aPrefixes = retval;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRUint32 nsUrlClassifierPrefixSet::BinSearch(PRUint32 start,
|
||||
PRUint32 end,
|
||||
PRUint32 target)
|
||||
@ -319,7 +290,7 @@ PRUint32 nsUrlClassifierPrefixSet::BinSearch(PRUint32 start,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierPrefixSet::Contains(PRUint32 aPrefix, bool* aFound)
|
||||
nsUrlClassifierPrefixSet::Contains(PRUint32 aPrefix, bool * aFound)
|
||||
{
|
||||
mPrefixSetLock.AssertCurrentThreadOwns();
|
||||
|
||||
@ -395,13 +366,32 @@ nsUrlClassifierPrefixSet::IsEmpty(bool * aEmpty)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix,
|
||||
nsUrlClassifierPrefixSet::GetKey(PRUint32 * aKey)
|
||||
{
|
||||
MutexAutoLock lock(mPrefixSetLock);
|
||||
*aKey = mRandomKey;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix, PRUint32 aKey,
|
||||
bool* aReady, bool* aFound)
|
||||
{
|
||||
MutexAutoLock lock(mPrefixSetLock);
|
||||
|
||||
*aFound = false;
|
||||
|
||||
// We might have raced here with a LoadPrefixSet call,
|
||||
// loading a saved PrefixSet with another key than the one used to probe us.
|
||||
// This must occur exactly between the GetKey call and the Probe call.
|
||||
// This could cause a false negative immediately after browser start.
|
||||
// Claim we are still busy loading instead.
|
||||
if (aKey != mRandomKey) {
|
||||
LOG(("Potential race condition detected, avoiding"));
|
||||
*aReady = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// check whether we are opportunistically probing or should wait
|
||||
if (*aReady) {
|
||||
// we should block until we are ready
|
||||
@ -425,7 +415,7 @@ nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose& fileFd)
|
||||
nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose & fileFd)
|
||||
{
|
||||
PRUint32 magic;
|
||||
PRInt32 read;
|
||||
@ -437,6 +427,8 @@ nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose& fileFd)
|
||||
PRUint32 indexSize;
|
||||
PRUint32 deltaSize;
|
||||
|
||||
read = PR_Read(fileFd, &mRandomKey, sizeof(PRUint32));
|
||||
NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FILE_CORRUPTED);
|
||||
read = PR_Read(fileFd, &indexSize, sizeof(PRUint32));
|
||||
NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FILE_CORRUPTED);
|
||||
read = PR_Read(fileFd, &deltaSize, sizeof(PRUint32));
|
||||
@ -489,10 +481,8 @@ nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose& fileFd)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::LoadFromFile(nsIFile* aFile)
|
||||
nsUrlClassifierPrefixSet::LoadFromFile(nsIFile * aFile)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FILELOAD_TIME> timer;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsILocalFile> file(do_QueryInterface(aFile, &rv));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -505,7 +495,7 @@ nsUrlClassifierPrefixSet::LoadFromFile(nsIFile* aFile)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose& fileFd)
|
||||
nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose & fileFd)
|
||||
{
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FALLOCATE_TIME> timer;
|
||||
@ -521,6 +511,9 @@ nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose& fileFd)
|
||||
written = PR_Write(fileFd, &magic, sizeof(PRUint32));
|
||||
NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
|
||||
|
||||
written = PR_Write(fileFd, &mRandomKey, sizeof(PRUint32));
|
||||
NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
|
||||
|
||||
PRUint32 indexSize = mIndexStarts.Length();
|
||||
PRUint32 deltaSize = mDeltas.Length();
|
||||
written = PR_Write(fileFd, &indexSize, sizeof(PRUint32));
|
||||
@ -543,7 +536,7 @@ nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose& fileFd)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierPrefixSet::StoreToFile(nsIFile* aFile)
|
||||
nsUrlClassifierPrefixSet::StoreToFile(nsIFile * aFile)
|
||||
{
|
||||
if (!mHasPrefixes) {
|
||||
LOG(("Attempt to serialize empty PrefixSet"));
|
||||
|
@ -44,7 +44,6 @@
|
||||
#include "nsISupportsUtils.h"
|
||||
#include "nsID.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsIUrlClassifierPrefixSet.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsToolkitCompsCID.h"
|
||||
@ -60,13 +59,12 @@ public:
|
||||
nsUrlClassifierPrefixSet();
|
||||
virtual ~nsUrlClassifierPrefixSet();
|
||||
|
||||
NS_IMETHOD Init(const nsACString& aName);
|
||||
NS_IMETHOD SetPrefixes(const PRUint32* aArray, PRUint32 aLength);
|
||||
NS_IMETHOD GetPrefixes(PRUint32* aCount, PRUint32** aPrefixes);
|
||||
NS_IMETHOD Probe(PRUint32 aPrefix, bool* aReady, bool* aFound);
|
||||
NS_IMETHOD IsEmpty(bool* aEmpty);
|
||||
NS_IMETHOD Probe(PRUint32 aPrefix, PRUint32 aKey, bool* aReady, bool* aFound);
|
||||
NS_IMETHOD IsEmpty(bool * aEmpty);
|
||||
NS_IMETHOD LoadFromFile(nsIFile* aFile);
|
||||
NS_IMETHOD StoreToFile(nsIFile* aFile);
|
||||
NS_IMETHOD GetKey(PRUint32* aKey);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
@ -86,12 +84,15 @@ protected:
|
||||
nsresult Contains(PRUint32 aPrefix, bool* aFound);
|
||||
nsresult MakePrefixSet(const PRUint32* aArray, PRUint32 aLength);
|
||||
PRUint32 BinSearch(PRUint32 start, PRUint32 end, PRUint32 target);
|
||||
nsresult LoadFromFd(mozilla::AutoFDClose& fileFd);
|
||||
nsresult StoreToFd(mozilla::AutoFDClose& fileFd);
|
||||
nsresult LoadFromFd(mozilla::AutoFDClose & fileFd);
|
||||
nsresult StoreToFd(mozilla::AutoFDClose & fileFd);
|
||||
nsresult InitKey();
|
||||
|
||||
// boolean indicating whether |setPrefixes| has been
|
||||
// called with a non-empty array.
|
||||
bool mHasPrefixes;
|
||||
// key used to randomize hash collisions
|
||||
PRUint32 mRandomKey;
|
||||
// the prefix for each index.
|
||||
FallibleTArray<PRUint32> mIndexPrefixes;
|
||||
// the value corresponds to the beginning of the run
|
||||
@ -99,6 +100,7 @@ protected:
|
||||
FallibleTArray<PRUint32> mIndexStarts;
|
||||
// array containing deltas from indices.
|
||||
FallibleTArray<PRUint16> mDeltas;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -183,7 +183,7 @@ UrlClassifierDBServiceWorkerProxy::CloseDb()
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::CacheCompletions(CacheResultArray * aEntries)
|
||||
UrlClassifierDBServiceWorkerProxy::CacheCompletions(nsTArray<nsUrlClassifierLookupResult>* aEntries)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r = new CacheCompletionsRunnable(mTarget, aEntries);
|
||||
return DispatchToWorkerThread(r);
|
||||
@ -196,27 +196,12 @@ UrlClassifierDBServiceWorkerProxy::CacheCompletionsRunnable::Run()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::CacheMisses(PrefixArray * aEntries)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r = new CacheMissesRunnable(mTarget, aEntries);
|
||||
return DispatchToWorkerThread(r);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierDBServiceWorkerProxy::CacheMissesRunnable::Run()
|
||||
{
|
||||
mTarget->CacheMisses(mEntries);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(UrlClassifierLookupCallbackProxy,
|
||||
nsIUrlClassifierLookupCallback)
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierLookupCallbackProxy::LookupComplete
|
||||
(LookupResultArray * aResults)
|
||||
(nsTArray<nsUrlClassifierLookupResult>* aResults)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r = new LookupCompleteRunnable(mTarget, aResults);
|
||||
return NS_DispatchToMainThread(r);
|
||||
|
@ -40,9 +40,6 @@
|
||||
|
||||
#include "nsIUrlClassifierDBService.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "LookupCache.h"
|
||||
|
||||
using namespace mozilla::safebrowsing;
|
||||
|
||||
/**
|
||||
* Thread proxy from the main thread to the worker thread.
|
||||
@ -153,7 +150,7 @@ public:
|
||||
{
|
||||
public:
|
||||
CacheCompletionsRunnable(nsIUrlClassifierDBServiceWorker* aTarget,
|
||||
CacheResultArray *aEntries)
|
||||
nsTArray<nsUrlClassifierLookupResult>* aEntries)
|
||||
: mTarget(aTarget)
|
||||
, mEntries(aEntries)
|
||||
{ }
|
||||
@ -162,23 +159,7 @@ public:
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIUrlClassifierDBServiceWorker> mTarget;
|
||||
CacheResultArray *mEntries;
|
||||
};
|
||||
|
||||
class CacheMissesRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
CacheMissesRunnable(nsIUrlClassifierDBServiceWorker* aTarget,
|
||||
PrefixArray *aEntries)
|
||||
: mTarget(aTarget)
|
||||
, mEntries(aEntries)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIUrlClassifierDBServiceWorker> mTarget;
|
||||
PrefixArray *mEntries;
|
||||
nsTArray<nsUrlClassifierLookupResult>* mEntries;
|
||||
};
|
||||
|
||||
private:
|
||||
@ -201,7 +182,7 @@ public:
|
||||
{
|
||||
public:
|
||||
LookupCompleteRunnable(nsIUrlClassifierLookupCallback* aTarget,
|
||||
LookupResultArray *aResults)
|
||||
nsTArray<nsUrlClassifierLookupResult>* aResults)
|
||||
: mTarget(aTarget)
|
||||
, mResults(aResults)
|
||||
{ }
|
||||
@ -210,7 +191,7 @@ public:
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIUrlClassifierLookupCallback> mTarget;
|
||||
LookupResultArray * mResults;
|
||||
nsTArray<nsUrlClassifierLookupResult>* mResults;
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -171,16 +171,11 @@ nsUrlClassifierStreamUpdater::FetchUpdate(const nsACString & aUpdateUrl,
|
||||
const nsACString & aStreamTable,
|
||||
const nsACString & aServerMAC)
|
||||
{
|
||||
LOG(("(pre) Fetching update from %s\n", PromiseFlatCString(aUpdateUrl).get()));
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), aUpdateUrl);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString urlSpec;
|
||||
uri->GetAsciiSpec(urlSpec);
|
||||
|
||||
LOG(("(post) Fetching update from %s\n", urlSpec.get()));
|
||||
LOG(("Fetching update from %s\n", PromiseFlatCString(aUpdateUrl).get()));
|
||||
|
||||
return FetchUpdate(uri, aRequestBody, aStreamTable, aServerMAC);
|
||||
}
|
||||
@ -245,11 +240,6 @@ nsUrlClassifierStreamUpdater::DownloadUpdates(
|
||||
mIsUpdating = true;
|
||||
*_retval = true;
|
||||
|
||||
nsCAutoString urlSpec;
|
||||
mUpdateUrl->GetAsciiSpec(urlSpec);
|
||||
|
||||
LOG(("FetchUpdate: %s", urlSpec.get()));
|
||||
//LOG(("requestBody: %s", aRequestBody.get()));
|
||||
|
||||
return FetchUpdate(mUpdateUrl, aRequestBody, EmptyCString(), EmptyCString());
|
||||
}
|
||||
|
@ -24,26 +24,14 @@ prefBranch.setIntPref("urlclassifier.gethashnoise", 0);
|
||||
prefBranch.setBoolPref("browser.safebrowsing.malware.enabled", true);
|
||||
prefBranch.setBoolPref("browser.safebrowsing.enabled", true);
|
||||
|
||||
function delFile(name) {
|
||||
function cleanUp() {
|
||||
try {
|
||||
// Delete a previously created sqlite file
|
||||
var file = dirSvc.get('ProfLD', Ci.nsIFile);
|
||||
file.append(name);
|
||||
file.append("urlclassifier3.sqlite");
|
||||
if (file.exists())
|
||||
file.remove(false);
|
||||
} catch(e) {
|
||||
}
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
delFile("classifier.hashkey");
|
||||
delFile("urlclassifier3.sqlite");
|
||||
delFile("safebrowsing/test-phish-simple.sbstore");
|
||||
delFile("safebrowsing/test-malware-simple.sbstore");
|
||||
delFile("safebrowsing/test-phish-simple.cache");
|
||||
delFile("safebrowsing/test-malware-simple.cache");
|
||||
delFile("safebrowsing/test-phish-simple.pset");
|
||||
delFile("safebrowsing/test-malware-simple.pset");
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
var dbservice = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(Ci.nsIUrlClassifierDBService);
|
||||
@ -288,10 +276,11 @@ function runNextTest()
|
||||
|
||||
dbservice.resetDatabase();
|
||||
dbservice.setHashCompleter('test-phish-simple', null);
|
||||
dumpn("running " + gTests[gNextTest]);
|
||||
|
||||
let test = gTests[gNextTest++];
|
||||
dump("running " + test.name + "\n");
|
||||
test();
|
||||
dump("running " + gTests[gNextTest]);
|
||||
|
||||
gTests[gNextTest++]();
|
||||
}
|
||||
|
||||
function runTests(tests)
|
||||
|
@ -55,7 +55,6 @@ function testSimpleSub()
|
||||
"chunkType" : "s",
|
||||
"urls": subUrls }]);
|
||||
|
||||
|
||||
var assertions = {
|
||||
"tableData" : "test-phish-simple;a:1:s:50",
|
||||
"urlsExist" : [ "bar.com/b" ],
|
||||
@ -362,8 +361,7 @@ function testExpireLists() {
|
||||
{ "chunkType" : "sd:1-3,5" }]);
|
||||
|
||||
var assertions = {
|
||||
// "tableData" : "test-phish-simple;"
|
||||
"tableData": ""
|
||||
"tableData" : "test-phish-simple;"
|
||||
};
|
||||
|
||||
doTest([addUpdate, subUpdate, expireUpdate], assertions);
|
||||
@ -481,7 +479,10 @@ function run_test()
|
||||
testSubPartiallyMatches2,
|
||||
testSubsDifferentChunks,
|
||||
testSubsDifferentChunksSameHostId,
|
||||
testExpireLists
|
||||
testExpireLists,
|
||||
testDuplicateAddChunks,
|
||||
testExpireWholeSub,
|
||||
testPreventWholeSub,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,195 @@
|
||||
//* -*- Mode: Javascript; tab-width: 8; indent-tabs-mode: nil; js-indent-level: 2 -*- *
|
||||
// Test an add of two urls to a fresh database
|
||||
function testCleanHostKeys() {
|
||||
var addUrls = [ "foo.com/a" ];
|
||||
var update = buildPhishingUpdate(
|
||||
[
|
||||
{ "chunkNum" : 1,
|
||||
"urls" : addUrls
|
||||
}]);
|
||||
|
||||
doStreamUpdate(update, function() {
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Components.interfaces.nsIIOService);
|
||||
|
||||
// Check with a clean host key
|
||||
var uri = ios.newURI("http://bar.com/a", null, null);
|
||||
|
||||
// Use the nsIURIClassifier interface (the
|
||||
// nsIUrlClassifierDBService will always queue a lookup,
|
||||
// nsIURIClassifier won't if the host key is known to be clean.
|
||||
var classifier = dbservice.QueryInterface(Ci.nsIURIClassifier);
|
||||
var result = classifier.classify(uri, function(errorCode) {
|
||||
var result2 = classifier.classify(uri, function() {
|
||||
do_throw("shouldn't get a callback");
|
||||
});
|
||||
// second call shouldn't result in a callback.
|
||||
do_check_eq(result2, false);
|
||||
do_throw("shouldn't get a callback");
|
||||
});
|
||||
|
||||
// The first classifier call will not result in a callback
|
||||
do_check_eq(result, false);
|
||||
runNextTest();
|
||||
}, updateError);
|
||||
}
|
||||
|
||||
// Make sure that an update properly clears the host key cache
|
||||
function testUpdate() {
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Components.interfaces.nsIIOService);
|
||||
|
||||
// Must put something in the PrefixSet
|
||||
var preUrls = [ "foo.com/b" ];
|
||||
var preUpdate = buildPhishingUpdate(
|
||||
[
|
||||
{ "chunkNum" : 1,
|
||||
"urls" : preUrls
|
||||
}]);
|
||||
|
||||
doStreamUpdate(preUpdate, function() {
|
||||
// First lookup won't happen...
|
||||
var uri = ios.newURI("http://foo.com/a", null, null);
|
||||
|
||||
// Use the nsIURIClassifier interface (the
|
||||
// nsIUrlClassifierDBService will always queue a lookup,
|
||||
// nsIURIClassifier won't if the host key is known to be clean.
|
||||
var classifier = dbservice.QueryInterface(Ci.nsIURIClassifier);
|
||||
var result = classifier.classify(uri, function(errorCode) {
|
||||
// shouldn't arrive here
|
||||
do_check_eq(errorCode, Cr.NS_OK);
|
||||
do_throw("shouldn't get a callback");
|
||||
});
|
||||
do_check_eq(result, false);
|
||||
|
||||
// Now add the url to the db...
|
||||
var addUrls = [ "foo.com/a" ];
|
||||
var update = buildPhishingUpdate(
|
||||
[
|
||||
{ "chunkNum" : 2,
|
||||
"urls" : addUrls
|
||||
}]);
|
||||
doStreamUpdate(update, function() {
|
||||
var result2 = classifier.classify(uri, function(errorCode) {
|
||||
do_check_neq(errorCode, Cr.NS_OK);
|
||||
runNextTest();
|
||||
});
|
||||
// second call should result in a callback.
|
||||
do_check_eq(result2, true);
|
||||
}, updateError);
|
||||
}, updateError);
|
||||
}
|
||||
|
||||
function testResetFullCache() {
|
||||
// Must put something in the PrefixSet
|
||||
var preUrls = [ "zaz.com/b" ];
|
||||
var preUpdate = buildPhishingUpdate(
|
||||
[
|
||||
{ "chunkNum" : 1,
|
||||
"urls" : preUrls
|
||||
}]);
|
||||
|
||||
doStreamUpdate(preUpdate, function() {
|
||||
// First do enough queries to fill up the clean hostkey cache
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Components.interfaces.nsIIOService);
|
||||
|
||||
// Use the nsIURIClassifier interface (the
|
||||
// nsIUrlClassifierDBService will always queue a lookup,
|
||||
// nsIURIClassifier won't if the host key is known to be clean.
|
||||
var classifier = dbservice.QueryInterface(Ci.nsIURIClassifier);
|
||||
|
||||
var uris1 = [
|
||||
"www.foo.com/",
|
||||
"www.bar.com/",
|
||||
"www.blah.com/",
|
||||
"www.site.com/",
|
||||
"www.example.com/",
|
||||
"www.test.com/",
|
||||
"www.malware.com/",
|
||||
"www.phishing.com/",
|
||||
"www.clean.com/" ];
|
||||
|
||||
var uris2 = [];
|
||||
|
||||
var runSecondLookup = function() {
|
||||
if (uris2.length == 0) {
|
||||
runNextTest();
|
||||
return;
|
||||
}
|
||||
|
||||
var spec = uris2.pop();
|
||||
var uri = ios.newURI("http://" + spec, null, null);
|
||||
|
||||
var result = classifier.classify(uri, function(errorCode) {
|
||||
});
|
||||
runSecondLookup();
|
||||
// now look up a few more times.
|
||||
}
|
||||
|
||||
var runInitialLookup = function() {
|
||||
if (uris1.length == 0) {
|
||||
// We're done filling up the cache. Run an update to flush it,
|
||||
// then start lookup up again.
|
||||
var addUrls = [ "notgoingtocheck.com/a" ];
|
||||
var update = buildPhishingUpdate(
|
||||
[
|
||||
{ "chunkNum" : 1,
|
||||
"urls" : addUrls
|
||||
}]);
|
||||
doStreamUpdate(update, function() {
|
||||
runSecondLookup();
|
||||
}, updateError);
|
||||
return;
|
||||
}
|
||||
var spec = uris1.pop();
|
||||
|
||||
uris2.push(spec);
|
||||
var uri = ios.newURI("http://" + spec, null, null);
|
||||
var result = classifier.classify(uri, function(errorCode) {
|
||||
});
|
||||
runInitialLookup();
|
||||
// None of these will generate a callback
|
||||
do_check_eq(result, false);
|
||||
if (!result) {
|
||||
doNextTest();
|
||||
}
|
||||
}
|
||||
|
||||
// XXX bug 457790: dbservice.resetDatabase() doesn't have a way to
|
||||
// wait to make sure it has been applied. Until this is added, we'll
|
||||
// just use a timeout.
|
||||
var t = new Timer(3000, runInitialLookup);
|
||||
}, updateError);
|
||||
}
|
||||
|
||||
function testBug475436() {
|
||||
var addUrls = [ "foo.com/a", "www.foo.com/" ];
|
||||
var update = buildPhishingUpdate(
|
||||
[
|
||||
{ "chunkNum" : 1,
|
||||
"urls" : addUrls
|
||||
}]);
|
||||
|
||||
var assertions = {
|
||||
"tableData" : "test-phish-simple;a:1",
|
||||
"urlsExist" : ["foo.com/a", "foo.com/a" ]
|
||||
};
|
||||
|
||||
doUpdateTest([update], assertions, runNextTest, updateError);
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
runTests([
|
||||
// XXX: We need to run testUpdate first, because of a
|
||||
// race condition (bug 457790) calling dbservice.classify()
|
||||
// directly after dbservice.resetDatabase().
|
||||
testUpdate,
|
||||
testCleanHostKeys,
|
||||
testResetFullCache,
|
||||
testBug475436
|
||||
]);
|
||||
}
|
||||
|
||||
do_test_pending();
|
@ -461,8 +461,7 @@ function testWrongTable()
|
||||
"tableData" : "test-phish-simple;a:1",
|
||||
// The urls were added as phishing urls, but the completer is claiming
|
||||
// that they are malware urls, and we trust the completer in this case.
|
||||
// The result will be discarded, so we can only check for non-existence.
|
||||
"urlsDontExist" : addUrls,
|
||||
"malwareUrlsExist" : addUrls,
|
||||
// Make sure the completer was actually queried.
|
||||
"completerQueried" : [completer, addUrls]
|
||||
};
|
||||
@ -471,14 +470,57 @@ function testWrongTable()
|
||||
function() {
|
||||
// Give the dbservice a chance to (not) cache the result.
|
||||
var timer = new Timer(3000, function() {
|
||||
// The miss earlier will have caused a miss to be cached.
|
||||
// Resetting the completer does not count as an update,
|
||||
// so we will not be probed again.
|
||||
var newCompleter = installCompleter('test-malware-simple', [[1, addUrls]], []); dbservice.setHashCompleter("test-phish-simple",
|
||||
// The dbservice shouldn't have cached this result,
|
||||
// so this completer should be queried.
|
||||
var newCompleter = installCompleter('test-malware-simple', [[1, addUrls]], []);
|
||||
|
||||
// The above installCompleter installs the
|
||||
// completer for test-malware-simple, we want it
|
||||
// to be used for test-phish-simple too.
|
||||
dbservice.setHashCompleter("test-phish-simple",
|
||||
newCompleter);
|
||||
|
||||
|
||||
var assertions = {
|
||||
"urlsDontExist" : addUrls
|
||||
"malwareUrlsExist" : addUrls,
|
||||
"completerQueried" : [newCompleter, addUrls]
|
||||
};
|
||||
checkAssertions(assertions, runNextTest);
|
||||
});
|
||||
}, updateError);
|
||||
}
|
||||
|
||||
function testWrongChunk()
|
||||
{
|
||||
var addUrls = [ "foo.com/a" ];
|
||||
var update = buildPhishingUpdate(
|
||||
[
|
||||
{ "chunkNum" : 1,
|
||||
"urls" : addUrls
|
||||
}],
|
||||
4);
|
||||
var completer = installCompleter('test-phish-simple',
|
||||
[[2, // wrong chunk number
|
||||
addUrls]], []);
|
||||
|
||||
var assertions = {
|
||||
"tableData" : "test-phish-simple;a:1",
|
||||
"urlsExist" : addUrls,
|
||||
// Make sure the completer was actually queried.
|
||||
"completerQueried" : [completer, addUrls]
|
||||
};
|
||||
|
||||
doUpdateTest([update], assertions,
|
||||
function() {
|
||||
// Give the dbservice a chance to (not) cache the result.
|
||||
var timer = new Timer(3000, function() {
|
||||
// The dbservice shouldn't have cached this result,
|
||||
// so this completer should be queried.
|
||||
var newCompleter = installCompleter('test-phish-simple', [[2, addUrls]], []);
|
||||
|
||||
var assertions = {
|
||||
"urlsExist" : addUrls,
|
||||
"completerQueried" : [newCompleter, addUrls]
|
||||
};
|
||||
checkAssertions(assertions, runNextTest);
|
||||
});
|
||||
@ -776,6 +818,7 @@ function run_test()
|
||||
testMixedSizesDifferentDomains,
|
||||
testInvalidHashSize,
|
||||
testWrongTable,
|
||||
testWrongChunk,
|
||||
testCachedResults,
|
||||
testCachedResultsWithSub,
|
||||
testCachedResultsWithExpire,
|
||||
@ -783,7 +826,7 @@ function run_test()
|
||||
testStaleList,
|
||||
testStaleListEmpty,
|
||||
testErrorList,
|
||||
testErrorListIndependent
|
||||
testErrorListIndependent,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
// newPset: returns an empty nsIUrlClassifierPrefixSet.
|
||||
function newPset() {
|
||||
let pset = Cc["@mozilla.org/url-classifier/prefixset;1"]
|
||||
.createInstance(Ci.nsIUrlClassifierPrefixSet);
|
||||
pset.init("all");
|
||||
return pset;
|
||||
return Cc["@mozilla.org/url-classifier/prefixset;1"]
|
||||
.createInstance(Ci.nsIUrlClassifierPrefixSet);
|
||||
}
|
||||
|
||||
// arrContains: returns true if |arr| contains the element |target|. Uses binary
|
||||
@ -30,22 +28,10 @@ function arrContains(arr, target) {
|
||||
return (!(i < 0 || i >= arr.length) && arr[i] == target);
|
||||
}
|
||||
|
||||
// checkContents: Check whether the PrefixSet pset contains
|
||||
// the prefixes in the passed array.
|
||||
function checkContents(pset, prefixes) {
|
||||
var outcount = {}, outset = {};
|
||||
outset = pset.getPrefixes(outcount);
|
||||
let inset = prefixes;
|
||||
do_check_eq(inset.length, outset.length);
|
||||
inset.sort(function(x,y) x - y);
|
||||
for (let i = 0; i < inset.length; i++) {
|
||||
do_check_eq(inset[i], outset[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function wrappedProbe(pset, prefix) {
|
||||
let key = pset.getKey();
|
||||
let dummy = {};
|
||||
return pset.probe(prefix, dummy);
|
||||
return pset.probe(prefix, key, dummy);
|
||||
};
|
||||
|
||||
// doRandomLookups: we use this to test for false membership with random input
|
||||
@ -88,9 +74,6 @@ function testBasicPset() {
|
||||
do_check_true(wrappedProbe(pset, 1593203));
|
||||
do_check_false(wrappedProbe(pset, 999));
|
||||
do_check_false(wrappedProbe(pset, 0));
|
||||
|
||||
|
||||
checkContents(pset, prefixes);
|
||||
}
|
||||
|
||||
function testDuplicates() {
|
||||
@ -105,9 +88,6 @@ function testDuplicates() {
|
||||
do_check_true(wrappedProbe(pset, 9));
|
||||
do_check_false(wrappedProbe(pset, 4));
|
||||
do_check_false(wrappedProbe(pset, 8));
|
||||
|
||||
|
||||
checkContents(pset, prefixes);
|
||||
}
|
||||
|
||||
function testSimplePset() {
|
||||
@ -117,9 +97,6 @@ function testSimplePset() {
|
||||
|
||||
doRandomLookups(pset, prefixes, 100);
|
||||
doExpectedLookups(pset, prefixes, 1);
|
||||
|
||||
|
||||
checkContents(pset, prefixes);
|
||||
}
|
||||
|
||||
function testReSetPrefixes() {
|
||||
@ -136,9 +113,6 @@ function testReSetPrefixes() {
|
||||
for (let i = 0; i < prefixes.length; i++) {
|
||||
do_check_false(wrappedProbe(pset, prefixes[i]));
|
||||
}
|
||||
|
||||
|
||||
checkContents(pset, secondPrefixes);
|
||||
}
|
||||
|
||||
function testLargeSet() {
|
||||
@ -157,9 +131,6 @@ function testLargeSet() {
|
||||
|
||||
doExpectedLookups(pset, arr, 1);
|
||||
doRandomLookups(pset, arr, 1000);
|
||||
|
||||
|
||||
checkContents(pset, arr);
|
||||
}
|
||||
|
||||
function testTinySet() {
|
||||
@ -170,12 +141,10 @@ function testTinySet() {
|
||||
|
||||
do_check_true(wrappedProbe(pset, 1));
|
||||
do_check_false(wrappedProbe(pset, 100000));
|
||||
checkContents(pset, prefixes);
|
||||
|
||||
prefixes = [];
|
||||
pset.setPrefixes(prefixes, prefixes.length);
|
||||
do_check_false(wrappedProbe(pset, 1));
|
||||
checkContents(pset, prefixes);
|
||||
}
|
||||
|
||||
let tests = [testBasicPset,
|
||||
|
@ -80,6 +80,8 @@ function testSimpleForward() {
|
||||
// Make sure that a nested forward (a forward within a forward) causes
|
||||
// the update to fail.
|
||||
function testNestedForward() {
|
||||
testFillDb(); // Make sure the db isn't empty
|
||||
|
||||
var add1Urls = [ "foo.com/a", "bar.com/c" ];
|
||||
var add2Urls = [ "foo.com/b" ];
|
||||
|
||||
@ -201,6 +203,8 @@ function testValidMAC() {
|
||||
|
||||
// Test a simple update with an invalid message authentication code.
|
||||
function testInvalidMAC() {
|
||||
testFillDb(); // Make sure the db isn't empty
|
||||
|
||||
var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ];
|
||||
var update = buildPhishingUpdate(
|
||||
[
|
||||
@ -220,6 +224,8 @@ function testInvalidMAC() {
|
||||
// Test a simple update without a message authentication code, when it is
|
||||
// expecting one.
|
||||
function testNoMAC() {
|
||||
testFillDb(); // Make sure the db isn't empty
|
||||
|
||||
var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ];
|
||||
var update = buildPhishingUpdate(
|
||||
[
|
||||
@ -276,6 +282,8 @@ function testValidForwardMAC() {
|
||||
// Test an update with a valid message authentication code, but with
|
||||
// invalid MACs on the forwards.
|
||||
function testInvalidForwardMAC() {
|
||||
testFillDb(); // Make sure the db isn't empty
|
||||
|
||||
var add1Urls = [ "foo.com/a", "bar.com/c" ];
|
||||
var add2Urls = [ "foo.com/b" ];
|
||||
var add3Urls = [ "bar.com/d" ];
|
||||
@ -315,6 +323,8 @@ function testInvalidForwardMAC() {
|
||||
// Test an update with a valid message authentication code, but no MAC
|
||||
// specified for sub-urls.
|
||||
function testNoForwardMAC() {
|
||||
testFillDb(); // Make sure the db isn't empty
|
||||
|
||||
var add1Urls = [ "foo.com/a", "bar.com/c" ];
|
||||
var add2Urls = [ "foo.com/b" ];
|
||||
var add3Urls = [ "bar.com/d" ];
|
||||
@ -381,6 +391,8 @@ gAssertions.gotRekey = function(data, cb)
|
||||
|
||||
// Tests a rekey request.
|
||||
function testRekey() {
|
||||
testFillDb();
|
||||
|
||||
var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ];
|
||||
var update = buildPhishingUpdate(
|
||||
[
|
||||
@ -445,9 +457,6 @@ function run_test()
|
||||
testInvalidUrlForward,
|
||||
testErrorUrlForward,
|
||||
testMultipleTables,
|
||||
testReset,
|
||||
// XXX: we're currently "once MAC, always MAC",
|
||||
// so any test not using a MAC must go above
|
||||
testValidMAC,
|
||||
testInvalidMAC,
|
||||
testNoMAC,
|
||||
@ -455,6 +464,7 @@ function run_test()
|
||||
testInvalidForwardMAC,
|
||||
testNoForwardMAC,
|
||||
testRekey,
|
||||
testReset,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ tail = tail_urlclassifier.js
|
||||
|
||||
[test_addsub.js]
|
||||
[test_backoff.js]
|
||||
[test_cleankeycache.js]
|
||||
[test_dbservice.js]
|
||||
[test_hashcompleter.js]
|
||||
[test_partial.js]
|
||||
|
Loading…
Reference in New Issue
Block a user