Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 |
| OLD | NEW |