diff options
author | paulg@google.com <paulg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-11 02:17:51 +0000 |
---|---|---|
committer | paulg@google.com <paulg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-11 02:17:51 +0000 |
commit | c3ff8949df1905e2c0bffe32527a4b11de212ccc (patch) | |
tree | ffb88432b79882716a8779aee7a15efec6f3c65e | |
parent | 95218fbcd0b0e804175ada19cd8f95f5818242ba (diff) | |
download | chromium_src-c3ff8949df1905e2c0bffe32527a4b11de212ccc.zip chromium_src-c3ff8949df1905e2c0bffe32527a4b11de212ccc.tar.gz chromium_src-c3ff8949df1905e2c0bffe32527a4b11de212ccc.tar.bz2 |
Add support for 256 bit full hashes to the new implementation.
This CL replaces the following, which seems to have become inaccessible:
http://codereview.chromium.org/9202/
Review URL: http://codereview.chromium.org/10402
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@5159 0039d316-1c4b-4281-b951-d872f2087c98
15 files changed, 1045 insertions, 615 deletions
diff --git a/chrome/browser/safe_browsing/protocol_manager.cc b/chrome/browser/safe_browsing/protocol_manager.cc index 073dc2e..1e760c6 100644 --- a/chrome/browser/safe_browsing/protocol_manager.cc +++ b/chrome/browser/safe_browsing/protocol_manager.cc @@ -272,6 +272,7 @@ bool SafeBrowsingProtocolManager::HandleServiceResponse(const GURL& url, } last_update_ = Time::Now(); + sb_service_->UpdateStarted(); if (update_state_ == FIRST_REQUEST) update_state_ = SECOND_REQUEST; @@ -477,22 +478,22 @@ void SafeBrowsingProtocolManager::OnGetChunksComplete( bool found_phishing = false; for (size_t i = 0; i < lists.size(); ++i) { list_data.append(FormatList(lists[i], use_mac)); - if (lists[i].name == "goog-phish-shavar") + if (lists[i].name == safe_browsing_util::kPhishingList) found_phishing = true; - if (lists[i].name == "goog-malware-shavar") + if (lists[i].name == safe_browsing_util::kMalwareList) found_malware = true; } // If we have an empty database, let the server know we want data for these // lists. if (!found_phishing) - list_data.append(FormatList(SBListChunkRanges("goog-phish-shavar"), - use_mac)); + list_data.append(FormatList( + SBListChunkRanges(safe_browsing_util::kPhishingList), use_mac)); if (!found_malware) - list_data.append(FormatList(SBListChunkRanges("goog-malware-shavar"), - use_mac)); + list_data.append(FormatList( + SBListChunkRanges(safe_browsing_util::kMalwareList), use_mac)); std::string url = StringPrintf(kSbUpdateUrl, kSbClientName, diff --git a/chrome/browser/safe_browsing/safe_browsing_database.h b/chrome/browser/safe_browsing/safe_browsing_database.h index f4c692a..9e07297 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database.h +++ b/chrome/browser/safe_browsing/safe_browsing_database.h @@ -6,9 +6,11 @@ #define CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_DATABASE_H_ #include <deque> +#include <list> #include <string> #include <vector> +#include "base/hash_tables.h" #include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "base/task.h" @@ -80,7 +82,10 @@ class SafeBrowsingDatabase { // Called when the user's machine has resumed from a lower power state. virtual void HandleResume() = 0; - virtual void UpdateFinished(bool update_succeeded) { } + virtual void UpdateStarted() {} + virtual void UpdateFinished(bool update_succeeded) {} + + virtual std::wstring filename() const { return filename_; } protected: static std::wstring BloomFilterFilename(const std::wstring& db_filename); @@ -98,8 +103,31 @@ class SafeBrowsingDatabase { virtual void BuildBloomFilter() = 0; // Measuring false positive rate. Call this each time we look in the filter. - virtual void IncrementBloomFilterReadCount() {}; + virtual void IncrementBloomFilterReadCount() {} + + // Full hash cache support. + friend class SafeBrowsingDatabase_HashCaching_Test; + + typedef struct HashCacheEntry { + SBFullHash full_hash; + int list_id; + int add_chunk_id; + int sub_chunk_id; + base::Time received; + } HashCacheEntry; + + typedef std::list<HashCacheEntry> HashList; + typedef base::hash_map<SBPrefix, HashList> HashCache; + + scoped_ptr<HashCache> hash_cache_; + HashCache* hash_cache() { return hash_cache_.get(); } + + // Cache of prefixes that returned empty results (no full hash match). + typedef std::set<SBPrefix> PrefixCache; + PrefixCache prefix_miss_cache_; + PrefixCache* prefix_miss_cache() { return &prefix_miss_cache_; } + std::wstring filename_; std::wstring bloom_filter_filename_; scoped_refptr<BloomFilter> bloom_filter_; }; diff --git a/chrome/browser/safe_browsing/safe_browsing_database_bloom.cc b/chrome/browser/safe_browsing/safe_browsing_database_bloom.cc index 041e1bf..e20fa87 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database_bloom.cc +++ b/chrome/browser/safe_browsing/safe_browsing_database_bloom.cc @@ -10,6 +10,7 @@ #include "base/message_loop.h" #include "base/platform_thread.h" #include "base/sha2.h" +#include "base/stats_counters.h" #include "base/string_util.h" #include "chrome/browser/safe_browsing/bloom_filter.h" #include "chrome/browser/safe_browsing/chunk_range.h" @@ -101,6 +102,8 @@ bool SafeBrowsingDatabaseBloom::Open() { statement_cache_.reset(new SqliteStatementCache(db_)); + hash_cache_.reset(new HashCache); + return true; } @@ -127,7 +130,7 @@ bool SafeBrowsingDatabaseBloom::CreateTables() { return false; } - // Store 32 sub prefixes here. + // Store 32 bit sub prefixes here. if (sqlite3_exec(db_, "CREATE TABLE sub_prefix (" "chunk INTEGER," "add_chunk INTEGER," @@ -136,55 +139,22 @@ bool SafeBrowsingDatabaseBloom::CreateTables() { return false; } - // TODO(paulg): Store 256 bit add full prefixes and GetHash results here. - // 'receive_time' is used for testing the staleness of GetHash results. - // if (sqlite3_exec(db_, "CREATE TABLE full_add_prefix (" - // "chunk INTEGER," - // "prefix INTEGER," - // "receive_time INTEGER," - // "full_prefix BLOB)", - // NULL, NULL, NULL) != SQLITE_OK) { - // return false; - // } - - // TODO(paulg): These tables are going to contain very few entries, so we - // need to measure if it's worth keeping an index on them. - // sqlite3_exec(db_, - // "CREATE INDEX full_add_prefix_chunk ON full_prefix(chunk)", - // NULL, NULL, NULL); - // sqlite3_exec(db_, - // "CREATE INDEX full_add_prefix_prefix ON full_prefix(prefix)", - // NULL, NULL, NULL); - - // TODO(paulg): Store 256 bit sub full prefixes here. - // if (sqlite3_exec(db_, "CREATE TABLE full_sub_prefix (" - // "chunk INTEGER," - // "add_chunk INTEGER," - // "prefix INTEGER," - // "receive_time INTEGER," - // "full_prefix BLOB)", - // NULL, NULL, NULL) != SQLITE_OK) { - // return false; - // } - - // TODO(paulg): These tables are going to contain very few entries, so we - // need to measure if it's worth keeping an index on them. - // sqlite3_exec( - // db_, - // "CREATE INDEX full_sub_prefix_chunk ON full_sub_prefix(chunk)", - // NULL, NULL, NULL); - // sqlite3_exec( - // db_, - // "CREATE INDEX full_sub_prefix_add_chunk ON full_sub_prefix(add_chunk)", - // NULL, NULL, NULL); - // sqlite3_exec( - // db_, - // "CREATE INDEX full_sub_prefix_prefix ON full_sub_prefix(prefix)", - // NULL, NULL, NULL); - - if (sqlite3_exec(db_, "CREATE TABLE list_names (" - "id INTEGER PRIMARY KEY AUTOINCREMENT," - "name TEXT)", + // Store 256 bit add full hashes (and GetHash results) here. + if (sqlite3_exec(db_, "CREATE TABLE add_full_hash (" + "chunk INTEGER," + "prefix INTEGER," + "receive_time INTEGER," + "full_hash BLOB)", + NULL, NULL, NULL) != SQLITE_OK) { + return false; + } + + // Store 256 bit sub full hashes here. + if (sqlite3_exec(db_, "CREATE TABLE sub_full_hash (" + "chunk INTEGER," + "add_chunk INTEGER," + "prefix INTEGER," + "full_hash BLOB)", NULL, NULL, NULL) != SQLITE_OK) { return false; } @@ -233,9 +203,11 @@ bool SafeBrowsingDatabaseBloom::CreateTables() { // database thread. Any URLs that the service needs to check when this is // running are queued up and run once the reset is done. bool SafeBrowsingDatabaseBloom::ResetDatabase() { - hash_cache_.clear(); + hash_cache_->clear(); ClearUpdateCaches(); + insert_transaction_.reset(); + bool rv = Close(); DCHECK(rv); @@ -256,7 +228,7 @@ bool SafeBrowsingDatabaseBloom::ResetDatabase() { bool SafeBrowsingDatabaseBloom::CheckCompatibleVersion() { SQLITE_UNIQUE_STATEMENT(statement, *statement_cache_, - "PRAGMA user_version"); + "PRAGMA user_version"); if (!statement.is_valid()) { NOTREACHED(); return false; @@ -300,10 +272,11 @@ bool SafeBrowsingDatabaseBloom::ContainsUrl( std::vector<std::string> paths; safe_browsing_util::GeneratePathsToCheck(url, &paths); - // Grab a reference to the existing filter so that it isn't deleted on us if - // a update is just about to finish. - scoped_refptr<BloomFilter> filter = bloom_filter_; - if (!filter.get()) + // Lock the bloom filter and cache so that they aren't deleted on us if an + // update is just about to finish. + AutoLock lock(lookup_lock_); + + if (!bloom_filter_.get()) return false; // TODO(erikkay): This may wind up being too many hashes on a complex page. @@ -317,7 +290,7 @@ bool SafeBrowsingDatabaseBloom::ContainsUrl( sizeof(SBFullHash)); SBPrefix prefix; memcpy(&prefix, &full_hash, sizeof(SBPrefix)); - if (filter->Exists(prefix)) + if (bloom_filter_->Exists(prefix)) prefix_hits->push_back(prefix); } } @@ -365,7 +338,7 @@ void SafeBrowsingDatabaseBloom::InsertChunks(const std::string& list_name, if (chunks->empty()) return; - int list_id = GetListID(list_name); + int list_id = GetListId(list_name); ChunkType chunk_type = chunks->front().is_add ? ADD_CHUNK : SUB_CHUNK; while (!chunks->empty()) { @@ -383,9 +356,9 @@ void SafeBrowsingDatabaseBloom::InsertChunks(const std::string& list_name, entry->set_list_id(list_id); if (chunk_type == ADD_CHUNK) { entry->set_chunk_id(chunk_id); - AddEntry(host, entry); + InsertAdd(host, entry); } else { - AddSub(chunk_id, host, entry); + InsertSub(chunk_id, host, entry); } entry->Destroy(); @@ -408,36 +381,59 @@ void SafeBrowsingDatabaseBloom::InsertChunks(const std::string& list_name, chunk_inserted_callback_->Run(); } +void SafeBrowsingDatabaseBloom::UpdateStarted() { + DCHECK(insert_transaction_.get() == NULL); + insert_transaction_.reset(new SQLTransaction(db_)); + if (insert_transaction_->Begin() != SQLITE_OK) { + // TODO(paulg): We should abort the update, and possibly enter back off mode + // if we have a problem with the database. + DCHECK(false) << "Safe browsing database couldn't start transaction"; + insert_transaction_.reset(); + } +} + void SafeBrowsingDatabaseBloom::UpdateFinished(bool update_succeeded) { if (update_succeeded) BuildBloomFilter(); + insert_transaction_.reset(); + // We won't need the chunk caches until the next update (which will read them // from the database), so free their memory as they may contain thousands of // entries. ClearUpdateCaches(); } -void SafeBrowsingDatabaseBloom::AddEntry(SBPrefix host, SBEntry* entry) { +void SafeBrowsingDatabaseBloom::InsertAdd(SBPrefix host, SBEntry* entry) { STATS_COUNTER(L"SB.HostInsert", 1); + int encoded = EncodeChunkId(entry->chunk_id(), entry->list_id()); + if (entry->type() == SBEntry::ADD_FULL_HASH) { - // TODO(erikkay, paulg): Add the full 256 bit prefix to the full_prefix - // table AND insert its 32 bit prefix into the regular prefix table. + base::Time receive_time = base::Time::Now(); + for (int i = 0; i < entry->prefix_count(); ++i) { + SBPrefix prefix; + SBFullHash full_hash = entry->FullHashAt(i); + memcpy(&prefix, full_hash.full_hash, sizeof(SBPrefix)); + InsertAddPrefix(prefix, encoded); + InsertAddFullHash(prefix, encoded, receive_time, full_hash); + } return; } - int encoded = EncodeChunkId(entry->chunk_id(), entry->list_id()); + + // This entry contains only regular (32 bit) prefixes. int count = entry->prefix_count(); if (count == 0) { - AddPrefix(host, encoded); + InsertAddPrefix(host, encoded); } else { for (int i = 0; i < count; i++) { SBPrefix prefix = entry->PrefixAt(i); - AddPrefix(prefix, encoded); + InsertAddPrefix(prefix, encoded); } } } -void SafeBrowsingDatabaseBloom::AddPrefix(SBPrefix prefix, int encoded_chunk) { +void SafeBrowsingDatabaseBloom::InsertAddPrefix(SBPrefix prefix, + int encoded_chunk) { STATS_COUNTER(L"SB.PrefixAdd", 1); std::string sql = "INSERT INTO add_prefix (chunk, prefix) VALUES (?, ?)"; SQLITE_UNIQUE_STATEMENT(statement, *statement_cache_, sql.c_str()); @@ -454,27 +450,27 @@ void SafeBrowsingDatabaseBloom::AddPrefix(SBPrefix prefix, int encoded_chunk) { } else { DCHECK(rv == SQLITE_DONE); } - bloom_filter_->Insert(prefix); add_count_++; } -void SafeBrowsingDatabaseBloom::AddFullPrefix(SBPrefix prefix, - int encoded_chunk, - SBFullHash full_prefix) { +void SafeBrowsingDatabaseBloom::InsertAddFullHash(SBPrefix prefix, + int encoded_chunk, + base::Time receive_time, + SBFullHash full_prefix) { STATS_COUNTER(L"SB.PrefixAddFull", 1); - std::string sql = "INSERT INTO full_add_prefix " - "(chunk, prefix, receive_time, full_prefix) " - "VALUES (?, ?, ?)"; + std::string sql = "INSERT INTO add_full_hash " + "(chunk, prefix, receive_time, full_hash) " + "VALUES (?,?,?,?)"; SQLITE_UNIQUE_STATEMENT(statement, *statement_cache_, sql.c_str()); if (!statement.is_valid()) { NOTREACHED(); return; } + statement->bind_int(0, encoded_chunk); statement->bind_int(1, prefix); - // TODO(paulg): Add receive_time and full_prefix. - // statement->bind_int64(2, receive_time); - // statement->bind_blob(3, full_prefix); + statement->bind_int64(2, receive_time.ToTimeT()); + statement->bind_blob(3, full_prefix.full_hash, sizeof(SBFullHash)); int rv = statement->step(); statement->reset(); if (rv == SQLITE_CORRUPT) { @@ -484,30 +480,40 @@ void SafeBrowsingDatabaseBloom::AddFullPrefix(SBPrefix prefix, } } -void SafeBrowsingDatabaseBloom::AddSub( +void SafeBrowsingDatabaseBloom::InsertSub( int chunk_id, SBPrefix host, SBEntry* entry) { STATS_COUNTER(L"SB.HostDelete", 1); - if (entry->type() == SBEntry::SUB_FULL_HASH) { - // TODO(erikkay) - return; - } - int encoded = EncodeChunkId(chunk_id, entry->list_id()); - int encoded_add = EncodeChunkId(entry->chunk_id(), entry->list_id()); - int count = entry->prefix_count(); - if (count == 0) { - AddSubPrefix(host, encoded, encoded_add); + int encoded_add; + + if (entry->type() == SBEntry::SUB_FULL_HASH) { + for (int i = 0; i < entry->prefix_count(); ++i) { + SBPrefix prefix; + SBFullHash full_hash = entry->FullHashAt(i); + memcpy(&prefix, full_hash.full_hash, sizeof(SBPrefix)); + encoded_add = EncodeChunkId(entry->ChunkIdAtPrefix(i), entry->list_id()); + InsertSubPrefix(prefix, encoded, encoded_add); + InsertSubFullHash(prefix, encoded, encoded_add, full_hash, false); + } } else { - for (int i = 0; i < count; i++) { - SBPrefix prefix = entry->PrefixAt(i); - AddSubPrefix(prefix, encoded, encoded_add); + // We have prefixes. + int count = entry->prefix_count(); + if (count == 0) { + encoded_add = EncodeChunkId(entry->chunk_id(), entry->list_id()); + InsertSubPrefix(host, encoded, encoded_add); + } else { + for (int i = 0; i < count; i++) { + SBPrefix prefix = entry->PrefixAt(i); + encoded_add = EncodeChunkId(entry->ChunkIdAtPrefix(i), entry->list_id()); + InsertSubPrefix(prefix, encoded, encoded_add); + } } } } -void SafeBrowsingDatabaseBloom::AddSubPrefix(SBPrefix prefix, - int encoded_chunk, - int encoded_add_chunk) { +void SafeBrowsingDatabaseBloom::InsertSubPrefix(SBPrefix prefix, + int encoded_chunk, + int encoded_add_chunk) { STATS_COUNTER(L"SB.PrefixSub", 1); std::string sql = "INSERT INTO sub_prefix (chunk, add_chunk, prefix) VALUES (?,?,?)"; @@ -528,14 +534,20 @@ void SafeBrowsingDatabaseBloom::AddSubPrefix(SBPrefix prefix, } } -void SafeBrowsingDatabaseBloom::SubFullPrefix(SBPrefix prefix, - int encoded_chunk, - int encoded_add_chunk, - SBFullHash full_prefix) { +void SafeBrowsingDatabaseBloom::InsertSubFullHash(SBPrefix prefix, + int encoded_chunk, + int encoded_add_chunk, + SBFullHash full_prefix, + bool use_temp_table) { STATS_COUNTER(L"SB.PrefixSubFull", 1); - std::string sql = "INSERT INTO full_sub_prefix " - "(chunk, add_chunk, prefix, receive_time, full_prefix) " - "VALUES (?,?,?,?)"; + std::string sql = "INSERT INTO "; + if (use_temp_table) { + sql += "sub_full_tmp"; + } else { + sql += "sub_full_hash"; + } + sql += " (chunk, add_chunk, prefix, full_hash) VALUES (?,?,?,?)"; + SQLITE_UNIQUE_STATEMENT(statement, *statement_cache_, sql.c_str()); if (!statement.is_valid()) { NOTREACHED(); @@ -544,9 +556,7 @@ void SafeBrowsingDatabaseBloom::SubFullPrefix(SBPrefix prefix, statement->bind_int(0, encoded_chunk); statement->bind_int(1, encoded_add_chunk); statement->bind_int(2, prefix); - // TODO(paulg): Add receive_time and full_prefix. - // statement->bind_int64(3, receive_time); - // statement->bind_blob(4, full_prefix); + statement->bind_blob(3, full_prefix.full_hash, sizeof(SBFullHash)); int rv = statement->step(); statement->reset(); if (rv == SQLITE_CORRUPT) { @@ -556,10 +566,22 @@ void SafeBrowsingDatabaseBloom::SubFullPrefix(SBPrefix prefix, } } -// TODO(paulg): Look for a less expensive way to maintain add_count_. +void SafeBrowsingDatabaseBloom::ReadFullHash(SqliteCompiledStatement& statement, + int column, + SBFullHash* full_hash) { + DCHECK(full_hash); + std::vector<unsigned char> blob; + statement->column_blob_as_vector(column, &blob); + DCHECK(blob.size() == sizeof(SBFullHash)); + memcpy(full_hash->full_hash, &blob[0], sizeof(SBFullHash)); +} + +// TODO(paulg): Look for a less expensive way to maintain add_count_? If we move +// to a native file format, we can just cache the count in the file and not have +// to scan at all. int SafeBrowsingDatabaseBloom::GetAddPrefixCount() { SQLITE_UNIQUE_STATEMENT(count, *statement_cache_, - "SELECT count(*) FROM add_prefix"); + "SELECT count(*) FROM add_prefix"); if (!count.is_valid()) { NOTREACHED(); return 0; @@ -579,7 +601,7 @@ void SafeBrowsingDatabaseBloom::DeleteChunks( if (chunk_deletes->empty()) return; - int list_id = GetListID(chunk_deletes->front().list_name); + int list_id = GetListId(chunk_deletes->front().list_name); for (size_t i = 0; i < chunk_deletes->size(); ++i) { const SBChunkDelete& chunk = (*chunk_deletes)[i]; @@ -640,100 +662,43 @@ void SafeBrowsingDatabaseBloom::GetChunkIds( void SafeBrowsingDatabaseBloom::GetListsInfo( std::vector<SBListChunkRanges>* lists) { DCHECK(lists); + lists->clear(); ReadChunkNumbers(); - lists->clear(); - SQLITE_UNIQUE_STATEMENT(statement, *statement_cache_, - "SELECT name,id FROM list_names"); - if (!statement.is_valid()) { - NOTREACHED(); - return; - } - - std::vector<std::vector<int> > chunks; - while (true) { - int rv = statement->step(); - if (rv != SQLITE_ROW) { - if (rv == SQLITE_CORRUPT) - HandleCorruptDatabase(); + lists->push_back(SBListChunkRanges(safe_browsing_util::kMalwareList)); + GetChunkIds(MALWARE, ADD_CHUNK, &lists->back().adds); + GetChunkIds(MALWARE, SUB_CHUNK, &lists->back().subs); - break; - } - int list_id = statement->column_int(1); - lists->push_back(SBListChunkRanges(statement->column_string(0))); - std::vector<int> c; - chunks.push_back(c); - GetChunkIds(list_id, ADD_CHUNK, &lists->back().adds); - GetChunkIds(list_id, SUB_CHUNK, &lists->back().subs); - } + lists->push_back(SBListChunkRanges(safe_browsing_util::kPhishingList)); + GetChunkIds(PHISH, ADD_CHUNK, &lists->back().adds); + GetChunkIds(PHISH, SUB_CHUNK, &lists->back().subs); return; } -int SafeBrowsingDatabaseBloom::AddList(const std::string& name) { - SQLITE_UNIQUE_STATEMENT(statement, *statement_cache_, - "INSERT INTO list_names" - "(id,name)" - "VALUES (NULL,?)"); - if (!statement.is_valid()) { - NOTREACHED(); - return 0; - } - - statement->bind_string(0, name); - int rv = statement->step(); - if (rv != SQLITE_DONE) { - if (rv == SQLITE_CORRUPT) { - HandleCorruptDatabase(); - } else { - NOTREACHED(); - } - - return 0; - } - - return static_cast<int>(sqlite3_last_insert_rowid(db_)); -} - -int SafeBrowsingDatabaseBloom::GetListID(const std::string& name) { - SQLITE_UNIQUE_STATEMENT(statement, *statement_cache_, - "SELECT id FROM list_names WHERE name=?"); - if (!statement.is_valid()) { - NOTREACHED(); - return 0; - } - - statement->bind_string(0, name); - int result = statement->step(); - if (result == SQLITE_ROW) - return statement->column_int(0); +/* static */ +int SafeBrowsingDatabaseBloom::GetListId(const std::string& name) { + if (name == safe_browsing_util::kMalwareList) + return MALWARE; + else if (name == safe_browsing_util::kPhishingList) + return PHISH; - if (result == SQLITE_CORRUPT) - HandleCorruptDatabase(); - - // There isn't an existing entry so add one. - return AddList(name); + NOTREACHED(); + return -1; } -std::string SafeBrowsingDatabaseBloom::GetListName(int id) { - SQLITE_UNIQUE_STATEMENT(statement, *statement_cache_, - "SELECT name FROM list_names WHERE id=?"); - if (!statement.is_valid()) { - NOTREACHED(); - return 0; - } - - statement->bind_int(0, id); - int result = statement->step(); - if (result != SQLITE_ROW) { - if (result == SQLITE_CORRUPT) - HandleCorruptDatabase(); - - return std::string(); +/* static */ +std::string SafeBrowsingDatabaseBloom::GetListName(int list_id) { + switch (list_id) { + case MALWARE: + return safe_browsing_util::kMalwareList; + case PHISH: + return safe_browsing_util::kPhishingList; + default: + NOTREACHED(); + return ""; } - - return statement->column_string(0); } void SafeBrowsingDatabaseBloom::ReadChunkNumbers() { @@ -863,11 +828,11 @@ int SafeBrowsingDatabaseBloom::PairCompare(const void* arg1, const void* arg2) { return delta; } -bool SafeBrowsingDatabaseBloom::BuildAddList(SBPair* adds) { +bool SafeBrowsingDatabaseBloom::BuildAddPrefixList(SBPair* adds) { // Read add_prefix into memory and sort it. STATS_COUNTER(L"SB.HostSelectForBloomFilter", 1); SQLITE_UNIQUE_STATEMENT(add_prefix, *statement_cache_, - "SELECT chunk, prefix FROM add_prefix"); + "SELECT chunk, prefix FROM add_prefix"); if (!add_prefix.is_valid()) { NOTREACHED(); return false; @@ -894,11 +859,14 @@ bool SafeBrowsingDatabaseBloom::BuildAddList(SBPair* adds) { return true; } -bool SafeBrowsingDatabaseBloom::RemoveSubs(SBPair* adds, - std::vector<bool>* adds_removed) { +bool SafeBrowsingDatabaseBloom::RemoveSubs( + SBPair* adds, std::vector<bool>* adds_removed, + HashCache* add_cache, HashCache* sub_cache) { + DCHECK(add_cache && sub_cache); + // Read through sub_prefix and zero out add_prefix entries that match. SQLITE_UNIQUE_STATEMENT(sub_prefix, *statement_cache_, - "SELECT chunk, add_chunk, prefix FROM sub_prefix"); + "SELECT chunk, add_chunk, prefix FROM sub_prefix"); if (!sub_prefix.is_valid()) { NOTREACHED(); return false; @@ -916,6 +884,16 @@ bool SafeBrowsingDatabaseBloom::RemoveSubs(SBPair* adds, return false; } + // Create a temporary sub full hash table, similar to the above prefix table. + if (sqlite3_exec(db_, "CREATE TABLE sub_full_tmp (" + "chunk INTEGER," + "add_chunk INTEGER," + "prefix INTEGER," + "full_hash BLOB)", + NULL, NULL, NULL) != SQLITE_OK) { + return false; + } + SQLITE_UNIQUE_STATEMENT( sub_prefix_tmp, *statement_cache_, @@ -945,17 +923,21 @@ bool SafeBrowsingDatabaseBloom::RemoveSubs(SBPair* adds, if (sub_del_cache_.find(sub_chunk) != sub_del_cache_.end()) continue; - void* match = bsearch(&sub, adds, add_count_, sizeof(SBPair), + void* match = bsearch(&sub, adds, add_count_, sizeof(SBPair), &SafeBrowsingDatabaseBloom::PairCompare); if (match) { SBPair* subbed = reinterpret_cast<SBPair*>(match); (*adds_removed)[subbed - adds] = true; + // Remove any GetHash results (full hashes) that match this sub, as well + // as removing any full subs we may have received. + ClearCachedEntry(sub.prefix, sub.chunk_id, add_cache); + ClearCachedEntry(sub.prefix, sub.chunk_id, sub_cache); } else { // This sub_prefix entry did not match any add, so we keep it around. sub_prefix_tmp->bind_int(0, sub_chunk); sub_prefix_tmp->bind_int(1, sub.chunk_id); sub_prefix_tmp->bind_int(2, sub.prefix); - int rv = sub_prefix_tmp->step(); + rv = sub_prefix_tmp->step(); if (rv == SQLITE_CORRUPT) { HandleCorruptDatabase(); return false; @@ -964,7 +946,7 @@ bool SafeBrowsingDatabaseBloom::RemoveSubs(SBPair* adds, sub_prefix_tmp->reset(); } } - + return true; } @@ -996,8 +978,7 @@ bool SafeBrowsingDatabaseBloom::UpdateTables() { } DCHECK(rv == SQLITE_DONE); - // Now blow away add_prefix and re-write it from our in-memory data, - // building the new bloom filter as we go. + // Now blow away add_prefix. We will write the new values out later. SQLITE_UNIQUE_STATEMENT(del_add, *statement_cache_, "DELETE FROM add_prefix"); if (!del_add.is_valid()) { NOTREACHED(); @@ -1010,6 +991,48 @@ bool SafeBrowsingDatabaseBloom::UpdateTables() { } DCHECK(rv == SQLITE_DONE); + // Delete the old sub_full_hash table and rename the temp full hash table. + SQLITE_UNIQUE_STATEMENT(del_full_sub, *statement_cache_, + "DROP TABLE sub_full_hash"); + if (!del_full_sub.is_valid()) { + NOTREACHED(); + return false; + } + + rv = del_full_sub->step(); + if (rv == SQLITE_CORRUPT) { + HandleCorruptDatabase(); + return false; + } + DCHECK(rv == SQLITE_DONE); + + SQLITE_UNIQUE_STATEMENT(rename_full_sub, *statement_cache_, + "ALTER TABLE sub_full_tmp RENAME TO sub_full_hash"); + if (!rename_full_sub.is_valid()) { + NOTREACHED(); + return false; + } + rv = rename_full_sub->step(); + if (rv == SQLITE_CORRUPT) { + HandleCorruptDatabase(); + return false; + } + DCHECK(rv == SQLITE_DONE); + + // Blow away all the full adds. We will write the new values out later. + SQLITE_UNIQUE_STATEMENT(del_full_add, *statement_cache_, + "DELETE FROM add_full_hash"); + if (!del_full_add.is_valid()) { + NOTREACHED(); + return false; + } + rv = del_full_add->step(); + if (rv == SQLITE_CORRUPT) { + HandleCorruptDatabase(); + return false; + } + DCHECK(rv == SQLITE_DONE); + return true; } @@ -1064,11 +1087,158 @@ bool SafeBrowsingDatabaseBloom::WritePrefixes( return true; } +void SafeBrowsingDatabaseBloom::WriteFullHashes(HashCache* hash_cache, + bool is_add) { + DCHECK(hash_cache); + HashCache::iterator it = hash_cache->begin(); + for (; it != hash_cache->end(); ++it) { + const HashList& entries = it->second; + WriteFullHashList(entries, is_add); + } +} + +void SafeBrowsingDatabaseBloom::WriteFullHashList(const HashList& hash_list, + bool is_add) { + HashList::const_iterator lit = hash_list.begin(); + for (; lit != hash_list.end(); ++lit) { + const HashCacheEntry& entry = *lit; + SBPrefix prefix; + memcpy(&prefix, entry.full_hash.full_hash, sizeof(SBPrefix)); + if (is_add) { + if (add_del_cache_.find(entry.add_chunk_id) == add_del_cache_.end()) { + InsertAddFullHash(prefix, entry.add_chunk_id, + entry.received, entry.full_hash); + } + } else { + if (sub_del_cache_.find(entry.sub_chunk_id) == sub_del_cache_.end()) { + InsertSubFullHash(prefix, entry.sub_chunk_id, + entry.add_chunk_id, entry.full_hash, true); + } + } + } +} + +bool SafeBrowsingDatabaseBloom::BuildAddFullHashCache(HashCache* add_cache) { + add_cache->clear(); + + // Read all full add entries to the cache. + SQLITE_UNIQUE_STATEMENT( + full_add_entry, + *statement_cache_, + "SELECT chunk, prefix, receive_time, full_hash FROM add_full_hash"); + if (!full_add_entry.is_valid()) { + NOTREACHED(); + return false; + } + + int rv; + while (true) { + rv = full_add_entry->step(); + if (rv != SQLITE_ROW) { + if (rv == SQLITE_CORRUPT) { + HandleCorruptDatabase(); + return false; + } + break; + } + HashCacheEntry entry; + entry.add_chunk_id = full_add_entry->column_int(0); + if (add_del_cache_.find(entry.add_chunk_id) != add_del_cache_.end()) + continue; // This entry's chunk was deleted so we skip it. + SBPrefix prefix = full_add_entry->column_int(1); + entry.received = base::Time::FromTimeT(full_add_entry->column_int64(2)); + int chunk, list_id; + DecodeChunkId(entry.add_chunk_id, &chunk, &list_id); + entry.list_id = list_id; + ReadFullHash(full_add_entry, 3, &entry.full_hash); + HashList& entries = (*add_cache)[prefix]; + entries.push_back(entry); + } + + // Clear the full add table. + SQLITE_UNIQUE_STATEMENT(full_add_drop, *statement_cache_, + "DELETE FROM add_full_hash"); + if (!full_add_drop.is_valid()) { + NOTREACHED(); + return false; + } + rv = full_add_drop->step(); + if (rv == SQLITE_CORRUPT) { + HandleCorruptDatabase(); + return false; + } + DCHECK(rv == SQLITE_DONE); + + return true; +} + +bool SafeBrowsingDatabaseBloom::BuildSubFullHashCache(HashCache* sub_cache) { + sub_cache->clear(); + + // Read all full sub entries to the cache. + SQLITE_UNIQUE_STATEMENT( + full_sub_entry, + *statement_cache_, + "SELECT chunk, add_chunk, prefix, full_hash FROM sub_full_hash"); + if (!full_sub_entry.is_valid()) { + NOTREACHED(); + return false; + } + + int rv; + while (true) { + rv = full_sub_entry->step(); + if (rv != SQLITE_ROW) { + if (rv == SQLITE_CORRUPT) { + HandleCorruptDatabase(); + return false; + } + break; + } + HashCacheEntry entry; + entry.sub_chunk_id = full_sub_entry->column_int(0); + if (sub_del_cache_.find(entry.sub_chunk_id) != sub_del_cache_.end()) + continue; // This entry's chunk was deleted so we skip it. + entry.add_chunk_id = full_sub_entry->column_int(1); + SBPrefix prefix = full_sub_entry->column_int(2); + int chunk, list_id; + DecodeChunkId(entry.add_chunk_id, &chunk, &list_id); + entry.list_id = list_id; + ReadFullHash(full_sub_entry, 3, &entry.full_hash); + HashList& entries = (*sub_cache)[prefix]; + entries.push_back(entry); + } + + // Clear the full sub table. + SQLITE_UNIQUE_STATEMENT(full_sub_drop, *statement_cache_, + "DELETE FROM sub_full_hash"); + if (!full_sub_drop.is_valid()) { + NOTREACHED(); + return false; + } + rv = full_sub_drop->step(); + if (rv == SQLITE_CORRUPT) { + HandleCorruptDatabase(); + return false; + } + DCHECK(rv == SQLITE_DONE); + + return true; +} + // TODO(erikkay): should we call WaitAfterResume() inside any of the loops here? // This is a pretty fast operation and it would be nice to let it finish. void SafeBrowsingDatabaseBloom::BuildBloomFilter() { Time before = Time::Now(); + // Get all the pending GetHash results and write them to disk. + HashList pending_hashes; + { + AutoLock lock(lookup_lock_); + pending_hashes.swap(pending_full_hashes_); + } + WriteFullHashList(pending_hashes, true); + add_count_ = GetAddPrefixCount(); if (add_count_ == 0) { bloom_filter_ = NULL; @@ -1078,16 +1248,18 @@ void SafeBrowsingDatabaseBloom::BuildBloomFilter() { scoped_array<SBPair> adds_array(new SBPair[add_count_]); SBPair* adds = adds_array.get(); - if (!BuildAddList(adds)) + if (!BuildAddPrefixList(adds)) return; - // Protect the remaining operations on the database with a transaction. - scoped_ptr<SQLTransaction> update_transaction; - update_transaction.reset(new SQLTransaction(db_)); - if (update_transaction->Begin() != SQLITE_OK) { - NOTREACHED(); + // Build the full add cache, which includes full hash updates and GetHash + // results. Subs may remove some of these entries. + scoped_ptr<HashCache> add_cache(new HashCache); + if (!BuildAddFullHashCache(add_cache.get())) + return; + + scoped_ptr<HashCache> sub_cache(new HashCache); + if (!BuildSubFullHashCache(sub_cache.get())) return; - } // Used to track which adds have been subbed out. The vector<bool> is actually // a bitvector so the size is as small as we can get. @@ -1095,22 +1267,34 @@ void SafeBrowsingDatabaseBloom::BuildBloomFilter() { adds_removed.resize(add_count_, false); // Flag any add as removed if there is a matching sub. - if (!RemoveSubs(adds, &adds_removed)) + if (!RemoveSubs(adds, &adds_removed, add_cache.get(), sub_cache.get())) return; // Prepare the database for writing out our remaining add and sub prefixes. if (!UpdateTables()) return; - // Write out the remaining adds to the filter and database. + // Write out the remaining add prefixes to the filter and database. int new_count; BloomFilter* filter; if (!WritePrefixes(adds, adds_removed, &new_count, &filter)) return; - // If there were any matching subs, the size will be smaller. - add_count_ = new_count; - bloom_filter_ = filter; + // Write out the remaining full hash adds and subs to the database. + WriteFullHashes(add_cache.get(), true); + WriteFullHashes(sub_cache.get(), false); + + // Save the chunk numbers we've received to the database for reporting in + // future update requests. + if (!WriteChunkNumbers()) + return; + + // Commit all the changes to the database. + int rv = insert_transaction_->Commit(); + if (rv != SQLITE_OK) { + NOTREACHED() << "SafeBrowsing update transaction failed to commit."; + return; + } TimeDelta bloom_gen = Time::Now() - before; SB_DLOG(INFO) << "SafeBrowsingDatabaseImpl built bloom filter in " @@ -1118,13 +1302,14 @@ void SafeBrowsingDatabaseBloom::BuildBloomFilter() { << " ms total. prefix count: "<< add_count_; UMA_HISTOGRAM_LONG_TIMES(L"SB.BuildBloom", bloom_gen); - // Save the chunk numbers we've received to the database for reporting in - // future update requests. - if (!WriteChunkNumbers()) - return; - - // We've made it this far without errors, so commit the changes. - update_transaction->Commit(); + // Swap in the newly built filter and cache. If there were any matching subs, + // the size (add_count_) will be smaller. + { + AutoLock lock(lookup_lock_); + add_count_ = new_count; + bloom_filter_ = filter; + hash_cache_.swap(add_cache); + } // Persist the bloom filter to disk. WriteBloomFilter(); @@ -1140,13 +1325,17 @@ void SafeBrowsingDatabaseBloom::GetCachedFullHashes( for (std::vector<SBPrefix>::const_iterator it = prefix_hits->begin(); it != prefix_hits->end(); ++it) { - HashCache::iterator hit = hash_cache_.find(*it); - if (hit != hash_cache_.end()) { + HashCache::iterator hit = hash_cache_->find(*it); + if (hit != hash_cache_->end()) { HashList& entries = hit->second; HashList::iterator eit = entries.begin(); while (eit != entries.end()) { // An entry is valid if we've received an update in the past 45 minutes, // or if this particular GetHash was received in the past 45 minutes. + // If an entry is does not meet the time criteria above, we are not + // allowed to use it since it might have become stale. We keep it + // around, though, and may be able to use it in the future once we + // receive the next update (that doesn't sub it). if (max_age < last_update || eit->received > max_age) { SBFullHashResult full_hash; memcpy(&full_hash.hash.full_hash, @@ -1155,15 +1344,12 @@ void SafeBrowsingDatabaseBloom::GetCachedFullHashes( full_hash.list_name = GetListName(eit->list_id); full_hash.add_chunk_id = eit->add_chunk_id; full_hits->push_back(full_hash); - ++eit; - } else { - // Evict the expired entry. - eit = entries.erase(eit); } + ++eit; } if (entries.empty()) - hash_cache_.erase(hit); + hash_cache_->erase(hit); } } } @@ -1171,6 +1357,8 @@ void SafeBrowsingDatabaseBloom::GetCachedFullHashes( void SafeBrowsingDatabaseBloom::CacheHashResults( const std::vector<SBPrefix>& prefixes, const std::vector<SBFullHashResult>& full_hits) { + AutoLock lock(lookup_lock_); + if (full_hits.empty()) { // These prefixes returned no results, so we store them in order to prevent // asking for them again. We flush this cache at the next update. @@ -1185,51 +1373,44 @@ void SafeBrowsingDatabaseBloom::CacheHashResults( for (std::vector<SBFullHashResult>::const_iterator it = full_hits.begin(); it != full_hits.end(); ++it) { SBPrefix prefix; - memcpy(&prefix, &it->hash.full_hash, sizeof(prefix)); - HashList& entries = hash_cache_[prefix]; + memcpy(&prefix, &it->hash.full_hash, sizeof(SBPrefix)); + HashList& entries = (*hash_cache_)[prefix]; HashCacheEntry entry; entry.received = now; - entry.list_id = GetListID(it->list_name); - entry.add_chunk_id = it->add_chunk_id; + entry.list_id = GetListId(it->list_name); + entry.add_chunk_id = EncodeChunkId(it->add_chunk_id, entry.list_id); memcpy(&entry.full_hash, &it->hash.full_hash, sizeof(SBFullHash)); entries.push_back(entry); - } -} -void SafeBrowsingDatabaseBloom::ClearCachedHashes(const SBEntry* entry) { - for (int i = 0; i < entry->prefix_count(); ++i) { - SBPrefix prefix; - if (entry->type() == SBEntry::SUB_FULL_HASH) - memcpy(&prefix, &entry->FullHashAt(i), sizeof(SBPrefix)); - else - prefix = entry->PrefixAt(i); - - HashCache::iterator it = hash_cache_.find(prefix); - if (it != hash_cache_.end()) - hash_cache_.erase(it); + // Also push a copy to the pending write queue. + pending_full_hashes_.push_back(entry); } } -// This clearing algorithm is a little inefficient, but we don't expect there to -// be too many entries for this to matter. Also, this runs as a background task -// during an update, so no user action is blocking on it. -void SafeBrowsingDatabaseBloom::ClearCachedHashesForChunk(int list_id, - int add_chunk_id) { - HashCache::iterator it = hash_cache_.begin(); - while (it != hash_cache_.end()) { - HashList& entries = it->second; - HashList::iterator eit = entries.begin(); - while (eit != entries.end()) { - if (eit->list_id == list_id && eit->add_chunk_id == add_chunk_id) - eit = entries.erase(eit); - else - ++eit; +bool SafeBrowsingDatabaseBloom::ClearCachedEntry(SBPrefix prefix, + int add_chunk, + HashCache* hash_cache) { + bool match = false; + HashCache::iterator it = hash_cache->find(prefix); + if (it == hash_cache->end()) + return match; + + HashList& entries = it->second; + HashList::iterator lit = entries.begin(); + while (lit != entries.end()) { + HashCacheEntry& entry = *lit; + if (entry.add_chunk_id == add_chunk) { + lit = entries.erase(lit); + match = true; + continue; } - if (entries.empty()) - hash_cache_.erase(it++); - else - ++it; + ++lit; } + + if (entries.empty()) + hash_cache->erase(it); + + return match; } void SafeBrowsingDatabaseBloom::HandleCorruptDatabase() { diff --git a/chrome/browser/safe_browsing/safe_browsing_database_bloom.h b/chrome/browser/safe_browsing/safe_browsing_database_bloom.h index 9569a43..9cebde6 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database_bloom.h +++ b/chrome/browser/safe_browsing/safe_browsing_database_bloom.h @@ -13,6 +13,7 @@ #include <vector> #include "base/hash_tables.h" +#include "base/lock.h" #include "base/scoped_ptr.h" #include "base/task.h" #include "base/time.h" @@ -57,7 +58,7 @@ class SafeBrowsingDatabaseBloom : public SafeBrowsingDatabase { // Returns the lists and their add/sub chunks. virtual void GetListsInfo(std::vector<SBListChunkRanges>* lists); - // Does nothing in this implementation. Operations in this class are + // Does nothing in this implementation. Operations in this class are // always synchronous. virtual void SetSynchronous(); @@ -71,7 +72,9 @@ class SafeBrowsingDatabaseBloom : public SafeBrowsingDatabase { // Called when the user's machine has resumed from a lower power state. virtual void HandleResume(); + virtual void UpdateStarted(); virtual void UpdateFinished(bool update_succeeded); + virtual bool NeedToCheckUrl(const GURL& url); private: @@ -107,15 +110,14 @@ class SafeBrowsingDatabaseBloom : public SafeBrowsingDatabase { // the given list and chunk type. void GetChunkIds(int list_id, ChunkType type, std::string* list); - // Adds the given list to the database. Returns its row id. - int AddList(const std::string& name); - - // Given a list name, returns its internal id. If we haven't seen it before, - // an id is created and stored in the database. On error, returns 0. - int GetListID(const std::string& name); - - // Given a list id, returns its name. - std::string GetListName(int id); + // Converts between the SafeBrowsing list names and their enumerated value. + // If the list names change, both of these methods must be updated. + enum ListType { + MALWARE = 0, + PHISH = 1, + }; + static int GetListId(const std::string& name); + static std::string GetListName(int list_id); // Generate a bloom filter. virtual void BuildBloomFilter(); @@ -128,11 +130,19 @@ class SafeBrowsingDatabaseBloom : public SafeBrowsingDatabase { static int PairCompare(const void* arg1, const void* arg2); - bool BuildAddList(SBPair* adds); - bool RemoveSubs(SBPair* adds, std::vector<bool>* adds_removed); + bool BuildAddPrefixList(SBPair* adds); + bool BuildAddFullHashCache(HashCache* add_cache); + bool BuildSubFullHashCache(HashCache* sub_cache); + bool RemoveSubs(SBPair* adds, + std::vector<bool>* adds_removed, + HashCache* add_cache, + HashCache* sub_cache); + bool UpdateTables(); bool WritePrefixes(SBPair* adds, const std::vector<bool>& adds_removed, int* new_add_count, BloomFilter** filter); + void WriteFullHashes(HashCache* hash_cache, bool is_add); + void WriteFullHashList(const HashList& hash_list, bool is_add); // Looks up any cached full hashes we may have. void GetCachedFullHashes(const std::vector<SBPrefix>* prefix_hits, @@ -140,10 +150,7 @@ class SafeBrowsingDatabaseBloom : public SafeBrowsingDatabase { base::Time last_update); // Remove cached entries that have prefixes contained in the entry. - void ClearCachedHashes(const SBEntry* entry); - - // Remove all GetHash entries that match the list and chunk id from an AddDel. - void ClearCachedHashesForChunk(int list_id, int add_chunk_id); + bool ClearCachedEntry(SBPrefix, int add_chunk_id, HashCache* hash_cache); void HandleCorruptDatabase(); void OnHandleCorruptDatabase(); @@ -156,18 +163,32 @@ class SafeBrowsingDatabaseBloom : public SafeBrowsingDatabase { // flag. This method should be called periodically inside of busy disk loops. void WaitAfterResume(); - void AddEntry(SBPrefix host, SBEntry* entry); - void AddPrefix(SBPrefix prefix, int encoded_chunk); - void AddSub(int chunk, SBPrefix host, SBEntry* entry); - void AddSubPrefix(SBPrefix prefix, int encoded_chunk, int encoded_add_chunk); + // Adding add entries to the database. + void InsertAdd(SBPrefix host, SBEntry* entry); + void InsertAddPrefix(SBPrefix prefix, int encoded_chunk); + void InsertAddFullHash(SBPrefix prefix, + int encoded_chunk, + base::Time received_time, + SBFullHash full_prefix); + + // Adding sub entries to the database. + void InsertSub(int chunk, SBPrefix host, SBEntry* entry); + void InsertSubPrefix(SBPrefix prefix, + int encoded_chunk, + int encoded_add_chunk); + void InsertSubFullHash(SBPrefix prefix, + int encoded_chunk, + int encoded_add_chunk, + SBFullHash full_prefix, + bool use_temp_table); + + // Used for reading full hashes from the database. + void ReadFullHash(SqliteCompiledStatement& statement, + int column, + SBFullHash* full_hash); + + // Returns the number of chunk + prefix pairs in the add prefix table. int GetAddPrefixCount(); - void AddFullPrefix(SBPrefix prefix, - int encoded_chunk, - SBFullHash full_prefix); - void SubFullPrefix(SBPrefix prefix, - int encoded_chunk, - int encoded_add_chunk, - SBFullHash full_prefix); // Reads and writes chunk numbers to and from persistent store. void ReadChunkNumbers(); @@ -178,7 +199,6 @@ class SafeBrowsingDatabaseBloom : public SafeBrowsingDatabase { // Encode the list id in the lower bit of the chunk. static inline int EncodeChunkId(int chunk, int list_id) { - list_id--; DCHECK(list_id == 0 || list_id == 1); chunk = chunk << 1; chunk |= list_id; @@ -187,7 +207,7 @@ class SafeBrowsingDatabaseBloom : public SafeBrowsingDatabase { // Split an encoded chunk id and return the original chunk id and list id. static inline void DecodeChunkId(int encoded, int* chunk, int* list_id) { - *list_id = 1 + (encoded & 0x1); + *list_id = encoded & 0x1; *chunk = encoded >> 1; } @@ -200,8 +220,6 @@ class SafeBrowsingDatabaseBloom : public SafeBrowsingDatabase { // True iff the database has been opened successfully. bool init_; - std::wstring filename_; - // Called after an add/sub chunk is processed. scoped_ptr<Callback0::Type> chunk_inserted_callback_; @@ -211,21 +229,6 @@ class SafeBrowsingDatabaseBloom : public SafeBrowsingDatabase { // Used to schedule resuming from a lower power state. ScopedRunnableMethodFactory<SafeBrowsingDatabaseBloom> resume_factory_; - // Used for caching GetHash results. - typedef struct HashCacheEntry { - SBFullHash full_hash; - int list_id; - int add_chunk_id; - base::Time received; - } HashCacheEntry; - - typedef std::list<HashCacheEntry> HashList; - typedef base::hash_map<SBPrefix, HashList> HashCache; - HashCache hash_cache_; - - // Cache of prefixes that returned empty results (no full hash match). - std::set<SBPrefix> prefix_miss_cache_; - // Caches for all of the existing add and sub chunks. std::set<int> add_chunk_cache_; std::set<int> sub_chunk_cache_; @@ -243,6 +246,15 @@ class SafeBrowsingDatabaseBloom : public SafeBrowsingDatabase { // it's presumably going to be pretty busy. bool did_resume_; + // Transaction for protecting database integrity during updates. + scoped_ptr<SQLTransaction> insert_transaction_; + + // Lock for protecting access to the bloom filter and hash cache. + Lock lookup_lock_; + + // A store for GetHash results that have not yet been written to the database. + HashList pending_full_hashes_; + DISALLOW_COPY_AND_ASSIGN(SafeBrowsingDatabaseBloom); }; diff --git a/chrome/browser/safe_browsing/safe_browsing_database_impl.cc b/chrome/browser/safe_browsing/safe_browsing_database_impl.cc index 6414a0e..34adc83 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database_impl.cc +++ b/chrome/browser/safe_browsing/safe_browsing_database_impl.cc @@ -109,6 +109,7 @@ bool SafeBrowsingDatabaseImpl::Init(const std::wstring& filename, init_ = true; chunk_inserted_callback_.reset(chunk_inserted_callback); + return true; } @@ -129,6 +130,8 @@ bool SafeBrowsingDatabaseImpl::Open() { bloom_read_factory_.RevokeAll(); bloom_write_factory_.RevokeAll(); + hash_cache_.reset(new HashCache); + return true; } @@ -216,7 +219,7 @@ bool SafeBrowsingDatabaseImpl::CreateTables() { // The SafeBrowsing service assumes this operation is synchronous. bool SafeBrowsingDatabaseImpl::ResetDatabase() { - hash_cache_.clear(); + hash_cache_->clear(); prefix_miss_cache_.clear(); bool rv = Close(); @@ -1091,8 +1094,8 @@ void SafeBrowsingDatabaseImpl::GetCachedFullHashes( for (std::vector<SBPrefix>::const_iterator it = prefix_hits->begin(); it != prefix_hits->end(); ++it) { - HashCache::iterator hit = hash_cache_.find(*it); - if (hit != hash_cache_.end()) { + HashCache::iterator hit = hash_cache_->find(*it); + if (hit != hash_cache_->end()) { HashList& entries = hit->second; HashList::iterator eit = entries.begin(); while (eit != entries.end()) { @@ -1114,7 +1117,7 @@ void SafeBrowsingDatabaseImpl::GetCachedFullHashes( } if (entries.empty()) - hash_cache_.erase(hit); + hash_cache_->erase(hit); } } } @@ -1137,7 +1140,7 @@ void SafeBrowsingDatabaseImpl::CacheHashResults( it != full_hits.end(); ++it) { SBPrefix prefix; memcpy(&prefix, &it->hash.full_hash, sizeof(prefix)); - HashList& entries = hash_cache_[prefix]; + HashList& entries = (*hash_cache_)[prefix]; HashCacheEntry entry; entry.received = now; entry.list_id = GetListID(it->list_name); @@ -1155,9 +1158,9 @@ void SafeBrowsingDatabaseImpl::ClearCachedHashes(const SBEntry* entry) { else prefix = entry->PrefixAt(i); - HashCache::iterator it = hash_cache_.find(prefix); - if (it != hash_cache_.end()) - hash_cache_.erase(it); + HashCache::iterator it = hash_cache_->find(prefix); + if (it != hash_cache_->end()) + hash_cache_->erase(it); } } @@ -1166,8 +1169,8 @@ void SafeBrowsingDatabaseImpl::ClearCachedHashes(const SBEntry* entry) { // during an update, so no user action is blocking on it. void SafeBrowsingDatabaseImpl::ClearCachedHashesForChunk(int list_id, int add_chunk_id) { - HashCache::iterator it = hash_cache_.begin(); - while (it != hash_cache_.end()) { + HashCache::iterator it = hash_cache_->begin(); + while (it != hash_cache_->end()) { HashList& entries = it->second; HashList::iterator eit = entries.begin(); while (eit != entries.end()) { @@ -1177,7 +1180,7 @@ void SafeBrowsingDatabaseImpl::ClearCachedHashesForChunk(int list_id, ++eit; } if (entries.empty()) - hash_cache_.erase(it++); + hash_cache_->erase(it++); else ++it; } diff --git a/chrome/browser/safe_browsing/safe_browsing_database_impl.h b/chrome/browser/safe_browsing/safe_browsing_database_impl.h index 4b47859..a69b419 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database_impl.h +++ b/chrome/browser/safe_browsing/safe_browsing_database_impl.h @@ -21,8 +21,6 @@ #include "chrome/common/sqlite_compiled_statement.h" #include "chrome/common/sqlite_utils.h" -//class BloomFilter; - // The reference implementation database using SQLite. class SafeBrowsingDatabaseImpl : public SafeBrowsingDatabase { public: @@ -71,8 +69,6 @@ class SafeBrowsingDatabaseImpl : public SafeBrowsingDatabase { virtual void HandleResume(); private: - friend class SafeBrowsingDatabaseImpl_HashCaching_Test; - // Opens the database. bool Open(); @@ -212,8 +208,6 @@ class SafeBrowsingDatabaseImpl : public SafeBrowsingDatabase { // True iff the database has been opened successfully. bool init_; - std::wstring filename_; - // Controls whether database writes are done synchronously in one go or // asynchronously in small chunks. bool asynchronous_; @@ -259,21 +253,6 @@ class SafeBrowsingDatabaseImpl : public SafeBrowsingDatabase { // Used to schedule resuming from a lower power state. ScopedRunnableMethodFactory<SafeBrowsingDatabaseImpl> resume_factory_; - // Used for caching GetHash results. - typedef struct HashCacheEntry { - SBFullHash full_hash; - int list_id; - int add_chunk_id; - base::Time received; - } HashCacheEntry; - - typedef std::list<HashCacheEntry> HashList; - typedef base::hash_map<SBPrefix, HashList> HashCache; - HashCache hash_cache_; - - // Cache of prefixes that returned empty results (no full hash match). - std::set<SBPrefix> prefix_miss_cache_; - // The amount of time, in milliseconds, to wait before the next disk write. int disk_delay_; diff --git a/chrome/browser/safe_browsing/safe_browsing_database_impl_unittest.cc b/chrome/browser/safe_browsing/safe_browsing_database_impl_unittest.cc deleted file mode 100644 index 614fa1b..0000000 --- a/chrome/browser/safe_browsing/safe_browsing_database_impl_unittest.cc +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// Unit tests for the SafeBrowsing storage system (specific to the -// SafeBrowsingDatabaseImpl implementation). - -#include "base/file_util.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "base/process_util.h" -#include "base/sha2.h" -#include "base/stats_counters.h" -#include "base/string_util.h" -#include "base/time.h" -#include "chrome/browser/safe_browsing/protocol_parser.h" -#include "chrome/browser/safe_browsing/safe_browsing_database_impl.h" -#include "googleurl/src/gurl.h" -#include "testing/gtest/include/gtest/gtest.h" - -using base::Time; -using base::TimeDelta; - -namespace { - SBPrefix Sha256Prefix(const std::string& str) { - SBPrefix hash; - base::SHA256HashString(str, &hash, sizeof(hash)); - return hash; - } - -// Helper function to do an AddDel or SubDel command. -void DelChunk(SafeBrowsingDatabase* db, - const std::string& list, - int chunk_id, - bool is_sub_del) { - std::vector<SBChunkDelete>* deletes = new std::vector<SBChunkDelete>; - SBChunkDelete chunk_delete; - chunk_delete.list_name = list; - chunk_delete.is_sub_del = is_sub_del; - chunk_delete.chunk_del.push_back(ChunkRange(chunk_id)); - deletes->push_back(chunk_delete); - db->DeleteChunks(deletes); -} - -void AddDelChunk(SafeBrowsingDatabase* db, - const std::string& list, - int chunk_id) { - DelChunk(db, list, chunk_id, false); -} - -} - -// Utility function for setting up the database for the caching test. -void PopulateDatabaseForCacheTest(SafeBrowsingDatabase* database) { - // Add a simple chunk with one hostkey and cache it. - SBChunkHost host; - host.host = Sha256Prefix("www.evil.com/"); - host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 2); - host.entry->set_chunk_id(1); - host.entry->SetPrefixAt(0, Sha256Prefix("www.evil.com/phishing.html")); - host.entry->SetPrefixAt(1, Sha256Prefix("www.evil.com/malware.html")); - - SBChunk chunk; - chunk.chunk_number = 1; - chunk.is_add = true; - chunk.hosts.push_back(host); - - std::deque<SBChunk>* chunks = new std::deque<SBChunk>; - chunks->push_back(chunk); - database->InsertChunks("goog-malware-shavar", chunks); - - // Add the GetHash results to the cache. - SBFullHashResult full_hash; - base::SHA256HashString("www.evil.com/phishing.html", - &full_hash.hash, sizeof(SBFullHash)); - full_hash.list_name = "goog-malware-shavar"; - full_hash.add_chunk_id = 1; - - std::vector<SBFullHashResult> results; - results.push_back(full_hash); - - base::SHA256HashString("www.evil.com/malware.html", - &full_hash.hash, sizeof(SBFullHash)); - results.push_back(full_hash); - - std::vector<SBPrefix> prefixes; - database->CacheHashResults(prefixes, results); -} - -TEST(SafeBrowsingDatabaseImpl, HashCaching) { - std::wstring filename; - PathService::Get(base::DIR_TEMP, &filename); - filename.push_back(file_util::kPathSeparator); - filename.append(L"SafeBrowsingTestDatabase"); - file_util::Delete(filename, false); // In case it existed from a previous run. - - SafeBrowsingDatabaseImpl database; - database.SetSynchronous(); - EXPECT_TRUE(database.Init(filename, NULL)); - - PopulateDatabaseForCacheTest(&database); - - // We should have both full hashes in the cache. - EXPECT_EQ(database.hash_cache_.size(), 2U); - - // Test the cache lookup for the first prefix. - std::string list; - std::vector<SBPrefix> prefixes; - std::vector<SBFullHashResult> full_hashes; - database.ContainsUrl(GURL("http://www.evil.com/phishing.html"), - &list, &prefixes, &full_hashes, Time::Now()); - EXPECT_EQ(full_hashes.size(), 1U); - - SBFullHashResult full_hash; - base::SHA256HashString("www.evil.com/phishing.html", - &full_hash.hash, sizeof(SBFullHash)); - EXPECT_EQ(memcmp(&full_hashes[0].hash, - &full_hash.hash, sizeof(SBFullHash)), 0); - - prefixes.clear(); - full_hashes.clear(); - - // Test the cache lookup for the second prefix. - database.ContainsUrl(GURL("http://www.evil.com/malware.html"), - &list, &prefixes, &full_hashes, Time::Now()); - EXPECT_EQ(full_hashes.size(), 1U); - base::SHA256HashString("www.evil.com/malware.html", - &full_hash.hash, sizeof(SBFullHash)); - EXPECT_EQ(memcmp(&full_hashes[0].hash, - &full_hash.hash, sizeof(SBFullHash)), 0); - - prefixes.clear(); - full_hashes.clear(); - - // Test removing a prefix via a sub chunk. - SBChunkHost host; - host.host = Sha256Prefix("www.evil.com/"); - host.entry = SBEntry::Create(SBEntry::SUB_PREFIX, 2); - host.entry->set_chunk_id(1); - host.entry->SetChunkIdAtPrefix(0, 1); - host.entry->SetPrefixAt(0, Sha256Prefix("www.evil.com/phishing.html")); - - SBChunk chunk; - chunk.chunk_number = 2; - chunk.is_add = false; - chunk.hosts.clear(); - chunk.hosts.push_back(host); - std::deque<SBChunk>* chunks = new std::deque<SBChunk>; - chunks->push_back(chunk); - database.InsertChunks("goog-malware-shavar", chunks); - - // This prefix should still be there. - database.ContainsUrl(GURL("http://www.evil.com/malware.html"), - &list, &prefixes, &full_hashes, Time::Now()); - EXPECT_EQ(full_hashes.size(), 1U); - base::SHA256HashString("www.evil.com/malware.html", - &full_hash.hash, sizeof(SBFullHash)); - EXPECT_EQ(memcmp(&full_hashes[0].hash, - &full_hash.hash, sizeof(SBFullHash)), 0); - - prefixes.clear(); - full_hashes.clear(); - - // This prefix should be gone. - database.ContainsUrl(GURL("http://www.evil.com/phishing.html"), - &list, &prefixes, &full_hashes, Time::Now()); - EXPECT_EQ(full_hashes.size(), 0U); - - prefixes.clear(); - full_hashes.clear(); - - // Test that an AddDel for the original chunk removes the last cached entry. - AddDelChunk(&database, "goog-malware-shavar", 1); - database.ContainsUrl(GURL("http://www.evil.com/malware.html"), - &list, &prefixes, &full_hashes, Time::Now()); - EXPECT_EQ(full_hashes.size(), 0U); - EXPECT_EQ(database.hash_cache_.size(), 0U); - - prefixes.clear(); - full_hashes.clear(); - - // Test that the cache won't return expired values. First we have to adjust - // the cached entries' received time to make them older, since the database - // cache insert uses Time::Now(). First, store some entries. - PopulateDatabaseForCacheTest(&database); - EXPECT_EQ(database.hash_cache_.size(), 2U); - - // Now adjust one of the entries times to be in the past. - Time expired = Time::Now() - TimeDelta::FromMinutes(60); - SBPrefix key; - memcpy(&key, &full_hash.hash, sizeof(SBPrefix)); - SafeBrowsingDatabaseImpl::HashList& entries = database.hash_cache_[key]; - SafeBrowsingDatabaseImpl::HashCacheEntry entry = entries.front(); - entries.pop_front(); - entry.received = expired; - entries.push_back(entry); - - database.ContainsUrl(GURL("http://www.evil.com/malware.html"), - &list, &prefixes, &full_hashes, expired); - EXPECT_EQ(full_hashes.size(), 0U); - - // Expired entry was dumped. - EXPECT_EQ(database.hash_cache_.size(), 1U); - - // This entry should still exist. - database.ContainsUrl(GURL("http://www.evil.com/phishing.html"), - &list, &prefixes, &full_hashes, expired); - EXPECT_EQ(full_hashes.size(), 1U); - - - // Testing prefix miss caching. First, we clear out the existing database, - // Since PopulateDatabaseForCacheTest() doesn't handle adding duplicate - // chunks. - AddDelChunk(&database, "goog-malware-shavar", 1); - - std::vector<SBPrefix> prefix_misses; - std::vector<SBFullHashResult> empty_full_hash; - prefix_misses.push_back(Sha256Prefix("http://www.bad.com/malware.html")); - prefix_misses.push_back(Sha256Prefix("http://www.bad.com/phishing.html")); - database.CacheHashResults(prefix_misses, empty_full_hash); - - // Prefixes with no full results are misses. - EXPECT_EQ(database.prefix_miss_cache_.size(), 2U); - - // Update the database. - PopulateDatabaseForCacheTest(&database); - - // Prefix miss cache should be cleared. - EXPECT_EQ(database.prefix_miss_cache_.size(), 0U); -} diff --git a/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc b/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc index 43249a5..cf187a2 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc +++ b/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc @@ -4,6 +4,7 @@ // // Unit tests for the SafeBrowsing storage system. +#include "base/command_line.h" #include "base/file_util.h" #include "base/logging.h" #include "base/path_service.h" @@ -14,12 +15,14 @@ #include "base/time.h" #include "chrome/browser/safe_browsing/protocol_parser.h" #include "chrome/browser/safe_browsing/safe_browsing_database.h" +#include "chrome/common/chrome_switches.h" #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" using base::Time; static const wchar_t kSafeBrowsingTestDatabase[] = L"SafeBrowsingTestDatabase"; +static const wchar_t kBloomSuffix[] = L" Bloom"; namespace { SBPrefix Sha256Prefix(const std::string& str) { @@ -65,7 +68,9 @@ namespace { SafeBrowsingDatabase* SetupTestDatabase() { std::wstring filename = GetTestDatabaseName(); + // In case it existed from a previous run. + file_util::Delete(filename + kBloomSuffix, false); file_util::Delete(filename, false); SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create(); @@ -76,12 +81,150 @@ namespace { } void TearDownTestDatabase(SafeBrowsingDatabase* database) { - file_util::Delete(GetTestDatabaseName(), false); + std::wstring filename = database->filename(); delete database; + file_util::Delete(filename, false); + file_util::Delete(filename + L" Filter", false); } } // namespace +// Tests retrieving list name information. +TEST(SafeBrowsingDatabase, ListName) { + SafeBrowsingDatabase* database = SetupTestDatabase(); + + // Insert some malware add chunks. + SBChunkHost host; + host.host = Sha256Prefix("www.evil.com/"); + host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 1); + host.entry->set_chunk_id(1); + host.entry->SetPrefixAt(0, Sha256Prefix("www.evil.com/malware.html")); + SBChunk chunk; + chunk.chunk_number = 1; + chunk.is_add = true; + chunk.hosts.push_back(host); + std::deque<SBChunk>* chunks = new std::deque<SBChunk>; + chunks->push_back(chunk); + database->UpdateStarted(); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); + + host.host = Sha256Prefix("www.foo.com/"); + host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 1); + host.entry->set_chunk_id(2); + host.entry->SetPrefixAt(0, Sha256Prefix("www.foo.com/malware.html")); + chunk.chunk_number = 2; + chunk.is_add = true; + chunk.hosts.clear(); + chunk.hosts.push_back(host); + chunks = new std::deque<SBChunk>; + chunks->push_back(chunk); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); + + host.host = Sha256Prefix("www.whatever.com/"); + host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 1); + host.entry->set_chunk_id(3); + host.entry->SetPrefixAt(0, Sha256Prefix("www.whatever.com/malware.html")); + chunk.chunk_number = 3; + chunk.is_add = true; + chunk.hosts.clear(); + chunk.hosts.push_back(host); + chunks = new std::deque<SBChunk>; + chunks->push_back(chunk); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); + database->UpdateFinished(true); + + std::vector<SBListChunkRanges> lists; + database->GetListsInfo(&lists); + + EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList); + EXPECT_EQ(lists[0].adds, "1-3"); + EXPECT_TRUE(lists[0].subs.empty()); + lists.clear(); + + // Insert a malware sub chunk. + host.host = Sha256Prefix("www.subbed.com/"); + host.entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1); + host.entry->set_chunk_id(7); + host.entry->SetChunkIdAtPrefix(0, 19); + host.entry->SetPrefixAt(0, Sha256Prefix("www.subbed.com/notevil1.html")); + chunk.chunk_number = 7; + chunk.is_add = false; + chunk.hosts.clear(); + chunk.hosts.push_back(host); + chunks = new std::deque<SBChunk>; + chunks->push_back(chunk); + + database->UpdateStarted(); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); + database->UpdateFinished(true); + + database->GetListsInfo(&lists); + EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList); + EXPECT_EQ(lists[0].adds, "1-3"); + EXPECT_EQ(lists[0].subs, "7"); + if (lists.size() == 2) { + // Old style database won't have the second entry since it creates the lists + // when it receives an update containing that list. The new bloom filter + // based database has these values hard coded. + EXPECT_TRUE(lists[1].name == safe_browsing_util::kPhishingList); + EXPECT_TRUE(lists[1].adds.empty()); + EXPECT_TRUE(lists[1].subs.empty()); + } + lists.clear(); + + // Add a phishing add chunk. + host.host = Sha256Prefix("www.evil.com/"); + host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 1); + host.entry->set_chunk_id(47); + host.entry->SetPrefixAt(0, Sha256Prefix("www.evil.com/phishing.html")); + chunk.chunk_number = 47; + chunk.is_add = true; + chunk.hosts.clear(); + chunk.hosts.push_back(host); + chunks = new std::deque<SBChunk>; + chunks->push_back(chunk); + database->UpdateStarted(); + database->InsertChunks(safe_browsing_util::kPhishingList, chunks); + + // Insert some phishing sub chunks. + host.host = Sha256Prefix("www.phishy.com/"); + host.entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1); + host.entry->set_chunk_id(200); + host.entry->SetChunkIdAtPrefix(0, 1999); + host.entry->SetPrefixAt(0, Sha256Prefix("www.phishy.com/notevil1.html")); + chunk.chunk_number = 200; + chunk.is_add = false; + chunk.hosts.clear(); + chunk.hosts.push_back(host); + chunks = new std::deque<SBChunk>; + chunks->push_back(chunk); + database->InsertChunks(safe_browsing_util::kPhishingList, chunks); + + host.host = Sha256Prefix("www.phishy2.com/"); + host.entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1); + host.entry->set_chunk_id(201); + host.entry->SetChunkIdAtPrefix(0, 1999); + host.entry->SetPrefixAt(0, Sha256Prefix("www.phishy2.com/notevil1.html")); + chunk.chunk_number = 201; + chunk.is_add = false; + chunk.hosts.clear(); + chunk.hosts.push_back(host); + chunks = new std::deque<SBChunk>; + chunks->push_back(chunk); + database->InsertChunks(safe_browsing_util::kPhishingList, chunks); + database->UpdateFinished(true); + + database->GetListsInfo(&lists); + EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList); + EXPECT_EQ(lists[0].adds, "1-3"); + EXPECT_EQ(lists[0].subs, "7"); + EXPECT_TRUE(lists[1].name == safe_browsing_util::kPhishingList); + EXPECT_EQ(lists[1].adds, "47"); + EXPECT_EQ(lists[1].subs, "200-201"); + + TearDownTestDatabase(database); +} + // Checks database reading and writing. TEST(SafeBrowsingDatabase, Database) { SafeBrowsingDatabase* database = SetupTestDatabase(); @@ -101,7 +244,8 @@ TEST(SafeBrowsingDatabase, Database) { std::deque<SBChunk>* chunks = new std::deque<SBChunk>; chunks->push_back(chunk); - database->InsertChunks("goog-malware", chunks); + database->UpdateStarted(); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); // Add another chunk with two different hostkeys. host.host = Sha256Prefix("www.evil.com/"); @@ -125,7 +269,7 @@ TEST(SafeBrowsingDatabase, Database) { chunks = new std::deque<SBChunk>; chunks->push_back(chunk); - database->InsertChunks("goog-malware", chunks); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); // and a chunk with an IP-based host host.host = Sha256Prefix("192.168.0.1/"); @@ -139,14 +283,13 @@ TEST(SafeBrowsingDatabase, Database) { chunks = new std::deque<SBChunk>; chunks->push_back(chunk); - database->InsertChunks("goog-malware", chunks); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); database->UpdateFinished(true); // Make sure they were added correctly. std::vector<SBListChunkRanges> lists; database->GetListsInfo(&lists); - EXPECT_EQ(lists.size(), 1U); - EXPECT_EQ(lists[0].name, "goog-malware"); + EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList); EXPECT_EQ(lists[0].adds, "1-3"); EXPECT_TRUE(lists[0].subs.empty()); @@ -208,7 +351,8 @@ TEST(SafeBrowsingDatabase, Database) { chunks = new std::deque<SBChunk>; chunks->push_back(chunk); - database->InsertChunks("goog-malware", chunks); + database->UpdateStarted(); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); database->UpdateFinished(true); EXPECT_TRUE(database->ContainsUrl(GURL("http://www.evil.com/phishing.html"), @@ -235,12 +379,12 @@ TEST(SafeBrowsingDatabase, Database) { &full_hashes, now)); database->GetListsInfo(&lists); - EXPECT_EQ(lists.size(), 1U); - EXPECT_EQ(lists[0].name, "goog-malware"); + EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList); EXPECT_EQ(lists[0].subs, "4"); // Test removing all the prefixes from an add chunk. - AddDelChunk(database, "goog-malware", 2); + database->UpdateStarted(); + AddDelChunk(database, safe_browsing_util::kMalwareList, 2); database->UpdateFinished(true); EXPECT_FALSE(database->ContainsUrl(GURL("http://www.evil.com/notevil2.html"), @@ -256,8 +400,7 @@ TEST(SafeBrowsingDatabase, Database) { &full_hashes, now)); database->GetListsInfo(&lists); - EXPECT_EQ(lists.size(), 1U); - EXPECT_EQ(lists[0].name, "goog-malware"); + EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList); EXPECT_EQ(lists[0].adds, "1,3"); EXPECT_EQ(lists[0].subs, "4"); @@ -276,19 +419,19 @@ TEST(SafeBrowsingDatabase, Database) { chunks = new std::deque<SBChunk>; chunks->push_back(chunk); - database->InsertChunks("goog-malware", chunks); + database->UpdateStarted(); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); // Now remove the dummy entry. If there are any problems with the // transactions, asserts will fire. - AddDelChunk(database, "goog-malware", 44); + AddDelChunk(database, safe_browsing_util::kMalwareList, 44); // Test the subdel command. - SubDelChunk(database, "goog-malware", 4); + SubDelChunk(database, safe_browsing_util::kMalwareList, 4); database->UpdateFinished(true); database->GetListsInfo(&lists); - EXPECT_EQ(lists.size(), 1U); - EXPECT_EQ(lists[0].name, "goog-malware"); + EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList); EXPECT_EQ(lists[0].adds, "1,3"); EXPECT_EQ(lists[0].subs, ""); @@ -308,7 +451,8 @@ TEST(SafeBrowsingDatabase, Database) { chunks = new std::deque<SBChunk>; chunks->push_back(chunk); - database->InsertChunks("goog-malware", chunks); + database->UpdateStarted(); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); database->UpdateFinished(true); EXPECT_FALSE(database->ContainsUrl( @@ -328,7 +472,8 @@ TEST(SafeBrowsingDatabase, Database) { chunks = new std::deque<SBChunk>; chunks->push_back(chunk); - database->InsertChunks("goog-malware", chunks); + database->UpdateStarted(); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); database->UpdateFinished(true); EXPECT_FALSE(database->ContainsUrl( @@ -373,7 +518,8 @@ TEST(SafeBrowsingDatabase, ZeroSizeChunk) { chunk.hosts.push_back(host); chunks->push_back(chunk); - database->InsertChunks("goog-malware", chunks); + database->UpdateStarted(); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); database->UpdateFinished(true); // Add an empty ADD and SUB chunk. @@ -386,12 +532,13 @@ TEST(SafeBrowsingDatabase, ZeroSizeChunk) { empty_chunk.is_add = true; chunks = new std::deque<SBChunk>; chunks->push_back(empty_chunk); - database->InsertChunks("goog-malware", chunks); + database->UpdateStarted(); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); chunks = new std::deque<SBChunk>; empty_chunk.chunk_number = 7; empty_chunk.is_add = false; chunks->push_back(empty_chunk); - database->InsertChunks("goog-malware", chunks); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); database->UpdateFinished(true); list_chunks_empty.clear(); @@ -427,7 +574,8 @@ TEST(SafeBrowsingDatabase, ZeroSizeChunk) { empty_chunk.is_add = true; chunks->push_back(empty_chunk); - database->InsertChunks("goog-malware", chunks); + database->UpdateStarted(); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); database->UpdateFinished(true); const Time now = Time::Now(); @@ -447,14 +595,16 @@ TEST(SafeBrowsingDatabase, ZeroSizeChunk) { EXPECT_EQ(list_chunks_empty[0].subs, "7"); // Handle AddDel and SubDel commands for empty chunks. - AddDelChunk(database, "goog-malware", 21); + database->UpdateStarted(); + AddDelChunk(database, safe_browsing_util::kMalwareList, 21); database->UpdateFinished(true); list_chunks_empty.clear(); database->GetListsInfo(&list_chunks_empty); EXPECT_EQ(list_chunks_empty[0].adds, "1,10,19-20,22"); EXPECT_EQ(list_chunks_empty[0].subs, "7"); - SubDelChunk(database, "goog-malware", 7); + database->UpdateStarted(); + SubDelChunk(database, safe_browsing_util::kMalwareList, 7); database->UpdateFinished(true); list_chunks_empty.clear(); database->GetListsInfo(&list_chunks_empty); @@ -464,6 +614,290 @@ TEST(SafeBrowsingDatabase, ZeroSizeChunk) { TearDownTestDatabase(database); } +// Utility function for setting up the database for the caching test. +void PopulateDatabaseForCacheTest(SafeBrowsingDatabase* database) { + // Add a simple chunk with one hostkey and cache it. + SBChunkHost host; + host.host = Sha256Prefix("www.evil.com/"); + host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 2); + host.entry->set_chunk_id(1); + host.entry->SetPrefixAt(0, Sha256Prefix("www.evil.com/phishing.html")); + host.entry->SetPrefixAt(1, Sha256Prefix("www.evil.com/malware.html")); + + SBChunk chunk; + chunk.chunk_number = 1; + chunk.is_add = true; + chunk.hosts.push_back(host); + + std::deque<SBChunk>* chunks = new std::deque<SBChunk>; + chunks->push_back(chunk); + database->UpdateStarted(); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); + database->UpdateFinished(true); + + // Add the GetHash results to the cache. + SBFullHashResult full_hash; + base::SHA256HashString("www.evil.com/phishing.html", + &full_hash.hash, sizeof(SBFullHash)); + full_hash.list_name = safe_browsing_util::kMalwareList; + full_hash.add_chunk_id = 1; + + std::vector<SBFullHashResult> results; + results.push_back(full_hash); + + base::SHA256HashString("www.evil.com/malware.html", + &full_hash.hash, sizeof(SBFullHash)); + results.push_back(full_hash); + + std::vector<SBPrefix> prefixes; + database->CacheHashResults(prefixes, results); +} + +TEST(SafeBrowsingDatabase, HashCaching) { + SafeBrowsingDatabase* database = SetupTestDatabase(); + + PopulateDatabaseForCacheTest(database); + + // We should have both full hashes in the cache. + SafeBrowsingDatabase::HashCache* hash_cache = database->hash_cache(); + EXPECT_EQ(hash_cache->size(), 2U); + + // Test the cache lookup for the first prefix. + std::string list; + std::vector<SBPrefix> prefixes; + std::vector<SBFullHashResult> full_hashes; + database->ContainsUrl(GURL("http://www.evil.com/phishing.html"), + &list, &prefixes, &full_hashes, Time::Now()); + EXPECT_EQ(full_hashes.size(), 1U); + + SBFullHashResult full_hash; + base::SHA256HashString("www.evil.com/phishing.html", + &full_hash.hash, sizeof(SBFullHash)); + EXPECT_EQ(memcmp(&full_hashes[0].hash, + &full_hash.hash, sizeof(SBFullHash)), 0); + + prefixes.clear(); + full_hashes.clear(); + + // Test the cache lookup for the second prefix. + database->ContainsUrl(GURL("http://www.evil.com/malware.html"), + &list, &prefixes, &full_hashes, Time::Now()); + EXPECT_EQ(full_hashes.size(), 1U); + base::SHA256HashString("www.evil.com/malware.html", + &full_hash.hash, sizeof(SBFullHash)); + EXPECT_EQ(memcmp(&full_hashes[0].hash, + &full_hash.hash, sizeof(SBFullHash)), 0); + + prefixes.clear(); + full_hashes.clear(); + + // Test removing a prefix via a sub chunk. + SBChunkHost host; + host.host = Sha256Prefix("www.evil.com/"); + host.entry = SBEntry::Create(SBEntry::SUB_PREFIX, 1); + host.entry->set_chunk_id(1); + host.entry->SetChunkIdAtPrefix(0, 1); + host.entry->SetPrefixAt(0, Sha256Prefix("www.evil.com/phishing.html")); + + SBChunk chunk; + chunk.chunk_number = 2; + chunk.is_add = false; + chunk.hosts.clear(); + chunk.hosts.push_back(host); + std::deque<SBChunk>* chunks = new std::deque<SBChunk>; + chunks->push_back(chunk); + database->UpdateStarted(); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); + database->UpdateFinished(true); + + // This prefix should still be there. + database->ContainsUrl(GURL("http://www.evil.com/malware.html"), + &list, &prefixes, &full_hashes, Time::Now()); + EXPECT_EQ(full_hashes.size(), 1U); + base::SHA256HashString("www.evil.com/malware.html", + &full_hash.hash, sizeof(SBFullHash)); + EXPECT_EQ(memcmp(&full_hashes[0].hash, + &full_hash.hash, sizeof(SBFullHash)), 0); + + prefixes.clear(); + full_hashes.clear(); + + // This prefix should be gone. + database->ContainsUrl(GURL("http://www.evil.com/phishing.html"), + &list, &prefixes, &full_hashes, Time::Now()); + EXPECT_EQ(full_hashes.size(), 0U); + + prefixes.clear(); + full_hashes.clear(); + + // Test that an AddDel for the original chunk removes the last cached entry. + database->UpdateStarted(); + AddDelChunk(database, safe_browsing_util::kMalwareList, 1); + database->UpdateFinished(true); + database->ContainsUrl(GURL("http://www.evil.com/malware.html"), + &list, &prefixes, &full_hashes, Time::Now()); + EXPECT_EQ(full_hashes.size(), 0U); + EXPECT_EQ(database->hash_cache()->size(), 0U); + + prefixes.clear(); + full_hashes.clear(); + + // Test that the cache won't return expired values. First we have to adjust + // the cached entries' received time to make them older, since the database + // cache insert uses Time::Now(). First, store some entries. + PopulateDatabaseForCacheTest(database); + hash_cache = database->hash_cache(); + EXPECT_EQ(hash_cache->size(), 2U); + + // Now adjust one of the entries times to be in the past. + base::Time expired = base::Time::Now() - base::TimeDelta::FromMinutes(60); + SBPrefix key; + memcpy(&key, &full_hash.hash, sizeof(SBPrefix)); + SafeBrowsingDatabase::HashList& entries = (*hash_cache)[key]; + SafeBrowsingDatabase::HashCacheEntry entry = entries.front(); + entries.pop_front(); + entry.received = expired; + entries.push_back(entry); + + database->ContainsUrl(GURL("http://www.evil.com/malware.html"), + &list, &prefixes, &full_hashes, expired); + EXPECT_EQ(full_hashes.size(), 0U); + + // This entry should still exist. + database->ContainsUrl(GURL("http://www.evil.com/phishing.html"), + &list, &prefixes, &full_hashes, expired); + EXPECT_EQ(full_hashes.size(), 1U); + + + // Testing prefix miss caching. First, we clear out the existing database, + // Since PopulateDatabaseForCacheTest() doesn't handle adding duplicate + // chunks. + database->UpdateStarted(); + AddDelChunk(database, safe_browsing_util::kMalwareList, 1); + database->UpdateFinished(true); + + std::vector<SBPrefix> prefix_misses; + std::vector<SBFullHashResult> empty_full_hash; + prefix_misses.push_back(Sha256Prefix("http://www.bad.com/malware.html")); + prefix_misses.push_back(Sha256Prefix("http://www.bad.com/phishing.html")); + database->CacheHashResults(prefix_misses, empty_full_hash); + + // Prefixes with no full results are misses. + EXPECT_EQ(database->prefix_miss_cache()->size(), 2U); + + // Update the database. + PopulateDatabaseForCacheTest(database); + + // Prefix miss cache should be cleared. + EXPECT_EQ(database->prefix_miss_cache()->size(), 0U); + + list.clear(); + prefixes.clear(); + full_hashes.clear(); + + // Test receiving a full add chunk. The old implementation doesn't support + // this test, so we bail here. + if (!CommandLine().HasSwitch(switches::kUseNewSafeBrowsing)) { + TearDownTestDatabase(database); + return; + } + + host.host = Sha256Prefix("www.fullevil.com/"); + host.entry = SBEntry::Create(SBEntry::ADD_FULL_HASH, 2); + host.entry->set_chunk_id(20); + SBFullHash full_add1; + base::SHA256HashString("www.fullevil.com/bad1.html", + &full_add1.full_hash, sizeof(SBFullHash)); + host.entry->SetFullHashAt(0, full_add1); + SBFullHash full_add2; + base::SHA256HashString("www.fullevil.com/bad2.html", + &full_add2.full_hash, sizeof(SBFullHash)); + host.entry->SetFullHashAt(1, full_add2); + + chunk.chunk_number = 20; + chunk.is_add = true; + chunk.hosts.clear(); + chunk.hosts.push_back(host); + chunks = new std::deque<SBChunk>; + chunks->push_back(chunk); + database->UpdateStarted(); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); + database->UpdateFinished(true); + + EXPECT_TRUE(database->ContainsUrl(GURL("http://www.fullevil.com/bad1.html"), + &list, &prefixes, &full_hashes, + Time::Now())); + EXPECT_EQ(full_hashes.size(), 1U); + EXPECT_EQ(0, memcmp(full_hashes[0].hash.full_hash, + full_add1.full_hash, + sizeof(SBFullHash))); + list.clear(); + prefixes.clear(); + full_hashes.clear(); + + EXPECT_TRUE(database->ContainsUrl(GURL("http://www.fullevil.com/bad2.html"), + &list, &prefixes, &full_hashes, + Time::Now())); + EXPECT_EQ(full_hashes.size(), 1U); + EXPECT_EQ(0, memcmp(full_hashes[0].hash.full_hash, + full_add2.full_hash, + sizeof(SBFullHash))); + list.clear(); + prefixes.clear(); + full_hashes.clear(); + + // Test receiving a full sub chunk, which will remove one of the full adds. + host.host = Sha256Prefix("www.fullevil.com/"); + host.entry = SBEntry::Create(SBEntry::SUB_FULL_HASH, 1); + host.entry->set_chunk_id(200); + host.entry->SetChunkIdAtPrefix(0, 20); + SBFullHash full_sub; + base::SHA256HashString("www.fullevil.com/bad1.html", + &full_sub.full_hash, sizeof(SBFullHash)); + host.entry->SetFullHashAt(0, full_sub); + + chunk.chunk_number = 200; + chunk.is_add = false; + chunk.hosts.clear(); + chunk.hosts.push_back(host); + chunks = new std::deque<SBChunk>; + chunks->push_back(chunk); + database->UpdateStarted(); + database->InsertChunks(safe_browsing_util::kMalwareList, chunks); + database->UpdateFinished(true); + + EXPECT_FALSE(database->ContainsUrl(GURL("http://www.fullevil.com/bad1.html"), + &list, &prefixes, &full_hashes, + Time::Now())); + EXPECT_EQ(full_hashes.size(), 0U); + + // There should be one remaining full add. + EXPECT_TRUE(database->ContainsUrl(GURL("http://www.fullevil.com/bad2.html"), + &list, &prefixes, &full_hashes, + Time::Now())); + EXPECT_EQ(full_hashes.size(), 1U); + EXPECT_EQ(0, memcmp(full_hashes[0].hash.full_hash, + full_add2.full_hash, + sizeof(SBFullHash))); + list.clear(); + prefixes.clear(); + full_hashes.clear(); + + // Now test an AddDel for the remaining full add. + database->UpdateStarted(); + AddDelChunk(database, safe_browsing_util::kMalwareList, 20); + database->UpdateFinished(true); + + EXPECT_FALSE(database->ContainsUrl(GURL("http://www.fullevil.com/bad1.html"), + &list, &prefixes, &full_hashes, + Time::Now())); + EXPECT_FALSE(database->ContainsUrl(GURL("http://www.fullevil.com/bad2.html"), + &list, &prefixes, &full_hashes, + Time::Now())); + + TearDownTestDatabase(database); +} + void PrintStat(const wchar_t* name) { #if defined(OS_WIN) int value = StatsTable::current()->GetCounterValue(name); @@ -518,8 +952,9 @@ void PeformUpdate(const std::wstring& initial_db, process_util::ProcessMetrics::CreateProcessMetrics(handle)); CHECK(metric->GetIOCounters(&before)); - database->DeleteChunks(deletes); + database->UpdateStarted(); + database->DeleteChunks(deletes); for (size_t i = 0; i < chunks.size(); ++i) database->InsertChunks(chunks[i].listname, chunks[i].chunks); @@ -682,7 +1117,7 @@ TEST(SafeBrowsingDatabase, DISABLED_DatabaseOldLotsofDeletesIO) { std::vector<SBChunkDelete>* deletes = new std::vector<SBChunkDelete>; SBChunkDelete del; del.is_sub_del = false; - del.list_name = "goog-malware-shavar"; + del.list_name = safe_browsing_util::kMalwareList; del.chunk_del.push_back(ChunkRange(3539, 3579)); deletes->push_back(del); PeformUpdate(GetOldSafeBrowsingPath(), chunks, deletes); diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc index 1382a5f..5a2f257 100644 --- a/chrome/browser/safe_browsing/safe_browsing_service.cc +++ b/chrome/browser/safe_browsing/safe_browsing_service.cc @@ -202,8 +202,8 @@ bool SafeBrowsingService::CheckUrlNew(const GURL& url, Client* client) { std::string list; std::vector<SBPrefix> prefix_hits; std::vector<SBFullHashResult> full_hits; - bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits, - &full_hits, + bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits, + &full_hits, protocol_manager_->last_update()); if (!prefix_match) return true; // URL is okay. @@ -415,6 +415,7 @@ void SafeBrowsingService::HandleGetHashResults( db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( this, &SafeBrowsingService::CacheHashResults, prefixes, full_hashes)); } else if (database_) { + // Cache the GetHash results in memory: database_->CacheHashResults(prefixes, full_hashes); } } @@ -464,6 +465,13 @@ void SafeBrowsingService::GetAllChunks() { this, &SafeBrowsingService::GetAllChunksFromDatabase)); } +void SafeBrowsingService::UpdateStarted() { + DCHECK(MessageLoop::current() == io_loop_); + DCHECK(enabled_); + db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &SafeBrowsingService::DatabaseUpdateStarted)); +} + void SafeBrowsingService::UpdateFinished(bool update_succeeded) { DCHECK(MessageLoop::current() == io_loop_); DCHECK(enabled_); @@ -471,6 +479,12 @@ void SafeBrowsingService::UpdateFinished(bool update_succeeded) { this, &SafeBrowsingService::DatabaseUpdateFinished, update_succeeded)); } +void SafeBrowsingService::DatabaseUpdateStarted() { + DCHECK(MessageLoop::current() == db_thread_->message_loop()); + if (GetDatabase()) + GetDatabase()->UpdateStarted(); +} + void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) { DCHECK(MessageLoop::current() == db_thread_->message_loop()); if (GetDatabase()) diff --git a/chrome/browser/safe_browsing/safe_browsing_service.h b/chrome/browser/safe_browsing/safe_browsing_service.h index 8a87726..d85dd89 100644 --- a/chrome/browser/safe_browsing/safe_browsing_service.h +++ b/chrome/browser/safe_browsing/safe_browsing_service.h @@ -59,9 +59,9 @@ class SafeBrowsingService GURL url; bool proceed; UrlCheckResult result; - Client* client; - int render_process_host_id; - int render_view_id; + Client* client; + int render_process_host_id; + int render_view_id; ResourceType::Type resource_type; }; @@ -135,6 +135,9 @@ class SafeBrowsingService void HandleChunk(const std::string& list, std::deque<SBChunk>* chunks); void HandleChunkDelete(std::vector<SBChunkDelete>* chunk_deletes); void GetAllChunks(); + + // Methods called to indicate the beginning and end of a complete update. + void UpdateStarted(); void UpdateFinished(bool update_succeeded); // The blocking page on the UI thread has completed. @@ -177,6 +180,9 @@ class SafeBrowsingService // threadsafe. SafeBrowsingDatabase* GetDatabase(); + // Release the final reference to the database on the db thread. + void ReleaseDatabase(SafeBrowsingDatabase* database); + // Called on the database thread to check a url. void CheckDatabase(SafeBrowsingCheck* info, base::Time last_update); @@ -203,6 +209,7 @@ class SafeBrowsingService void NotifyClientBlockingComplete(Client* client, bool proceed); + void DatabaseUpdateStarted(); void DatabaseUpdateFinished(bool update_succeeded); void Start(); @@ -215,7 +222,7 @@ class SafeBrowsingService // Runs on the io thread when the reset is complete. void OnResetComplete(); - // Store the results of a GetHash request. Runs on the database thread. + // Store in-memory the GetHash response. Runs on the database thread. void CacheHashResults(const std::vector<SBPrefix>& prefixes, const std::vector<SBFullHashResult>& full_hashes); diff --git a/chrome/browser/safe_browsing/safe_browsing_util.cc b/chrome/browser/safe_browsing/safe_browsing_util.cc index b7cf098..7b529fa 100644 --- a/chrome/browser/safe_browsing/safe_browsing_util.cc +++ b/chrome/browser/safe_browsing/safe_browsing_util.cc @@ -25,6 +25,9 @@ static const char kReportParams[] = "?tpl=generic&continue=%s&url=%s"; namespace safe_browsing_util { +const char kMalwareList[] = "goog-malware-shavar"; +const char kPhishingList[] = "goog-phish-shavar"; + void GenerateHostsToCheck(const GURL& url, std::vector<std::string>* hosts) { // Per Safe Browsing Protocol 2 spec, first we try the host. Then we try up // to 4 hostnames starting with the last 5 components and successively diff --git a/chrome/browser/safe_browsing/safe_browsing_util.h b/chrome/browser/safe_browsing/safe_browsing_util.h index 3d8cfb7..a034b73 100644 --- a/chrome/browser/safe_browsing/safe_browsing_util.h +++ b/chrome/browser/safe_browsing/safe_browsing_util.h @@ -273,6 +273,10 @@ class SBHostInfo { namespace safe_browsing_util { +// SafeBrowsing list names. +extern const char kMalwareList[]; +extern const char kPhishingList[]; + void FreeChunks(std::deque<SBChunk>* chunks); // Given a URL, returns all the hosts we need to check. They are returned diff --git a/chrome/chrome.xcodeproj/project.pbxproj b/chrome/chrome.xcodeproj/project.pbxproj index cf149ce..53bf20f 100644 --- a/chrome/chrome.xcodeproj/project.pbxproj +++ b/chrome/chrome.xcodeproj/project.pbxproj @@ -1031,7 +1031,6 @@ 4D7BFAE00E9D49DE009A6919 /* safe_browsing_database_bloom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = safe_browsing_database_bloom.h; sourceTree = "<group>"; }; 4D7BFAE10E9D49DE009A6919 /* safe_browsing_database_impl.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = safe_browsing_database_impl.cc; sourceTree = "<group>"; }; 4D7BFAE20E9D49DE009A6919 /* safe_browsing_database_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = safe_browsing_database_impl.h; sourceTree = "<group>"; }; - 4D7BFAE30E9D49DE009A6919 /* safe_browsing_database_impl_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = safe_browsing_database_impl_unittest.cc; sourceTree = "<group>"; }; 4D7BFAE40E9D49DE009A6919 /* safe_browsing_database_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = safe_browsing_database_unittest.cc; sourceTree = "<group>"; }; 4D7BFAE50E9D49DE009A6919 /* safe_browsing_service.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = safe_browsing_service.cc; sourceTree = "<group>"; }; 4D7BFAE60E9D49DE009A6919 /* safe_browsing_service.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = safe_browsing_service.h; sourceTree = "<group>"; }; @@ -1945,7 +1944,6 @@ 4D7BFAE00E9D49DE009A6919 /* safe_browsing_database_bloom.h */, 4D7BFAE10E9D49DE009A6919 /* safe_browsing_database_impl.cc */, 4D7BFAE20E9D49DE009A6919 /* safe_browsing_database_impl.h */, - 4D7BFAE30E9D49DE009A6919 /* safe_browsing_database_impl_unittest.cc */, 4D7BFAE40E9D49DE009A6919 /* safe_browsing_database_unittest.cc */, 4D7BFAE50E9D49DE009A6919 /* safe_browsing_service.cc */, 4D7BFAE60E9D49DE009A6919 /* safe_browsing_service.h */, diff --git a/chrome/test/unit/unit_tests.scons b/chrome/test/unit/unit_tests.scons index 3129c16..407fdaa 100644 --- a/chrome/test/unit/unit_tests.scons +++ b/chrome/test/unit/unit_tests.scons @@ -126,7 +126,6 @@ if env['PLATFORM'] in ('posix', 'win32'): '$CHROME_DIR/browser/safe_browsing/bloom_filter_unittest.cc', '$CHROME_DIR/browser/safe_browsing/chunk_range_unittest.cc', '$CHROME_DIR/browser/safe_browsing/safe_browsing_database_unittest.cc', - '$CHROME_DIR/browser/safe_browsing/safe_browsing_database_impl_unittest.cc', '$CHROME_DIR/browser/safe_browsing/protocol_parser_unittest.cc', '$CHROME_DIR/browser/safe_browsing/safe_browsing_util_unittest.cc', '$CHROME_DIR/common/animation_unittest.cc', diff --git a/chrome/test/unit/unittests.vcproj b/chrome/test/unit/unittests.vcproj index d39295b..0b131c0 100644 --- a/chrome/test/unit/unittests.vcproj +++ b/chrome/test/unit/unittests.vcproj @@ -814,10 +814,6 @@ > </File> <File - RelativePath="..\..\browser\safe_browsing\safe_browsing_database_impl_unittest.cc" - > - </File> - <File RelativePath="..\..\browser\safe_browsing\safe_browsing_database_unittest.cc" > </File> |