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 "base/metrics/histogram_macros.h" | 7 #include "base/metrics/histogram_macros.h" |
| 8 #include "components/safe_browsing_db/v4_get_hash_protocol_manager.h" | 8 #include "components/safe_browsing_db/v4_get_hash_protocol_manager.h" |
| 9 #include "content/public/browser/browser_thread.h" | 9 #include "content/public/browser/browser_thread.h" |
| 10 #include "net/url_request/url_request_context_getter.h" | 10 #include "net/url_request/url_request_context_getter.h" |
| 11 #include "url/gurl.h" | 11 #include "url/gurl.h" |
| 12 | 12 |
| 13 using content::BrowserThread; | 13 using content::BrowserThread; |
| 14 | 14 |
| 15 namespace { | 15 namespace { |
| 16 | 16 |
| 17 // Enumerate full hash cache hits/misses for histogramming purposes. | |
| 18 // DO NOT CHANGE THE ORDERING OF THESE VALUES. | |
| 19 enum V4FullHashCacheResultType { | |
| 20 // Full hashes for which there is no cache hit. | |
| 21 FULL_HASH_CACHE_MISS = 0, | |
| 22 | |
| 23 // Full hashes with a cache hit. | |
| 24 FULL_HASH_CACHE_HIT = 1, | |
| 25 | |
| 26 // Full hashes with a negative cache hit. | |
| 27 FULL_HASH_NEGATIVE_CACHE_HIT = 2, | |
| 28 | |
| 29 // Memory space for histograms is determined by the max. ALWAYS | |
| 30 // ADD NEW VALUES BEFORE THIS ONE. | |
| 31 FULL_HASH_CACHE_RESULT_MAX | |
| 32 }; | |
| 33 | |
| 34 // Enumerate GetHash hits/misses for histogramming purposes. DO NOT CHANGE THE | 17 // Enumerate GetHash hits/misses for histogramming purposes. DO NOT CHANGE THE |
| 35 // ORDERING OF THESE VALUES. | 18 // ORDERING OF THESE VALUES. |
| 36 enum V4GetHashCheckResultType { | 19 enum V4GetHashCheckResultType { |
| 37 // Successful responses which returned no full hashes. | 20 // Successful responses which returned no full hashes. |
| 38 GET_HASH_CHECK_EMPTY = 0, | 21 GET_HASH_CHECK_EMPTY = 0, |
| 39 | 22 |
| 40 // Successful responses for which one or more of the full hashes matched. | 23 // Successful responses for which one or more of the full hashes matched. |
| 41 GET_HASH_CHECK_HIT = 1, | 24 GET_HASH_CHECK_HIT = 1, |
| 42 | 25 |
| 43 // Successful responses which weren't empty but have no matches. | 26 // Successful responses which weren't empty but have no matches. |
| 44 GET_HASH_CHECK_MISS = 2, | 27 GET_HASH_CHECK_MISS = 2, |
| 45 | 28 |
| 46 // Memory space for histograms is determined by the max. ALWAYS | 29 // Memory space for histograms is determined by the max. ALWAYS |
| 47 // ADD NEW VALUES BEFORE THIS ONE. | 30 // ADD NEW VALUES BEFORE THIS ONE. |
| 48 GET_HASH_CHECK_RESULT_MAX | 31 GET_HASH_CHECK_RESULT_MAX |
| 49 }; | 32 }; |
| 50 | 33 |
| 51 // Record a full hash cache hit result. | |
| 52 void RecordV4FullHashCacheResult( | |
| 53 V4FullHashCacheResultType result_type) { | |
| 54 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4FullHashCacheResult", result_type, | |
| 55 FULL_HASH_CACHE_RESULT_MAX); | |
| 56 } | |
| 57 | |
| 58 // Record a GetHash hit result. | 34 // Record a GetHash hit result. |
| 59 void RecordV4GetHashCheckResult( | 35 void RecordV4GetHashCheckResult( |
| 60 V4GetHashCheckResultType result_type) { | 36 V4GetHashCheckResultType result_type) { |
| 61 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4GetHashCheckResult", result_type, | 37 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4GetHashCheckResult", result_type, |
| 62 GET_HASH_CHECK_RESULT_MAX); | 38 GET_HASH_CHECK_RESULT_MAX); |
| 63 } | 39 } |
| 64 | 40 |
| 65 } // namespace | 41 } // namespace |
| 66 | 42 |
| 67 namespace safe_browsing { | 43 namespace safe_browsing { |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 147 if (full_hashes.empty()) | 123 if (full_hashes.empty()) |
| 148 return true; | 124 return true; |
| 149 | 125 |
| 150 // First check the cache. | 126 // First check the cache. |
| 151 | 127 |
| 152 // Used to determine cache expiration. | 128 // Used to determine cache expiration. |
| 153 base::Time now = base::Time::Now(); | 129 base::Time now = base::Time::Now(); |
| 154 | 130 |
| 155 std::vector<SBFullHashResult> cached_results; | 131 std::vector<SBFullHashResult> cached_results; |
| 156 std::vector<SBPrefix> prefixes_needing_reqs; | 132 std::vector<SBPrefix> prefixes_needing_reqs; |
| 157 GetFullHashCachedResults(SB_THREAT_TYPE_API_ABUSE, | 133 v4_get_hash_protocol_manager_->GetFullHashCachedResults(SB_THREAT_TYPE_API_ABU SE, |
|
Nathan Parker
2016/08/15 19:54:47
How about moving all of this code into v4_get_hash
| |
| 158 full_hashes, now, &prefixes_needing_reqs, &cached_results); | 134 full_hashes, now, &prefixes_needing_reqs, &cached_results); |
| 159 | 135 |
| 160 if (prefixes_needing_reqs.empty() && cached_results.empty()) | 136 if (prefixes_needing_reqs.empty() && cached_results.empty()) |
| 161 return true; | 137 return true; |
| 162 | 138 |
| 163 SafeBrowsingApiCheck* check = | 139 SafeBrowsingApiCheck* check = |
| 164 new SafeBrowsingApiCheck(url, prefixes_needing_reqs, full_hashes, | 140 new SafeBrowsingApiCheck(url, prefixes_needing_reqs, full_hashes, |
| 165 cached_results, client); | 141 cached_results, client); |
| 166 api_checks_.insert(check); | 142 api_checks_.insert(check); |
| 167 | 143 |
| 168 if (prefixes_needing_reqs.empty()) { | 144 if (prefixes_needing_reqs.empty()) { |
| 169 check->set_start_time(base::TimeTicks::Now()); | 145 check->set_start_time(base::TimeTicks::Now()); |
| 170 // We can call the callback immediately if no prefixes require a request. | 146 // We can call the callback immediately if no prefixes require a request. |
| 171 // The |full_hash_results| representing the results fromt eh SB server will | 147 // The |full_hash_results| representing the results fromt eh SB server will |
| 172 // be empty. | 148 // be empty. |
| 173 std::vector<SBFullHashResult> full_hash_results; | 149 std::vector<SBFullHashResult> full_hash_results; |
| 174 HandleGetHashesWithApisResults(check, full_hash_results, base::Time()); | 150 HandleGetHashesWithApisResults(check, full_hash_results, base::Time()); |
| 175 return false; | 151 return false; |
| 176 } | 152 } |
| 177 | 153 |
| 178 v4_get_hash_protocol_manager_->GetFullHashesWithApis(prefixes_needing_reqs, | 154 v4_get_hash_protocol_manager_->GetFullHashesWithApis(prefixes_needing_reqs, |
| 179 base::Bind(&SafeBrowsingDatabaseManager::HandleGetHashesWithApisResults, | 155 base::Bind(&SafeBrowsingDatabaseManager::HandleGetHashesWithApisResults, |
| 180 base::Unretained(this), check)); | 156 base::Unretained(this), check)); |
| 181 | 157 |
| 182 return false; | 158 return false; |
| 183 } | 159 } |
| 184 | 160 |
| 185 void SafeBrowsingDatabaseManager::GetFullHashCachedResults( | |
| 186 const SBThreatType& threat_type, | |
| 187 const std::vector<SBFullHash>& full_hashes, | |
| 188 base::Time now, | |
| 189 std::vector<SBPrefix>* prefixes_needing_reqs, | |
| 190 std::vector<SBFullHashResult>* cached_results) { | |
| 191 DCHECK(prefixes_needing_reqs); | |
| 192 prefixes_needing_reqs->clear(); | |
| 193 DCHECK(cached_results); | |
| 194 cached_results->clear(); | |
| 195 | |
| 196 // Caching behavior is documented here: | |
| 197 // https://developers.google.com/safe-browsing/v4/caching#about-caching | |
| 198 // | |
| 199 // The cache operates as follows: | |
| 200 // Lookup: | |
| 201 // Case 1: The prefix is in the cache. | |
| 202 // Case a: The full hash is in the cache. | |
| 203 // Case i : The positive full hash result has not expired. | |
| 204 // The result is unsafe and we do not need to send a new | |
| 205 // request. | |
| 206 // Case ii: The positive full hash result has expired. | |
| 207 // We need to send a request for full hashes. | |
| 208 // Case b: The full hash is not in the cache. | |
| 209 // Case i : The negative cache entry has not expired. | |
| 210 // The result is still safe and we do not need to send a | |
| 211 // new request. | |
| 212 // Case ii: The negative cache entry has expired. | |
| 213 // We need to send a request for full hashes. | |
| 214 // Case 2: The prefix is not in the cache. | |
| 215 // We need to send a request for full hashes. | |
| 216 // | |
| 217 // Eviction: | |
| 218 // SBCachedFullHashResult entries can be removed from the cache only when | |
| 219 // the negative cache expire time and the cache expire time of all full | |
| 220 // hash results for that prefix have expired. | |
| 221 // Individual full hash results can be removed from the prefix's | |
| 222 // cache entry if they expire AND their expire time is after the negative | |
| 223 // cache expire time. | |
| 224 for (const SBFullHash& full_hash : full_hashes) { | |
| 225 auto entry = v4_full_hash_cache_[threat_type].find(full_hash.prefix); | |
| 226 if (entry != v4_full_hash_cache_[threat_type].end()) { | |
| 227 // Case 1. | |
| 228 SBCachedFullHashResult& cache_result = entry->second; | |
| 229 | |
| 230 const SBFullHashResult* found_full_hash = nullptr; | |
| 231 size_t matched_idx = 0; | |
| 232 for (const SBFullHashResult& hash_result : cache_result.full_hashes) { | |
| 233 if (SBFullHashEqual(full_hash, hash_result.hash)) { | |
| 234 found_full_hash = &hash_result; | |
| 235 break; | |
| 236 } | |
| 237 ++matched_idx; | |
| 238 } | |
| 239 | |
| 240 if (found_full_hash) { | |
| 241 // Case a. | |
| 242 if (found_full_hash->cache_expire_after > now) { | |
| 243 // Case i. | |
| 244 cached_results->push_back(*found_full_hash); | |
| 245 RecordV4FullHashCacheResult(FULL_HASH_CACHE_HIT); | |
| 246 } else { | |
| 247 // Case ii. | |
| 248 prefixes_needing_reqs->push_back(full_hash.prefix); | |
| 249 RecordV4FullHashCacheResult(FULL_HASH_CACHE_MISS); | |
| 250 // If the negative cache expire time has passed, evict this full hash | |
| 251 // result from the cache. | |
| 252 if (cache_result.expire_after <= now) { | |
| 253 cache_result.full_hashes.erase( | |
| 254 cache_result.full_hashes.begin() + matched_idx); | |
| 255 // If there are no more full hashes, we can evict the entire entry. | |
| 256 if (cache_result.full_hashes.empty()) { | |
| 257 v4_full_hash_cache_[threat_type].erase(entry); | |
| 258 } | |
| 259 } | |
| 260 } | |
| 261 } else { | |
| 262 // Case b. | |
| 263 if (cache_result.expire_after > now) { | |
| 264 // Case i. | |
| 265 RecordV4FullHashCacheResult(FULL_HASH_NEGATIVE_CACHE_HIT); | |
| 266 } else { | |
| 267 // Case ii. | |
| 268 prefixes_needing_reqs->push_back(full_hash.prefix); | |
| 269 RecordV4FullHashCacheResult(FULL_HASH_CACHE_MISS); | |
| 270 } | |
| 271 } | |
| 272 } else { | |
| 273 // Case 2. | |
| 274 prefixes_needing_reqs->push_back(full_hash.prefix); | |
| 275 RecordV4FullHashCacheResult(FULL_HASH_CACHE_MISS); | |
| 276 } | |
| 277 } | |
| 278 | |
| 279 // Multiple full hashes could share a prefix, remove duplicates. | |
| 280 // TODO(kcarattini): Make |prefixes_needing_reqs| a set. | |
| 281 std::sort(prefixes_needing_reqs->begin(), prefixes_needing_reqs->end()); | |
| 282 prefixes_needing_reqs->erase(std::unique(prefixes_needing_reqs->begin(), | |
| 283 prefixes_needing_reqs->end()), prefixes_needing_reqs->end()); | |
| 284 } | |
| 285 | |
| 286 void SafeBrowsingDatabaseManager::HandleGetHashesWithApisResults( | 161 void SafeBrowsingDatabaseManager::HandleGetHashesWithApisResults( |
| 287 SafeBrowsingApiCheck* check, | 162 SafeBrowsingApiCheck* check, |
| 288 const std::vector<SBFullHashResult>& full_hash_results, | 163 const std::vector<SBFullHashResult>& full_hash_results, |
| 289 const base::Time& negative_cache_expire) { | 164 const base::Time& negative_cache_expire) { |
| 290 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 165 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 291 DCHECK(check); | 166 DCHECK(check); |
| 292 | 167 |
| 293 // Record the network time. | 168 // Record the network time. |
| 294 if (!check->start_time().is_null()) { | 169 if (!check->start_time().is_null()) { |
| 295 UMA_HISTOGRAM_LONG_TIMES("SafeBrowsing.GetV4HashNetwork", | 170 UMA_HISTOGRAM_LONG_TIMES("SafeBrowsing.GetV4HashNetwork", |
| 296 base::TimeTicks::Now() - check->start_time()); | 171 base::TimeTicks::Now() - check->start_time()); |
| 297 } | 172 } |
| 298 | 173 |
| 299 // If the time is uninitialized, don't cache the results. | |
| 300 if (!negative_cache_expire.is_null()) { | |
| 301 // Cache the results. | |
| 302 // Create or reset all cached results for this prefix. | |
| 303 for (const SBPrefix& prefix : check->prefixes()) { | |
| 304 v4_full_hash_cache_[SB_THREAT_TYPE_API_ABUSE][prefix] = | |
| 305 SBCachedFullHashResult(negative_cache_expire); | |
| 306 } | |
| 307 // Insert any full hash hits. Note that there may be one, multiple, or no | |
| 308 // full hashes for any given prefix. | |
| 309 for (const SBFullHashResult& result : full_hash_results) { | |
| 310 v4_full_hash_cache_[SB_THREAT_TYPE_API_ABUSE][result.hash.prefix]. | |
| 311 full_hashes.push_back(result); | |
| 312 } | |
| 313 } | |
| 314 | |
| 315 // If the check is not in |api_checks_| then the request was cancelled by the | 174 // If the check is not in |api_checks_| then the request was cancelled by the |
| 316 // client. | 175 // client. |
| 317 ApiCheckSet::iterator it = api_checks_.find(check); | 176 ApiCheckSet::iterator it = api_checks_.find(check); |
| 318 if (it == api_checks_.end()) | 177 if (it == api_checks_.end()) |
| 319 return; | 178 return; |
| 320 | 179 |
| 321 ThreatMetadata md; | 180 ThreatMetadata md; |
| 322 // Merge the metadata from all matching results. | 181 // Merge the metadata from all matching results. |
| 323 // Note: A full hash may have a result in both the cached results (from | 182 // Note: A full hash may have a result in both the cached results (from |
| 324 // its own cache lookup) and in the server results (if another full hash | 183 // its own cache lookup) and in the server results (if another full hash |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 369 const std::vector<SBFullHashResult>& cached_results, | 228 const std::vector<SBFullHashResult>& cached_results, |
| 370 Client* client) | 229 Client* client) |
| 371 : url_(url), prefixes_(prefixes), full_hashes_(full_hashes), | 230 : url_(url), prefixes_(prefixes), full_hashes_(full_hashes), |
| 372 cached_results_(cached_results), client_(client) { | 231 cached_results_(cached_results), client_(client) { |
| 373 } | 232 } |
| 374 | 233 |
| 375 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::~SafeBrowsingApiCheck() { | 234 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::~SafeBrowsingApiCheck() { |
| 376 } | 235 } |
| 377 | 236 |
| 378 } // namespace safe_browsing | 237 } // namespace safe_browsing |
| OLD | NEW |