summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/safe_browsing/safe_browsing_service.cc775
-rw-r--r--chrome/browser/safe_browsing/safe_browsing_service.h155
2 files changed, 459 insertions, 471 deletions
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index 164547e..3f2d7d8 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -5,31 +5,25 @@
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
-#include "base/command_line.h"
-#include "base/histogram.h"
-#include "base/logging.h"
-#include "base/message_loop.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/net/url_request_context_getter.h"
#include "chrome/browser/profile_manager.h"
#include "chrome/browser/safe_browsing/protocol_manager.h"
#include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
#include "chrome/browser/safe_browsing/safe_browsing_database.h"
-#include "chrome/browser/tab_contents/navigation_entry.h"
#include "chrome/browser/tab_contents/tab_util.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
-#include "chrome/common/pref_service.h"
#include "chrome/common/url_constants.h"
+#include "net/base/registry_controlled_domain.h"
+
#if defined(OS_WIN)
#include "chrome/installer/util/browser_distribution.h"
#endif
-#include "net/base/registry_controlled_domain.h"
using base::Time;
using base::TimeDelta;
@@ -50,10 +44,6 @@ SafeBrowsingService::SafeBrowsingService()
update_in_progress_(false) {
}
-SafeBrowsingService::~SafeBrowsingService() {
-}
-
-// Only called on the UI thread.
void SafeBrowsingService::Initialize() {
// Get the profile's preference for SafeBrowsing.
PrefService* pref_service = GetDefaultProfile()->GetPrefs();
@@ -61,138 +51,12 @@ void SafeBrowsingService::Initialize() {
Start();
}
-// Start up SafeBrowsing objects. This can be called at browser start, or when
-// the user checks the "Enable SafeBrowsing" option in the Advanced options UI.
-void SafeBrowsingService::Start() {
- DCHECK(!safe_browsing_thread_.get());
- safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
- if (!safe_browsing_thread_->Start())
- return;
-
- // Retrieve client MAC keys.
- PrefService* local_state = g_browser_process->local_state();
- std::string client_key, wrapped_key;
- if (local_state) {
- client_key =
- WideToASCII(local_state->GetString(prefs::kSafeBrowsingClientKey));
- wrapped_key =
- WideToASCII(local_state->GetString(prefs::kSafeBrowsingWrappedKey));
- }
-
- // We will issue network fetches using the default profile's request context.
- URLRequestContextGetter* request_context_getter =
- GetDefaultProfile()->GetRequestContext();
- request_context_getter->AddRef(); // Balanced in OnIOInitialize.
-
- ChromeThread::PostTask(
- ChromeThread::IO, FROM_HERE,
- NewRunnableMethod(
- this, &SafeBrowsingService::OnIOInitialize, client_key, wrapped_key,
- request_context_getter));
-
- safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
- this, &SafeBrowsingService::OnDBInitialize));
-}
-
void SafeBrowsingService::ShutDown() {
ChromeThread::PostTask(
ChromeThread::IO, FROM_HERE,
NewRunnableMethod(this, &SafeBrowsingService::OnIOShutdown));
}
-void SafeBrowsingService::OnIOInitialize(
- const std::string& client_key,
- const std::string& wrapped_key,
- URLRequestContextGetter* request_context_getter) {
- DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- enabled_ = true;
-
- // On Windows, get the safe browsing client name from the browser
- // distribution classes in installer util. These classes don't yet have
- // an analog on non-Windows builds so just keep the name specified here.
-#if defined(OS_WIN)
- BrowserDistribution* dist = BrowserDistribution::GetDistribution();
- std::string client_name(dist->GetSafeBrowsingName());
-#else
-#if defined(GOOGLE_CHROME_BUILD)
- std::string client_name("googlechrome");
-#else
- std::string client_name("chromium");
-#endif
-#endif
-
- protocol_manager_ = new SafeBrowsingProtocolManager(this,
- client_name,
- client_key,
- wrapped_key,
- request_context_getter);
-
- // Balance the reference added by Start().
- request_context_getter->Release();
-
- // We want to initialize the protocol manager only after the database has
- // loaded, which we'll receive asynchronously (DatabaseLoadComplete). If
- // database_loaded_ isn't true, we'll wait for that notification to do the
- // init.
- if (database_loaded_)
- protocol_manager_->Initialize();
-}
-
-void SafeBrowsingService::OnDBInitialize() {
- DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
- GetDatabase();
-}
-
-void SafeBrowsingService::OnIOShutdown() {
- DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- if (!enabled_)
- return;
-
- enabled_ = false;
- resetting_ = false;
-
- // This cancels all in-flight GetHash requests.
- delete protocol_manager_;
- protocol_manager_ = NULL;
-
- if (safe_browsing_thread_.get())
- safe_browsing_thread_->message_loop()->DeleteSoon(FROM_HERE, database_);
-
- // Flush the database thread. Any in-progress database check results will be
- // ignored and cleaned up below.
- safe_browsing_thread_.reset(NULL);
-
- database_ = NULL;
- database_loaded_ = false;
-
- // Delete queued and pending checks once the database thread is done, calling
- // back any clients with 'URL_SAFE'.
- while (!queued_checks_.empty()) {
- QueuedCheck check = queued_checks_.front();
- if (check.client)
- check.client->OnUrlCheckResult(check.url, URL_SAFE);
- queued_checks_.pop_front();
- }
-
- for (CurrentChecks::iterator it = checks_.begin();
- it != checks_.end(); ++it) {
- if ((*it)->client)
- (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
- delete *it;
- }
- checks_.clear();
-
- gethash_requests_.clear();
-}
-
-// Runs on the UI thread.
-void SafeBrowsingService::OnEnable(bool enabled) {
- if (enabled)
- Start();
- else
- ShutDown();
-}
-
bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
return url.SchemeIs(chrome::kHttpScheme) ||
url.SchemeIs(chrome::kHttpsScheme);
@@ -242,6 +106,25 @@ bool SafeBrowsingService::CheckUrl(const GURL& url, Client* client) {
return false;
}
+void SafeBrowsingService::CancelCheck(Client* client) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+
+ for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
+ if ((*i)->client == client)
+ (*i)->client = NULL;
+ }
+
+ // Scan the queued clients store. Clients may be here if they requested a URL
+ // check before the database has finished loading or resetting.
+ if (!database_loaded_ || resetting_) {
+ std::deque<QueuedCheck>::iterator it = queued_checks_.begin();
+ for (; it != queued_checks_.end(); ++it) {
+ if (it->client == client)
+ it->client = NULL;
+ }
+ }
+}
+
void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
ResourceType::Type resource_type,
UrlCheckResult result,
@@ -279,143 +162,6 @@ void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
this, &SafeBrowsingService::DoDisplayBlockingPage, resource));
}
-// Invoked on the UI thread.
-void SafeBrowsingService::DoDisplayBlockingPage(
- const UnsafeResource& resource) {
- // The tab might have been closed.
- TabContents* wc =
- tab_util::GetTabContentsByID(resource.render_process_host_id,
- resource.render_view_id);
-
- if (!wc) {
- // The tab is gone and we did not have a chance at showing the interstitial.
- // Just act as "Don't Proceed" was chosen.
- std::vector<UnsafeResource> resources;
- resources.push_back(resource);
- ChromeThread::PostTask(
- ChromeThread::IO, FROM_HERE,
- NewRunnableMethod(
- this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
- return;
- }
-
- // Report the malware sub-resource to the SafeBrowsing servers if we have a
- // malware sub-resource on a safe page and only if the user has opted in to
- // reporting statistics.
- PrefService* prefs = g_browser_process->local_state();
- DCHECK(prefs);
- if (prefs && prefs->GetBoolean(prefs::kMetricsReportingEnabled) &&
- resource.resource_type != ResourceType::MAIN_FRAME &&
- resource.threat_type == SafeBrowsingService::URL_MALWARE) {
- GURL page_url = wc->GetURL();
- GURL referrer_url;
- NavigationEntry* entry = wc->controller().GetActiveEntry();
- if (entry)
- referrer_url = entry->referrer();
- ChromeThread::PostTask(
- ChromeThread::IO, FROM_HERE,
- NewRunnableMethod(this,
- &SafeBrowsingService::ReportMalware,
- resource.url,
- page_url,
- referrer_url));
- }
-
- SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
-}
-
-void SafeBrowsingService::CancelCheck(Client* client) {
- DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
-
- for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
- if ((*i)->client == client)
- (*i)->client = NULL;
- }
-
- // Scan the queued clients store. Clients may be here if they requested a URL
- // check before the database has finished loading or resetting.
- if (!database_loaded_ || resetting_) {
- std::deque<QueuedCheck>::iterator it = queued_checks_.begin();
- for (; it != queued_checks_.end(); ++it) {
- if (it->client == client)
- it->client = NULL;
- }
- }
-}
-
-void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
- DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
-
- // If we've been shutdown during the database lookup, this check will already
- // have been deleted (in OnIOShutdown).
- if (!enabled_ || checks_.find(check) == checks_.end())
- return;
-
- if (check->client && check->need_get_hash) {
- // We have a partial match so we need to query Google for the full hash.
- // Clean up will happen in HandleGetHashResults.
-
- // See if we have a GetHash request already in progress for this particular
- // prefix. If so, we just append ourselves to the list of interested parties
- // when the results arrive. We only do this for checks involving one prefix,
- // since that is the common case (multiple prefixes will issue the request
- // as normal).
- if (check->prefix_hits.size() == 1) {
- SBPrefix prefix = check->prefix_hits[0];
- GetHashRequests::iterator it = gethash_requests_.find(prefix);
- if (it != gethash_requests_.end()) {
- // There's already a request in progress.
- it->second.push_back(check);
- return;
- }
-
- // No request in progress, so we're the first for this prefix.
- GetHashRequestors requestors;
- requestors.push_back(check);
- gethash_requests_[prefix] = requestors;
- }
-
- // Reset the start time so that we can measure the network time without the
- // database time.
- check->start = Time::Now();
- protocol_manager_->GetFullHash(check, check->prefix_hits);
- } else {
- // We may have cached results for previous GetHash queries.
- HandleOneCheck(check, check->full_hits);
- }
-}
-
-SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
- DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
- if (database_)
- return database_;
-
- FilePath path;
- bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
- DCHECK(result);
- path = path.Append(chrome::kSafeBrowsingFilename);
-
- Time before = Time::Now();
- SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
- Callback0::Type* chunk_callback =
- NewCallback(this, &SafeBrowsingService::ChunkInserted);
- database->Init(path, chunk_callback);
- database_ = database;
-
- ChromeThread::PostTask(
- ChromeThread::IO, FROM_HERE,
- NewRunnableMethod(this, &SafeBrowsingService::DatabaseLoadComplete));
-
- TimeDelta open_time = Time::Now() - before;
- SB_DLOG(INFO) << "SafeBrowsing database open took " <<
- open_time.InMilliseconds() << " ms.";
-
- return database_;
-}
-
-// Public API called only on the IO thread.
-// The SafeBrowsingProtocolManager has received the full hash results for
-// prefix hits detected in the database.
void SafeBrowsingService::HandleGetHashResults(
SafeBrowsingCheck* check,
const std::vector<SBFullHashResult>& full_hashes,
@@ -436,48 +182,20 @@ void SafeBrowsingService::HandleGetHashResults(
}
}
-void SafeBrowsingService::OnHandleGetHashResults(
- SafeBrowsingCheck* check,
- const std::vector<SBFullHashResult>& full_hashes) {
- SBPrefix prefix = check->prefix_hits[0];
- GetHashRequests::iterator it = gethash_requests_.find(prefix);
- if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
- HandleOneCheck(check, full_hashes);
- return;
- }
-
- // Call back all interested parties.
- GetHashRequestors& requestors = it->second;
- for (GetHashRequestors::iterator r = requestors.begin();
- r != requestors.end(); ++r) {
- HandleOneCheck(*r, full_hashes);
- }
-
- gethash_requests_.erase(it);
+void SafeBrowsingService::HandleChunk(const std::string& list,
+ std::deque<SBChunk>* chunks) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ DCHECK(enabled_);
+ safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
}
-void SafeBrowsingService::HandleOneCheck(
- SafeBrowsingCheck* check,
- const std::vector<SBFullHashResult>& full_hashes) {
- if (check->client) {
- UrlCheckResult result = URL_SAFE;
- int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
- if (index != -1) {
- result = GetResultFromListname(full_hashes[index].list_name);
- } else {
- // Log the case where the SafeBrowsing servers return full hashes in the
- // GetHash response that match the prefix we're looking up, but don't
- // match the full hash of the URL.
- if (!full_hashes.empty())
- UMA_HISTOGRAM_COUNTS("SB2.GetHashServerMiss", 1);
- }
-
- // Let the client continue handling the original request.
- check->client->OnUrlCheckResult(check->url, result);
- }
-
- checks_.erase(check);
- delete check;
+void SafeBrowsingService::HandleChunkDelete(
+ std::vector<SBChunkDelete>* chunk_deletes) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ DCHECK(enabled_);
+ safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
}
void SafeBrowsingService::UpdateStarted() {
@@ -501,12 +219,6 @@ void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
}
}
-void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
- DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
- if (GetDatabase())
- GetDatabase()->UpdateFinished(update_succeeded);
-}
-
void SafeBrowsingService::OnBlockingPageDone(
const std::vector<UnsafeResource>& resources,
bool proceed) {
@@ -528,12 +240,6 @@ void SafeBrowsingService::OnBlockingPageDone(
}
}
-void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
- bool proceed) {
- client->OnBlockingPageComplete(proceed);
-}
-
-// This method runs on the UI loop to access the prefs.
void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
const std::string& wrapped_key) {
PrefService* prefs = g_browser_process->local_state();
@@ -543,32 +249,11 @@ void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
}
}
-void SafeBrowsingService::ChunkInserted() {
- DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
- ChromeThread::PostTask(
- ChromeThread::IO, FROM_HERE,
- NewRunnableMethod(this, &SafeBrowsingService::OnChunkInserted));
-}
-
-void SafeBrowsingService::OnChunkInserted() {
- DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- if (enabled_)
- protocol_manager_->OnChunkInserted();
-}
-
-void SafeBrowsingService::DatabaseLoadComplete() {
- DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- if (!enabled_)
- return;
-
- database_loaded_ = true;
-
- if (protocol_manager_)
- protocol_manager_->Initialize();
-
- // If we have any queued requests, we can now check them.
- if (!resetting_)
- RunQueuedClients();
+void SafeBrowsingService::OnEnable(bool enabled) {
+ if (enabled)
+ Start();
+ else
+ ShutDown();
}
// static
@@ -584,55 +269,168 @@ void SafeBrowsingService::ResetDatabase() {
this, &SafeBrowsingService::OnResetDatabase));
}
-void SafeBrowsingService::OnResetDatabase() {
- DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
- GetDatabase()->ResetDatabase();
- ChromeThread::PostTask(
- ChromeThread::IO, FROM_HERE,
- NewRunnableMethod(this, &SafeBrowsingService::OnResetComplete));
+void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
+ UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
}
-void SafeBrowsingService::OnResetComplete() {
- DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- if (enabled_) {
- resetting_ = false;
- database_loaded_ = true;
- RunQueuedClients();
- }
+SafeBrowsingService::~SafeBrowsingService() {
}
-void SafeBrowsingService::HandleChunk(const std::string& list,
- std::deque<SBChunk>* chunks) {
+void SafeBrowsingService::OnIOInitialize(
+ const std::string& client_key,
+ const std::string& wrapped_key,
+ URLRequestContextGetter* request_context_getter) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- DCHECK(enabled_);
- safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
- this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
+ enabled_ = true;
+
+ // On Windows, get the safe browsing client name from the browser
+ // distribution classes in installer util. These classes don't yet have
+ // an analog on non-Windows builds so just keep the name specified here.
+#if defined(OS_WIN)
+ BrowserDistribution* dist = BrowserDistribution::GetDistribution();
+ std::string client_name(dist->GetSafeBrowsingName());
+#else
+#if defined(GOOGLE_CHROME_BUILD)
+ std::string client_name("googlechrome");
+#else
+ std::string client_name("chromium");
+#endif
+#endif
+
+ protocol_manager_ = new SafeBrowsingProtocolManager(this,
+ client_name,
+ client_key,
+ wrapped_key,
+ request_context_getter);
+
+ // Balance the reference added by Start().
+ request_context_getter->Release();
+
+ // We want to initialize the protocol manager only after the database has
+ // loaded, which we'll receive asynchronously (DatabaseLoadComplete). If
+ // database_loaded_ isn't true, we'll wait for that notification to do the
+ // init.
+ if (database_loaded_)
+ protocol_manager_->Initialize();
}
-void SafeBrowsingService::HandleChunkForDatabase(
- const std::string& list_name,
- std::deque<SBChunk>* chunks) {
+void SafeBrowsingService::OnDBInitialize() {
DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
-
- GetDatabase()->InsertChunks(list_name, chunks);
+ GetDatabase();
}
-void SafeBrowsingService::HandleChunkDelete(
- std::vector<SBChunkDelete>* chunk_deletes) {
+void SafeBrowsingService::OnIOShutdown() {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- DCHECK(enabled_);
- safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
- this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
+ if (!enabled_)
+ return;
+
+ enabled_ = false;
+ resetting_ = false;
+
+ // This cancels all in-flight GetHash requests.
+ delete protocol_manager_;
+ protocol_manager_ = NULL;
+
+ if (safe_browsing_thread_.get())
+ safe_browsing_thread_->message_loop()->DeleteSoon(FROM_HERE, database_);
+
+ // Flush the database thread. Any in-progress database check results will be
+ // ignored and cleaned up below.
+ safe_browsing_thread_.reset(NULL);
+
+ database_ = NULL;
+ database_loaded_ = false;
+
+ // Delete queued and pending checks once the database thread is done, calling
+ // back any clients with 'URL_SAFE'.
+ while (!queued_checks_.empty()) {
+ QueuedCheck check = queued_checks_.front();
+ if (check.client)
+ check.client->OnUrlCheckResult(check.url, URL_SAFE);
+ queued_checks_.pop_front();
+ }
+
+ for (CurrentChecks::iterator it = checks_.begin();
+ it != checks_.end(); ++it) {
+ if ((*it)->client)
+ (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
+ delete *it;
+ }
+ checks_.clear();
+
+ gethash_requests_.clear();
}
-void SafeBrowsingService::DeleteChunks(
- std::vector<SBChunkDelete>* chunk_deletes) {
+SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
+ if (database_)
+ return database_;
- GetDatabase()->DeleteChunks(chunk_deletes);
+ FilePath path;
+ bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
+ DCHECK(result);
+ path = path.Append(chrome::kSafeBrowsingFilename);
+
+ Time before = Time::Now();
+ SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
+ Callback0::Type* chunk_callback =
+ NewCallback(this, &SafeBrowsingService::ChunkInserted);
+ database->Init(path, chunk_callback);
+ database_ = database;
+
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(this, &SafeBrowsingService::DatabaseLoadComplete));
+
+ TimeDelta open_time = Time::Now() - before;
+ SB_DLOG(INFO) << "SafeBrowsing database open took " <<
+ open_time.InMilliseconds() << " ms.";
+
+ return database_;
+}
+
+void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+
+ // If we've been shutdown during the database lookup, this check will already
+ // have been deleted (in OnIOShutdown).
+ if (!enabled_ || checks_.find(check) == checks_.end())
+ return;
+
+ if (check->client && check->need_get_hash) {
+ // We have a partial match so we need to query Google for the full hash.
+ // Clean up will happen in HandleGetHashResults.
+
+ // See if we have a GetHash request already in progress for this particular
+ // prefix. If so, we just append ourselves to the list of interested parties
+ // when the results arrive. We only do this for checks involving one prefix,
+ // since that is the common case (multiple prefixes will issue the request
+ // as normal).
+ if (check->prefix_hits.size() == 1) {
+ SBPrefix prefix = check->prefix_hits[0];
+ GetHashRequests::iterator it = gethash_requests_.find(prefix);
+ if (it != gethash_requests_.end()) {
+ // There's already a request in progress.
+ it->second.push_back(check);
+ return;
+ }
+
+ // No request in progress, so we're the first for this prefix.
+ GetHashRequestors requestors;
+ requestors.push_back(check);
+ gethash_requests_[prefix] = requestors;
+ }
+
+ // Reset the start time so that we can measure the network time without the
+ // database time.
+ check->start = Time::Now();
+ protocol_manager_->GetFullHash(check, check->prefix_hits);
+ } else {
+ // We may have cached results for previous GetHash queries.
+ HandleOneCheck(check, check->full_hits);
+ }
}
-// Database worker function.
void SafeBrowsingService::GetAllChunksFromDatabase() {
DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
bool database_error = true;
@@ -653,7 +451,6 @@ void SafeBrowsingService::GetAllChunksFromDatabase() {
database_error));
}
-// Called on the io thread with the results of all chunks.
void SafeBrowsingService::OnGetAllChunksFromDatabase(
const std::vector<SBListChunkRanges>& lists, bool database_error) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
@@ -661,6 +458,49 @@ void SafeBrowsingService::OnGetAllChunksFromDatabase(
protocol_manager_->OnGetChunksComplete(lists, database_error);
}
+void SafeBrowsingService::ChunkInserted() {
+ DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(this, &SafeBrowsingService::OnChunkInserted));
+}
+
+void SafeBrowsingService::OnChunkInserted() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ if (enabled_)
+ protocol_manager_->OnChunkInserted();
+}
+
+void SafeBrowsingService::DatabaseLoadComplete() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ if (!enabled_)
+ return;
+
+ database_loaded_ = true;
+
+ if (protocol_manager_)
+ protocol_manager_->Initialize();
+
+ // If we have any queued requests, we can now check them.
+ if (!resetting_)
+ RunQueuedClients();
+}
+
+void SafeBrowsingService::HandleChunkForDatabase(
+ const std::string& list_name,
+ std::deque<SBChunk>* chunks) {
+ DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
+
+ GetDatabase()->InsertChunks(list_name, chunks);
+}
+
+void SafeBrowsingService::DeleteChunks(
+ std::vector<SBChunkDelete>* chunk_deletes) {
+ DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
+
+ GetDatabase()->DeleteChunks(chunk_deletes);
+}
+
SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
const std::string& list_name) {
if (safe_browsing_util::IsPhishingList(list_name)) {
@@ -675,8 +515,63 @@ SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
return URL_SAFE;
}
-void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
- UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
+void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
+ bool proceed) {
+ client->OnBlockingPageComplete(proceed);
+}
+
+void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
+ DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
+ if (GetDatabase())
+ GetDatabase()->UpdateFinished(update_succeeded);
+}
+
+void SafeBrowsingService::Start() {
+ DCHECK(!safe_browsing_thread_.get());
+ safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
+ if (!safe_browsing_thread_->Start())
+ return;
+
+ // Retrieve client MAC keys.
+ PrefService* local_state = g_browser_process->local_state();
+ std::string client_key, wrapped_key;
+ if (local_state) {
+ client_key =
+ WideToASCII(local_state->GetString(prefs::kSafeBrowsingClientKey));
+ wrapped_key =
+ WideToASCII(local_state->GetString(prefs::kSafeBrowsingWrappedKey));
+ }
+
+ // We will issue network fetches using the default profile's request context.
+ URLRequestContextGetter* request_context_getter =
+ GetDefaultProfile()->GetRequestContext();
+ request_context_getter->AddRef(); // Balanced in OnIOInitialize.
+
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(
+ this, &SafeBrowsingService::OnIOInitialize, client_key, wrapped_key,
+ request_context_getter));
+
+ safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &SafeBrowsingService::OnDBInitialize));
+}
+
+void SafeBrowsingService::OnResetDatabase() {
+ DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
+ GetDatabase()->ResetDatabase();
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(this, &SafeBrowsingService::OnResetComplete));
+}
+
+void SafeBrowsingService::OnResetComplete() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ if (enabled_) {
+ resetting_ = false;
+ database_loaded_ = true;
+ RunQueuedClients();
+ }
}
void SafeBrowsingService::CacheHashResults(
@@ -686,15 +581,92 @@ void SafeBrowsingService::CacheHashResults(
GetDatabase()->CacheHashResults(prefixes, full_hashes);
}
-void SafeBrowsingService::RunQueuedClients() {
- DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
- HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
- while (!queued_checks_.empty()) {
- QueuedCheck check = queued_checks_.front();
- HISTOGRAM_TIMES("SB.QueueDelay", Time::Now() - check.start);
- CheckUrl(check.url, check.client);
- queued_checks_.pop_front();
+void SafeBrowsingService::OnHandleGetHashResults(
+ SafeBrowsingCheck* check,
+ const std::vector<SBFullHashResult>& full_hashes) {
+ SBPrefix prefix = check->prefix_hits[0];
+ GetHashRequests::iterator it = gethash_requests_.find(prefix);
+ if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
+ HandleOneCheck(check, full_hashes);
+ return;
+ }
+
+ // Call back all interested parties.
+ GetHashRequestors& requestors = it->second;
+ for (GetHashRequestors::iterator r = requestors.begin();
+ r != requestors.end(); ++r) {
+ HandleOneCheck(*r, full_hashes);
+ }
+
+ gethash_requests_.erase(it);
+}
+
+void SafeBrowsingService::HandleOneCheck(
+ SafeBrowsingCheck* check,
+ const std::vector<SBFullHashResult>& full_hashes) {
+ if (check->client) {
+ UrlCheckResult result = URL_SAFE;
+ int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
+ if (index != -1) {
+ result = GetResultFromListname(full_hashes[index].list_name);
+ } else {
+ // Log the case where the SafeBrowsing servers return full hashes in the
+ // GetHash response that match the prefix we're looking up, but don't
+ // match the full hash of the URL.
+ if (!full_hashes.empty())
+ UMA_HISTOGRAM_COUNTS("SB2.GetHashServerMiss", 1);
+ }
+
+ // Let the client continue handling the original request.
+ check->client->OnUrlCheckResult(check->url, result);
+ }
+
+ checks_.erase(check);
+ delete check;
+}
+
+void SafeBrowsingService::DoDisplayBlockingPage(
+ const UnsafeResource& resource) {
+ // The tab might have been closed.
+ TabContents* wc =
+ tab_util::GetTabContentsByID(resource.render_process_host_id,
+ resource.render_view_id);
+
+ if (!wc) {
+ // The tab is gone and we did not have a chance at showing the interstitial.
+ // Just act as "Don't Proceed" was chosen.
+ std::vector<UnsafeResource> resources;
+ resources.push_back(resource);
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(
+ this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
+ return;
}
+
+ // Report the malware sub-resource to the SafeBrowsing servers if we have a
+ // malware sub-resource on a safe page and only if the user has opted in to
+ // reporting statistics.
+ PrefService* prefs = g_browser_process->local_state();
+ DCHECK(prefs);
+ if (prefs && prefs->GetBoolean(prefs::kMetricsReportingEnabled) &&
+ resource.resource_type != ResourceType::MAIN_FRAME &&
+ resource.threat_type == SafeBrowsingService::URL_MALWARE) {
+ GURL page_url = wc->GetURL();
+ GURL referrer_url;
+ NavigationEntry* entry = wc->controller().GetActiveEntry();
+ if (entry)
+ referrer_url = entry->referrer();
+ ChromeThread::PostTask(
+ ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(this,
+ &SafeBrowsingService::ReportMalware,
+ resource.url,
+ page_url,
+ referrer_url));
+ }
+
+ SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
}
void SafeBrowsingService::ReportMalware(const GURL& malware_url,
@@ -716,3 +688,14 @@ void SafeBrowsingService::ReportMalware(const GURL& malware_url,
if (full_hits.empty())
protocol_manager_->ReportMalware(malware_url, page_url, referrer_url);
}
+
+void SafeBrowsingService::RunQueuedClients() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
+ while (!queued_checks_.empty()) {
+ QueuedCheck check = queued_checks_.front();
+ HISTOGRAM_TIMES("SB.QueueDelay", Time::Now() - check.start);
+ CheckUrl(check.url, check.client);
+ queued_checks_.pop_front();
+ }
+}
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.h b/chrome/browser/safe_browsing/safe_browsing_service.h
index 09d5792..bd466f6 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.h
+++ b/chrome/browser/safe_browsing/safe_browsing_service.h
@@ -15,8 +15,6 @@
#include "base/hash_tables.h"
#include "base/ref_counted.h"
-#include "base/scoped_ptr.h"
-#include "base/thread.h"
#include "base/time.h"
#include "chrome/browser/safe_browsing/safe_browsing_util.h"
#include "googleurl/src/gurl.h"
@@ -29,6 +27,10 @@ class SafeBrowsingDatabase;
class SafeBrowsingProtocolManager;
class URLRequestContextGetter;
+namespace base {
+class Thread;
+}
+
// Construction needs to happen on the main thread.
class SafeBrowsingService
: public base::RefCountedThreadSafe<SafeBrowsingService> {
@@ -64,40 +66,40 @@ class SafeBrowsingService
int render_view_id;
};
+ // Bundle of SafeBrowsing state for one URL check.
+ struct SafeBrowsingCheck {
+ GURL url;
+ Client* client;
+ bool need_get_hash;
+ base::Time start; // Time that check was sent to SB service.
+ UrlCheckResult result;
+ std::vector<SBPrefix> prefix_hits;
+ std::vector<SBFullHashResult> full_hits;
+ };
+
// Creates the safe browsing service. Need to initialize before using.
SafeBrowsingService();
- // Initializes the service.
+ // Called on the UI thread to initialize the service.
void Initialize();
- // Called to initialize objects that are used on the io_thread.
- void OnIOInitialize(const std::string& client_key,
- const std::string& wrapped_key,
- URLRequestContextGetter* request_context_getter);
-
- // Called to initialize objects that are used on the db_thread.
- void OnDBInitialize();
-
- // Called to shutdown operations on the io_thread.
- void OnIOShutdown();
-
// Called on the main thread to let us know that the io_thread is going away.
void ShutDown();
- // Called on the IO thread.
-
// Returns true if the url's scheme can be checked.
bool CanCheckUrl(const GURL& url) const;
- // Checks if the given url is safe or not. If we can synchronously determine
- // that the url is safe, CheckUrl returns true. Otherwise it returns false,
- // and "client" is called asynchronously with the result when it is ready.
+ // Called on the IO thread to check if the given url is safe or not. If we
+ // can synchronously determine that the url is safe, CheckUrl returns true.
+ // Otherwise it returns false, and "client" is called asynchronously with the
+ // result when it is ready.
bool CheckUrl(const GURL& url, Client* client);
- // Cancels a pending check if the result is no longer needed.
+ // Called on the IO thread to cancel a pending check if the result is no
+ // longer needed.
void CancelCheck(Client* client);
- // Displays an interstitial page.
+ // Called on the IO thread to display an interstitial page.
void DisplayBlockingPage(const GURL& url,
ResourceType::Type resource_type,
UrlCheckResult result,
@@ -105,27 +107,18 @@ class SafeBrowsingService
int render_process_host_id,
int render_view_id);
- // Bundle of SafeBrowsing state for one URL check.
- struct SafeBrowsingCheck {
- GURL url;
- Client* client;
- bool need_get_hash;
- base::Time start; // Time that check was sent to SB service.
- UrlCheckResult result;
- std::vector<SBPrefix> prefix_hits;
- std::vector<SBFullHashResult> full_hits;
- };
-
- // API used by the SafeBrowsingProtocolManager to interface with the
- // SafeBrowsing storage system.
+ // Called on the IO thread when the SafeBrowsingProtocolManager has received
+ // the full hash results for prefix hits detected in the database.
void HandleGetHashResults(
SafeBrowsingCheck* check,
const std::vector<SBFullHashResult>& full_hashes,
bool can_cache);
+
+ // Called on the IO thread.
void HandleChunk(const std::string& list, std::deque<SBChunk>* chunks);
void HandleChunkDelete(std::vector<SBChunkDelete>* chunk_deletes);
- // Update management.
+ // Update management. Called on the IO thread.
void UpdateStarted();
void UpdateFinished(bool update_succeeded);
@@ -133,25 +126,20 @@ class SafeBrowsingService
void OnBlockingPageDone(const std::vector<UnsafeResource>& resources,
bool proceed);
- // Called when the SafeBrowsingProtocolManager has received updated MAC keys.
+ // Called on the UI thread when the SafeBrowsingProtocolManager has received
+ // updated MAC keys.
void OnNewMacKeys(const std::string& client_key,
const std::string& wrapped_key);
- // Notification from the advanced options UI.
+ // Notification on the UI thread from the advanced options UI.
void OnEnable(bool enabled);
- bool enabled() const { return enabled_; }
-
- // Called by the database (on the db thread) when a chunk insertion is
- // complete.
- void ChunkInserted();
- // Notification from the database when it's done loading its bloom filter.
- void DatabaseLoadComplete();
+ bool enabled() const { return enabled_; }
// Preference handling.
static void RegisterPrefs(PrefService* prefs);
- // The SafeBrowsing system has instructed us to reset our database.
+ // Called on the IO thread to reset the database.
void ResetDatabase();
// Log the user perceived delay caused by SafeBrowsing. This delay is the time
@@ -160,41 +148,69 @@ class SafeBrowsingService
// the current page is 'safe'.
void LogPauseDelay(base::TimeDelta time);
- // Report any pages that contain malware sub-resources to the SafeBrowsing
- // service.
- void ReportMalware(const GURL& malware_url,
- const GURL& page_url,
- const GURL& referrer_url);
-
private:
+ typedef std::set<SafeBrowsingCheck*> CurrentChecks;
+ typedef std::vector<SafeBrowsingCheck*> GetHashRequestors;
+ typedef base::hash_map<SBPrefix, GetHashRequestors> GetHashRequests;
+
+ // Used for whitelisting a render view when the user ignores our warning.
+ struct WhiteListedEntry {
+ int render_process_host_id;
+ int render_view_id;
+ std::string domain;
+ UrlCheckResult result;
+ };
+
+ // Clients that we've queued up for checking later once the database is ready.
+ struct QueuedCheck {
+ Client* client;
+ GURL url;
+ base::Time start;
+ };
+
friend class base::RefCountedThreadSafe<SafeBrowsingService>;
~SafeBrowsingService();
+ // Called to initialize objects that are used on the io_thread.
+ void OnIOInitialize(const std::string& client_key,
+ const std::string& wrapped_key,
+ URLRequestContextGetter* request_context_getter);
+
+ // Called to initialize objects that are used on the db_thread.
+ void OnDBInitialize();
+
+ // Called to shutdown operations on the io_thread.
+ void OnIOShutdown();
+
// Should only be called on db thread as SafeBrowsingDatabase is not
// threadsafe.
SafeBrowsingDatabase* GetDatabase();
- // Release the final reference to the database on the db thread.
- void ReleaseDatabase(SafeBrowsingDatabase* database);
-
// Called on the IO thread with the check result.
void OnCheckDone(SafeBrowsingCheck* info);
// Called on the database thread to retrieve chunks.
void GetAllChunksFromDatabase();
- // Called on the IOthread with the results of all chunks.
+ // Called on the IO thread with the results of all chunks.
void OnGetAllChunksFromDatabase(const std::vector<SBListChunkRanges>& lists,
bool database_error);
+ // Called on the db thread when a chunk insertion is complete.
+ void ChunkInserted();
+
// Called on the IO thread after the database reports that it added a chunk.
void OnChunkInserted();
+ // Notification that the database is done loading its bloom filter.
+ void DatabaseLoadComplete();
+
// Called on the database thread to add/remove chunks and host keys.
// Callee will free the data when it's done.
void HandleChunkForDatabase(const std::string& list,
std::deque<SBChunk>* chunks);
+
void DeleteChunks(std::vector<SBChunkDelete>* chunk_deletes);
static UrlCheckResult GetResultFromListname(const std::string& list_name);
@@ -203,8 +219,10 @@ class SafeBrowsingService
void DatabaseUpdateFinished(bool update_succeeded);
+ // Start up SafeBrowsing objects. This can be called at browser start, or when
+ // the user checks the "Enable SafeBrowsing" option in the Advanced options
+ // UI.
void Start();
- void Stop();
// Runs on the db thread to reset the database. We assume that resetting the
// database is a synchronous operation.
@@ -227,19 +245,20 @@ class SafeBrowsingService
// Invoked on the UI thread to show the blocking page.
void DoDisplayBlockingPage(const UnsafeResource& resource);
+ // Report any pages that contain malware sub-resources to the SafeBrowsing
+ // service.
+ void ReportMalware(const GURL& malware_url,
+ const GURL& page_url,
+ const GURL& referrer_url);
+
// During a reset or the initial load we may have to queue checks until the
// database is ready. This method is run once the database has loaded (or if
// we shut down SafeBrowsing before the database has finished loading).
void RunQueuedClients();
- void OnUpdateComplete(bool update_succeeded);
-
- typedef std::set<SafeBrowsingCheck*> CurrentChecks;
CurrentChecks checks_;
// Used for issuing only one GetHash request for a given prefix.
- typedef std::vector<SafeBrowsingCheck*> GetHashRequestors;
- typedef base::hash_map<SBPrefix, GetHashRequestors> GetHashRequests;
GetHashRequests gethash_requests_;
// The sqlite database. We don't use a scoped_ptr because it needs to be
@@ -249,14 +268,6 @@ class SafeBrowsingService
// Handles interaction with SafeBrowsing servers.
SafeBrowsingProtocolManager* protocol_manager_;
- // Used for whitelisting a render view when the user ignores our warning.
- struct WhiteListedEntry {
- int render_process_host_id;
- int render_view_id;
- std::string domain;
- UrlCheckResult result;
- };
-
std::vector<WhiteListedEntry> white_listed_entries_;
// Whether the service is running. 'enabled_' is used by SafeBrowsingService
@@ -275,12 +286,6 @@ class SafeBrowsingService
// Indicates if we're currently in an update cycle.
bool update_in_progress_;
- // Clients that we've queued up for checking later once the database is ready.
- typedef struct {
- Client* client;
- GURL url;
- base::Time start;
- } QueuedCheck;
std::deque<QueuedCheck> queued_checks_;
DISALLOW_COPY_AND_ASSIGN(SafeBrowsingService);