Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(127)

Side by Side Diff: components/safe_browsing_db/database_manager.cc

Issue 2009183002: SafeBrowsing: Implement cache lookup for full hash checks (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Cleanup Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/safe_browsing_db/database_manager.h" 5 #include "components/safe_browsing_db/database_manager.h"
6 6
7 #include "components/safe_browsing_db/v4_get_hash_protocol_manager.h" 7 #include "components/safe_browsing_db/v4_get_hash_protocol_manager.h"
8 #include "content/public/browser/browser_thread.h" 8 #include "content/public/browser/browser_thread.h"
9 #include "net/url_request/url_request_context_getter.h" 9 #include "net/url_request/url_request_context_getter.h"
10 #include "url/gurl.h" 10 #include "url/gurl.h"
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
87 87
88 // There can only be one in-progress check for the same client at a time. 88 // There can only be one in-progress check for the same client at a time.
89 DCHECK(FindClientApiCheck(client) == api_checks_.end()); 89 DCHECK(FindClientApiCheck(client) == api_checks_.end());
90 90
91 // Compute a list of hashes for this url. 91 // Compute a list of hashes for this url.
92 std::vector<SBFullHash> full_hashes; 92 std::vector<SBFullHash> full_hashes;
93 UrlToFullHashes(url, false, &full_hashes); 93 UrlToFullHashes(url, false, &full_hashes);
94 if (full_hashes.empty()) 94 if (full_hashes.empty())
95 return true; 95 return true;
96 96
97 // Copy to prefixes. 97 // First check the cache.
98
99 // Used to determine cache expiration.
100 base::Time now = base::Time::Now();
101
102 std::vector<SBFullHashResult> cached_results;
98 std::vector<SBPrefix> prefixes; 103 std::vector<SBPrefix> prefixes;
Nathan Parker 2016/05/26 22:30:55 maybe "prefixes_needing_reqs"? Otherwise it looks
awoz 2016/05/27 16:54:36 +1
kcarattini 2016/05/30 05:22:51 Done.
99 for (const SBFullHash& full_hash : full_hashes) { 104 GetCachedResults(full_hashes, now, &prefixes, &cached_results);
100 prefixes.push_back(full_hash.prefix); 105
101 } 106 if (prefixes.empty() && cached_results.empty())
102 // Multiple full hashes could share a prefix, remove duplicates. 107 return true;
103 std::sort(prefixes.begin(), prefixes.end());
104 prefixes.erase(std::unique(prefixes.begin(), prefixes.end()), prefixes.end());
105 DCHECK(!prefixes.empty());
106 108
107 SafeBrowsingApiCheck* check = 109 SafeBrowsingApiCheck* check =
108 new SafeBrowsingApiCheck(url, prefixes, full_hashes, client); 110 new SafeBrowsingApiCheck(url, prefixes, full_hashes, cached_results,
111 client);
109 api_checks_.insert(check); 112 api_checks_.insert(check);
110 113
111 // TODO(kcarattini): Implement cache compliance. 114 if (prefixes.empty()) {
115 // We can call the callback immediately if no prefixes require a request.
116 std::vector<SBFullHashResult> full_hash_results;
117 HandleGetHashesWithApisResults(check, full_hash_results, base::Time());
Nathan Parker 2016/05/26 22:30:55 Shouldn't this be cached_results?
kcarattini 2016/05/30 05:22:50 The cached_results are stored in the check (passed
118 return false;
119 }
120
112 v4_get_hash_protocol_manager_->GetFullHashesWithApis(prefixes, 121 v4_get_hash_protocol_manager_->GetFullHashesWithApis(prefixes,
113 base::Bind(&SafeBrowsingDatabaseManager::HandleGetHashesWithApisResults, 122 base::Bind(&SafeBrowsingDatabaseManager::HandleGetHashesWithApisResults,
114 base::Unretained(this), check)); 123 base::Unretained(this), check));
115 124
116 return false; 125 return false;
117 } 126 }
118 127
128 void SafeBrowsingDatabaseManager::GetCachedResults(
129 const std::vector<SBFullHash>& full_hashes,
130 base::Time now,
131 std::vector<SBPrefix>* prefixes,
132 std::vector<SBFullHashResult>* cached_results) {
133 DCHECK(prefixes);
134 prefixes->clear();
135 DCHECK(cached_results);
136 cached_results->clear();
137
138 if (full_hashes.empty())
Nathan Parker 2016/05/26 22:30:55 nit: This is duplicate of that up on line 94
kcarattini 2016/05/30 05:22:50 Removed.
139 return;
140
141 // Caching behavior is documented here:
142 // https://devsite.googleplex.com/safe-browsing/v4/caching#about-caching
awoz 2016/05/27 16:54:36 Can you update this to the public URL? https://dev
kcarattini 2016/05/30 05:22:50 Done.
143 //
144 // The cache operates as follows:
Nathan Parker 2016/05/26 22:30:55 Nice documentation!
awoz 2016/05/27 16:54:36 +1!
kcarattini 2016/05/30 05:22:50 Thanks!
145 // Lookup:
146 // Case 1: The prefix is in the cache.
147 // Case a: The full hash is in the cache.
148 // Case i : The positive full hash result has not expired.
149 // The result is unsafe and we do not need to send a new
150 // request.
151 // Case ii: The positive full hash result has expired.
152 // We need to send a request for full hashes.
153 // Case b: The full hash is not in the cache.
154 // Case i : The negative cache entry has not expired.
155 // The result is still safe and we do not need to send a
156 // new request.
157 // Case ii: The negative cache entry has expired.
158 // We need to send a request for full hashes.
159 // Case 2: The prefix is not in the cache.
160 // We need to send a request for full hashes.
161 //
162 // Eviction:
163 // SBCachedFullHashResult entries can be removed from the cache only when
164 // the negative cache expire time and the cache expire time of all full
165 // hash results for that prefix have expired.
166 // Individual full hash results can be removed from the prefix'
awoz 2016/05/27 16:54:36 s/prefix'/prefix's
kcarattini 2016/05/30 05:22:51 Done.
167 // cache entry if they expire AND their expire time is after the negative
168 // cache expire time.
169 //
170 // TODO(kcarattini): Implement cache eviction.
171 for (const SBFullHash& full_hash : full_hashes) {
172 auto entry = api_cache_.find(full_hash.prefix);
173 if (entry != api_cache_.end()) {
174 // Case 1.
175 const SBCachedFullHashResult& cache_result = entry->second;
176 bool full_hash_in_cache = false;
177 size_t i = 0;
178
179 for (; i < cache_result.full_hashes.size(); ++i) {
Nathan Parker 2016/05/26 22:30:55 Not sure if this is better, you decide. It's one
kcarattini 2016/05/30 05:22:50 Done.
180 const SBFullHashResult& hash_result = cache_result.full_hashes[i];
181 if (SBFullHashEqual(full_hash, hash_result.hash)) {
182 full_hash_in_cache = true;
183 break;
184 }
185 }
186
187 if (full_hash_in_cache) {
188 // Case a.
189 const SBFullHashResult& hash_result = cache_result.full_hashes[i];
190 if (hash_result.cache_expire_after > now) {
191 // Case i.
192 cached_results->push_back(hash_result);
193 } else {
194 // Case ii.
195 prefixes->push_back(full_hash.prefix);
196 }
197 } else {
198 // Case b.
199 if (cache_result.expire_after > now) {
200 // Case i.
Nathan Parker 2016/05/26 22:30:55 This is a little weird, but I see the point of the
kcarattini 2016/05/30 05:22:50 Acknowledged.
201 } else {
202 // Case ii.
203 prefixes->push_back(full_hash.prefix);
204 }
205 }
206 } else {
207 // Case 2.
208 prefixes->push_back(full_hash.prefix);
209 }
210 }
211
212 // Multiple full hashes could share a prefix, remove duplicates.
213 std::sort(prefixes->begin(), prefixes->end());
214 prefixes->erase(std::unique(
215 prefixes->begin(), prefixes->end()), prefixes->end());
216 }
217
119 void SafeBrowsingDatabaseManager::HandleGetHashesWithApisResults( 218 void SafeBrowsingDatabaseManager::HandleGetHashesWithApisResults(
120 SafeBrowsingApiCheck* check, 219 SafeBrowsingApiCheck* check,
121 const std::vector<SBFullHashResult>& full_hash_results, 220 const std::vector<SBFullHashResult>& full_hash_results,
122 const base::Time& negative_cache_expire) { 221 const base::Time& negative_cache_expire) {
123 DCHECK_CURRENTLY_ON(BrowserThread::IO); 222 DCHECK_CURRENTLY_ON(BrowserThread::IO);
124 DCHECK(check); 223 DCHECK(check);
125 224
126 // If the time is uninitialized, don't cache the results. 225 // If the time is uninitialized, don't cache the results.
127 if (!negative_cache_expire.is_null()) { 226 if (!negative_cache_expire.is_null()) {
128 // Cache the results. 227 // Cache the results.
129 // Create or reset all cached results for this prefix. 228 // Create or reset all cached results for this prefix.
130 for (const SBPrefix& prefix : check->prefixes()) { 229 for (const SBPrefix& prefix : check->prefixes()) {
131 api_cache_[prefix] = SBCachedFullHashResult(negative_cache_expire); 230 api_cache_[prefix] = SBCachedFullHashResult(negative_cache_expire);
132 } 231 }
133 // Insert any full hash hits. Note that there may be one, multiple, or no 232 // Insert any full hash hits. Note that there may be one, multiple, or no
134 // full hashes for any given prefix. 233 // full hashes for any given prefix.
135 for (const SBFullHashResult& result : full_hash_results) { 234 for (const SBFullHashResult& result : full_hash_results) {
136 api_cache_[result.hash.prefix].full_hashes.push_back(result); 235 api_cache_[result.hash.prefix].full_hashes.push_back(result);
137 } 236 }
138 } 237 }
139 238
140 // If the check is not in |api_checks_| then the request was cancelled by the 239 // If the check is not in |api_checks_| then the request was cancelled by the
141 // client. 240 // client.
142 ApiCheckSet::iterator it = api_checks_.find(check); 241 ApiCheckSet::iterator it = api_checks_.find(check);
143 if (it == api_checks_.end()) 242 if (it == api_checks_.end())
144 return; 243 return;
145 244
146 ThreatMetadata md; 245 ThreatMetadata md;
147 // Merge the metadata from all matching results. 246 // Merge the metadata from all matching results.
148 // TODO(kcarattini): This is O(N^2). Look at improving performance by 247 // Note: A full hash may have a result in both the cached results (from
149 // using a map, sorting or doing binary search etc.. 248 // its own cache lookup) and in the server results (if another full hash
150 for (const SBFullHashResult& result : full_hash_results) { 249 // with the same prefix needed to request results from the server). In this
151 for (const SBFullHash& full_hash : check->full_hashes()) { 250 // unlikely case, the two results' metadata will be merged.
152 if (SBFullHashEqual(full_hash, result.hash)) { 251 PopulateMetadataResult(full_hash_results, check->full_hashes(), &md);
153 md.api_permissions.insert(md.api_permissions.end(), 252 PopulateMetadataResult(check->cached_results(), check->full_hashes(), &md);
154 result.metadata.api_permissions.begin(),
155 result.metadata.api_permissions.end());
156 break;
157 }
158 }
159 }
160 253
161 check->client()->OnCheckApiBlacklistUrlResult(check->url(), md); 254 check->client()->OnCheckApiBlacklistUrlResult(check->url(), md);
162 api_checks_.erase(it); 255 api_checks_.erase(it);
163 delete check; 256 delete check;
164 } 257 }
165 258
259 // TODO(kcarattini): This is O(N^2). Look at improving performance by
260 // using a map, sorting or doing binary search etc..
261 void SafeBrowsingDatabaseManager::PopulateMetadataResult(
262 const std::vector<SBFullHashResult>& results,
263 const std::vector<SBFullHash>& full_hashes,
264 ThreatMetadata* md) {
265 DCHECK(md);
266 for (const SBFullHashResult& result : results) {
267 for (const SBFullHash& full_hash : full_hashes) {
268 if (SBFullHashEqual(full_hash, result.hash)) {
269 md->api_permissions.insert(md->api_permissions.end(),
270 result.metadata.api_permissions.begin(),
271 result.metadata.api_permissions.end());
272 break;
273 }
274 }
275 }
276 }
277
166 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::SafeBrowsingApiCheck( 278 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::SafeBrowsingApiCheck(
167 const GURL& url, const std::vector<SBPrefix>& prefixes, 279 const GURL& url,
168 const std::vector<SBFullHash>& full_hashes, Client* client) 280 const std::vector<SBPrefix>& prefixes,
281 const std::vector<SBFullHash>& full_hashes,
282 const std::vector<SBFullHashResult>& cached_results,
283 Client* client)
169 : url_(url), prefixes_(prefixes), full_hashes_(full_hashes), 284 : url_(url), prefixes_(prefixes), full_hashes_(full_hashes),
170 client_(client) { 285 cached_results_(cached_results), client_(client) {
171 } 286 }
172 287
173 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::~SafeBrowsingApiCheck() { 288 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::~SafeBrowsingApiCheck() {
174 } 289 }
175 290
176 } // namespace safe_browsing 291 } // namespace safe_browsing
OLDNEW
« no previous file with comments | « components/safe_browsing_db/database_manager.h ('k') | components/safe_browsing_db/database_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698