| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 // |
| 5 // Implementation of the MalwareDetails class. |
| 6 |
| 7 #include "chrome/browser/safe_browsing/malware_details.h" |
| 8 |
| 9 #include "base/callback.h" |
| 10 #include "base/lazy_instance.h" |
| 11 #include "base/md5.h" |
| 12 #include "chrome/browser/net/chrome_url_request_context.h" |
| 13 #include "chrome/browser/profiles/profile.h" |
| 14 #include "chrome/browser/safe_browsing/malware_details_cache.h" |
| 15 #include "chrome/browser/safe_browsing/safe_browsing_service.h" |
| 16 #include "chrome/browser/safe_browsing/report.pb.h" |
| 17 #include "chrome/common/net/url_request_context_getter.h" |
| 18 #include "content/browser/browser_thread.h" |
| 19 #include "net/base/io_buffer.h" |
| 20 #include "net/disk_cache/disk_cache.h" |
| 21 #include "net/http/http_cache.h" |
| 22 #include "net/http/http_response_headers.h" |
| 23 #include "net/http/http_response_info.h" |
| 24 |
| 25 using safe_browsing::ClientMalwareReportRequest; |
| 26 |
| 27 // Only send small files for now, a better strategy would use the size |
| 28 // of the whole report and the user's bandwidth. |
| 29 static const int kMaxBodySize = 1024; |
| 30 |
| 31 MalwareDetailsCacheCollector::MalwareDetailsCacheCollector(): |
| 32 cache_state_(STATE_NONE), |
| 33 ALLOW_THIS_IN_INITIALIZER_LIST( |
| 34 cache_callback_(this , &MalwareDetailsCacheCollector::CacheLoop)), |
| 35 ALLOW_THIS_IN_INITIALIZER_LIST( |
| 36 entry_callback_( |
| 37 new net::CancelableCompletionCallback<MalwareDetailsCacheCollector>( |
| 38 this, &MalwareDetailsCacheCollector::CacheLoop))) { |
| 39 } |
| 40 |
| 41 MalwareDetailsCacheCollector::~MalwareDetailsCacheCollector() { |
| 42 } |
| 43 |
| 44 // We modify |resources|, so it must remain alive. |
| 45 void MalwareDetailsCacheCollector::StartCacheCollection( |
| 46 URLRequestContextGetter* request_context_getter, |
| 47 safe_browsing::ResourceMap* resources, |
| 48 MalwareDetailsCacheCollectorCallback* callback) { |
| 49 // Start the data collection from the HTTP cache. |
| 50 DVLOG(1) << "Getting disk_cache data for all urls..."; |
| 51 request_context_getter_ = request_context_getter; |
| 52 resources_ = resources; |
| 53 resources_it_ = resources_->begin(); |
| 54 cache_state_ = STATE_OPEN_CACHE; |
| 55 callback_ = callback; |
| 56 |
| 57 AddRef(); // The cache calls are asynchronous. |
| 58 |
| 59 // Post a task in the message loop, so the callers don't need to |
| 60 // check if we call their callback immediately. |
| 61 BrowserThread::PostTask( |
| 62 BrowserThread::IO, FROM_HERE, |
| 63 NewRunnableMethod( |
| 64 this, &MalwareDetailsCacheCollector::CacheLoop, net::OK)); |
| 65 } |
| 66 |
| 67 bool MalwareDetailsCacheCollector::InProgress() { |
| 68 return cache_state_ != STATE_NONE; |
| 69 } |
| 70 |
| 71 // The IO thread loop. Called once the DOM details are callected, |
| 72 // and then is passed to all the cache lookups as a callback. |
| 73 void MalwareDetailsCacheCollector::CacheLoop(int result) { |
| 74 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 75 DCHECK(cache_state_ != STATE_NONE); |
| 76 |
| 77 int rv = result; |
| 78 do { |
| 79 DVLOG(1) << "cache_state_:" << cache_state_; |
| 80 CacheState state = cache_state_; |
| 81 cache_state_ = STATE_NONE; |
| 82 switch (state) { |
| 83 case STATE_OPEN_CACHE: |
| 84 rv = DoOpenCache(); |
| 85 break; |
| 86 case STATE_OPEN_CACHE_COMPLETE: |
| 87 rv = DoOpenCacheComplete(rv); |
| 88 break; |
| 89 case STATE_OPEN_ENTRY: |
| 90 rv = DoOpenEntry(); |
| 91 break; |
| 92 case STATE_OPEN_ENTRY_COMPLETE: |
| 93 rv = DoOpenEntryComplete(rv); |
| 94 break; |
| 95 case STATE_READ_RESPONSE: |
| 96 rv = DoReadResponse(); |
| 97 break; |
| 98 case STATE_READ_RESPONSE_COMPLETE: |
| 99 rv = DoReadResponseComplete(rv); |
| 100 break; |
| 101 case STATE_READ_DATA: |
| 102 rv = DoReadData(); |
| 103 break; |
| 104 case STATE_READ_DATA_COMPLETE: |
| 105 rv = DoReadDataComplete(rv); |
| 106 break; |
| 107 default: |
| 108 NOTREACHED() << "Bad state:" << state; |
| 109 rv = net::ERR_FAILED; |
| 110 break; |
| 111 } |
| 112 } while (rv != net::ERR_IO_PENDING && cache_state_ != STATE_NONE); |
| 113 |
| 114 if (rv != net::ERR_IO_PENDING) { |
| 115 DVLOG(1) << "CacheIO done."; |
| 116 DoCacheDone(rv); |
| 117 } |
| 118 } |
| 119 |
| 120 net::URLRequestContext* MalwareDetailsCacheCollector::GetURLRequestContext() { |
| 121 if (!request_context_getter_) { |
| 122 DVLOG(1) << "Missing request context getter"; |
| 123 return NULL; |
| 124 } |
| 125 return request_context_getter_->GetURLRequestContext(); |
| 126 } |
| 127 |
| 128 int MalwareDetailsCacheCollector::DoOpenCache() { |
| 129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 130 cache_state_ = STATE_OPEN_CACHE_COMPLETE; |
| 131 |
| 132 net::URLRequestContext* context = GetURLRequestContext(); |
| 133 if (!context) { |
| 134 LOG(ERROR) << "Could not get URLRequestContext"; |
| 135 cache_state_ = STATE_NONE; |
| 136 return net::ERR_FAILED; |
| 137 } |
| 138 |
| 139 if (!context->http_transaction_factory()) { |
| 140 DVLOG(1) << "Could not get HTTP transaction factory"; |
| 141 cache_state_ = STATE_NONE; |
| 142 return net::ERR_FAILED; |
| 143 } |
| 144 |
| 145 net::HttpCache* http_cache = |
| 146 context->http_transaction_factory()->GetCache(); |
| 147 if (!http_cache) { |
| 148 DVLOG(1) << "Could not get http cache"; |
| 149 cache_state_ = STATE_NONE; |
| 150 return net::ERR_FAILED; |
| 151 } |
| 152 |
| 153 return http_cache->GetBackend(&cache_, &cache_callback_); |
| 154 } |
| 155 |
| 156 int MalwareDetailsCacheCollector::DoOpenCacheComplete(int result) { |
| 157 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 158 cache_state_ = STATE_OPEN_ENTRY; |
| 159 |
| 160 if (result == net::ERR_FAILED || !cache_) { |
| 161 LOG(ERROR) << "Could not get disk cache"; |
| 162 cache_state_ = STATE_NONE; |
| 163 return net::ERR_FAILED; |
| 164 } |
| 165 |
| 166 return net::OK; |
| 167 } |
| 168 |
| 169 int MalwareDetailsCacheCollector::DoOpenEntry() { |
| 170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 171 cache_state_ = STATE_OPEN_ENTRY_COMPLETE; |
| 172 |
| 173 if (resources_it_ == resources_->end()) { |
| 174 cache_state_ = STATE_NONE; |
| 175 return net::OK; |
| 176 } |
| 177 |
| 178 entry_ = NULL; // Reset the entry. |
| 179 return cache_->OpenEntry(resources_it_->first, &entry_, &cache_callback_); |
| 180 } |
| 181 |
| 182 int MalwareDetailsCacheCollector::DoOpenEntryComplete(int rv) { |
| 183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 184 cache_state_ = STATE_READ_RESPONSE; |
| 185 return net::OK; |
| 186 } |
| 187 |
| 188 int MalwareDetailsCacheCollector::DoReadResponse() { |
| 189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 190 cache_state_ = STATE_READ_RESPONSE_COMPLETE; |
| 191 // entry_callback_->AddRef(); |
| 192 |
| 193 if (!entry_) { // No entry in the cache for this url. |
| 194 return net::OK; |
| 195 } |
| 196 |
| 197 // net::HttpCache::kResponseInfoIndex = 0. |
| 198 buf_len_ = entry_->GetDataSize(0); |
| 199 if (!buf_len_) { |
| 200 DVLOG(1) << "Empty response"; |
| 201 return buf_len_; |
| 202 } |
| 203 |
| 204 buf_ = new net::IOBuffer(buf_len_); |
| 205 return entry_->ReadData(0, 0, buf_, buf_len_, entry_callback_); |
| 206 } |
| 207 |
| 208 int MalwareDetailsCacheCollector::DoReadResponseComplete(int result) { |
| 209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 210 cache_state_ = STATE_READ_DATA; |
| 211 // entry_callback_->Release(); |
| 212 |
| 213 if (!entry_ || !result || result != buf_len_) { |
| 214 DVLOG(1) << "No entry"; |
| 215 } else { |
| 216 linked_ptr<ClientMalwareReportRequest::Resource> |
| 217 pb_resource = resources_it_->second; |
| 218 net::HttpResponseInfo response; |
| 219 bool truncated; |
| 220 if (net::HttpCache::ParseResponseInfo(buf_->data(), buf_len_, &response, |
| 221 &truncated)) { |
| 222 if (!truncated) { |
| 223 if (response.headers) { |
| 224 ClientMalwareReportRequest::HTTPResponse* pb_response = |
| 225 pb_resource->mutable_response(); |
| 226 pb_response->mutable_firstline()->set_code( |
| 227 response.headers->response_code()); |
| 228 void* iter = NULL; |
| 229 std::string name, value; |
| 230 while (response.headers->EnumerateHeaderLines(&iter, &name, &value)) { |
| 231 // Strip any Set-Cookie headers. |
| 232 if (LowerCaseEqualsASCII(name, "set-cookie")) { |
| 233 continue; |
| 234 } |
| 235 ClientMalwareReportRequest::HTTPHeader* pb_header = |
| 236 pb_response->add_headers(); |
| 237 pb_header->set_name(name); |
| 238 pb_header->set_value(value); |
| 239 } |
| 240 } else { |
| 241 DVLOG(1) << "Missing response headers."; |
| 242 } |
| 243 } else { |
| 244 DVLOG(1) << "Response truncated"; |
| 245 } |
| 246 } else { |
| 247 LOG(ERROR) << "Could not parse HTTP cache entry"; |
| 248 } |
| 249 } |
| 250 |
| 251 return net::OK; |
| 252 } |
| 253 |
| 254 int MalwareDetailsCacheCollector::DoReadData() { |
| 255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 256 cache_state_ = STATE_READ_DATA_COMPLETE; |
| 257 // entry_callback_->AddRef(); |
| 258 |
| 259 if (!entry_) { // No entry in the cache for this url. |
| 260 return net::OK; |
| 261 } |
| 262 |
| 263 // net::HttpCache::kResponseContentIndex = 1. |
| 264 buf_len_ = entry_->GetDataSize(1); |
| 265 if (!buf_len_) { |
| 266 DVLOG(1) << "Empty data"; |
| 267 return buf_len_; |
| 268 } |
| 269 |
| 270 buf_ = new net::IOBuffer(buf_len_); |
| 271 return entry_->ReadData(1, 0, buf_, buf_len_, entry_callback_); |
| 272 } |
| 273 |
| 274 int MalwareDetailsCacheCollector::DoReadDataComplete(int result) { |
| 275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 276 cache_state_ = STATE_OPEN_ENTRY; |
| 277 // entry_callback_->Release(); |
| 278 |
| 279 if (entry_ && result && result == buf_len_) { |
| 280 linked_ptr<ClientMalwareReportRequest::Resource> |
| 281 pb_resource = resources_it_->second; |
| 282 ClientMalwareReportRequest::HTTPResponse* pb_response = |
| 283 pb_resource->mutable_response(); |
| 284 if (buf_len_ <= kMaxBodySize) { // Only send small bodies for now. |
| 285 pb_response->set_body(buf_->data(), buf_len_); |
| 286 } |
| 287 pb_response->set_bodylength(buf_len_); |
| 288 MD5Digest digest; |
| 289 MD5Sum(buf_->data(), buf_len_, &digest); |
| 290 pb_response->set_bodydigest(MD5DigestToBase16(digest)); |
| 291 entry_->Close(); |
| 292 } |
| 293 |
| 294 ++resources_it_; // Advance to the next entry. |
| 295 return net::OK; |
| 296 } |
| 297 |
| 298 void MalwareDetailsCacheCollector::DoCacheDone(int result) { |
| 299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 300 callback_->Run(result); // Runs MalwareDetails::OnCacheCollectionReady. |
| 301 |
| 302 Release(); |
| 303 // We are now deleted. |
| 304 } |
| OLD | NEW |