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

Side by Side Diff: chrome/browser/safe_browsing/malware_details.cc

Issue 6611023: The optional Malware Details also collects HTTP cache information. See design... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 9 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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 // Implementation of the MalwareDetails class. 5 // Implementation of the MalwareDetails class.
6 6
7 #include "chrome/browser/safe_browsing/malware_details.h" 7 #include "chrome/browser/safe_browsing/malware_details.h"
8 8
9 #include "base/callback.h"
9 #include "base/lazy_instance.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"
10 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 14 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
11 #include "chrome/browser/safe_browsing/report.pb.h" 15 #include "chrome/browser/safe_browsing/report.pb.h"
16 #include "chrome/common/net/url_request_context_getter.h"
12 #include "chrome/common/render_messages.h" 17 #include "chrome/common/render_messages.h"
13 #include "chrome/common/render_messages_params.h" 18 #include "chrome/common/render_messages_params.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"
14 #include "content/browser/browser_thread.h" 24 #include "content/browser/browser_thread.h"
15 #include "content/browser/renderer_host/render_view_host.h" 25 #include "content/browser/renderer_host/render_view_host.h"
16 #include "content/browser/tab_contents/navigation_entry.h" 26 #include "content/browser/tab_contents/navigation_entry.h"
17 #include "content/browser/tab_contents/tab_contents.h" 27 #include "content/browser/tab_contents/tab_contents.h"
18 28
19 using safe_browsing::ClientMalwareReportRequest; 29 using safe_browsing::ClientMalwareReportRequest;
20 30
21 // Keep in sync with KMaxNodes in renderer/safe_browsing/malware_dom_details 31 // Keep in sync with KMaxNodes in renderer/safe_browsing/malware_dom_details
22 static const uint32 kMaxDomNodes = 500; 32 static const uint32 kMaxDomNodes = 500;
23 33
34 // Only send tiny files for now, a better strategy would use the size
35 // of the whole report and the user's bandwidth.
36 static const int kMaxBodySize = 1024;
37
24 // static 38 // static
25 MalwareDetailsFactory* MalwareDetails::factory_ = NULL; 39 MalwareDetailsFactory* MalwareDetails::factory_ = NULL;
26 40
27 // The default MalwareDetailsFactory. Global, made a singleton so we 41 // The default MalwareDetailsFactory. Global, made a singleton so we
28 // don't leak it. 42 // don't leak it.
29 class MalwareDetailsFactoryImpl 43 class MalwareDetailsFactoryImpl
30 : public MalwareDetailsFactory { 44 : public MalwareDetailsFactory {
31 public: 45 public:
32 MalwareDetails* CreateMalwareDetails( 46 MalwareDetails* CreateMalwareDetails(
33 TabContents* tab_contents, 47 TabContents* tab_contents,
(...skipping 23 matching lines...) Expand all
57 if (!factory_) 71 if (!factory_)
58 factory_ = g_malware_details_factory_impl.Pointer(); 72 factory_ = g_malware_details_factory_impl.Pointer();
59 return factory_->CreateMalwareDetails(tab_contents, resource); 73 return factory_->CreateMalwareDetails(tab_contents, resource);
60 } 74 }
61 75
62 // Create a MalwareDetails for the given tab. Runs in the UI thread. 76 // Create a MalwareDetails for the given tab. Runs in the UI thread.
63 MalwareDetails::MalwareDetails( 77 MalwareDetails::MalwareDetails(
64 TabContents* tab_contents, 78 TabContents* tab_contents,
65 const SafeBrowsingService::UnsafeResource resource) 79 const SafeBrowsingService::UnsafeResource resource)
66 : TabContentsObserver(tab_contents), 80 : TabContentsObserver(tab_contents),
67 resource_(resource) { 81 resource_(resource),
82 cache_state_(STATE_NONE),
83 ALLOW_THIS_IN_INITIALIZER_LIST(
84 cache_callback_(this, &MalwareDetails::CacheLoop)),
85 ALLOW_THIS_IN_INITIALIZER_LIST(
86 entry_callback_(
87 new net::CancelableCompletionCallback<MalwareDetails>(
88 this, &MalwareDetails::CacheLoop))) {
68 StartCollection(); 89 StartCollection();
69 } 90 }
70 91
71 MalwareDetails::~MalwareDetails() {} 92 MalwareDetails::~MalwareDetails() {}
72 93
73 bool MalwareDetails::OnMessageReceived(const IPC::Message& message) { 94 bool MalwareDetails::OnMessageReceived(const IPC::Message& message) {
74 bool handled = true; 95 bool handled = true;
75 IPC_BEGIN_MESSAGE_MAP(MalwareDetails, message) 96 IPC_BEGIN_MESSAGE_MAP(MalwareDetails, message)
76 IPC_MESSAGE_HANDLER(ViewHostMsg_MalwareDOMDetails, 97 IPC_MESSAGE_HANDLER(ViewHostMsg_MalwareDOMDetails,
77 OnReceivedMalwareDOMDetails) 98 OnReceivedMalwareDOMDetails)
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
185 } 206 }
186 207
187 // Add the referrer url. 208 // Add the referrer url.
188 if (nav_entry && !referrer_url.is_empty()) { 209 if (nav_entry && !referrer_url.is_empty()) {
189 AddUrl(referrer_url, GURL(), "", NULL); 210 AddUrl(referrer_url, GURL(), "", NULL);
190 } 211 }
191 212
192 // Get URLs of frames, scripts etc from the DOM. 213 // Get URLs of frames, scripts etc from the DOM.
193 // OnReceivedMalwareDOMDetails will be called when the renderer replies. 214 // OnReceivedMalwareDOMDetails will be called when the renderer replies.
194 tab_contents()->render_view_host()->GetMalwareDOMDetails(); 215 tab_contents()->render_view_host()->GetMalwareDOMDetails();
216
217 // DoOpenCache will need this later. Must be called on the UI thread, so
218 // we do it now.
219 request_context_getter_ = tab_contents()->profile()->GetRequestContext();
lzheng 2011/03/05 00:37:50 Might be better to put this in the constructor.
panayiotis 2011/03/29 18:14:55 Done.
195 } 220 }
196 221
197 // When the renderer is done, this is called. 222 // When the renderer is done, this is called.
198 void MalwareDetails::OnReceivedMalwareDOMDetails( 223 void MalwareDetails::OnReceivedMalwareDOMDetails(
199 const ViewHostMsg_MalwareDOMDetails_Params& params) { 224 const ViewHostMsg_MalwareDOMDetails_Params& params) {
200 // Schedule this in IO thread, so it doesn't conflict with future users 225 // Schedule this in IO thread, so it doesn't conflict with future users
201 // of our data structures (eg GetSerializedReport). 226 // of our data structures (eg GetSerializedReport).
202 BrowserThread::PostTask( 227 BrowserThread::PostTask(
203 BrowserThread::IO, FROM_HERE, 228 BrowserThread::IO, FROM_HERE,
204 NewRunnableMethod( 229 NewRunnableMethod(
205 this, &MalwareDetails::AddDOMDetails, params)); 230 this, &MalwareDetails::AddDOMDetails, params));
206 } 231 }
207 232
208 void MalwareDetails::AddDOMDetails( 233 void MalwareDetails::AddDOMDetails(
209 const ViewHostMsg_MalwareDOMDetails_Params& params) { 234 const ViewHostMsg_MalwareDOMDetails_Params& params) {
210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 235 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
236 DVLOG(1) << "Nodes from the DOM: " << params.nodes.size();
237 // If we have already started collecting data from the HTTP cache, don't
lzheng 2011/03/05 00:37:50 nit: add an empty line above.
panayiotis 2011/03/29 18:14:55 Done.
238 // modify our state.
239 if (cache_state_ != STATE_NONE)
240 return;
241
211 // Add the urls from the DOM to |resources_|. The renderer could be 242 // Add the urls from the DOM to |resources_|. The renderer could be
212 // sending bogus messages, so limit the number of nodes we accept. 243 // sending bogus messages, so limit the number of nodes we accept.
213 DVLOG(1) << "Nodes from the DOM: " << params.nodes.size();
214 for (uint32 i = 0; i < params.nodes.size() && i < kMaxDomNodes; ++i) { 244 for (uint32 i = 0; i < params.nodes.size() && i < kMaxDomNodes; ++i) {
215 ViewHostMsg_MalwareDOMDetails_Node node = params.nodes[i]; 245 ViewHostMsg_MalwareDOMDetails_Node node = params.nodes[i];
216 DVLOG(1) << node.url << ", " << node.tag_name << ", " << node.parent; 246 DVLOG(1) << node.url << ", " << node.tag_name << ", " << node.parent;
217 AddUrl(node.url, node.parent, node.tag_name, &(node.children)); 247 AddUrl(node.url, node.parent, node.tag_name, &(node.children));
218 } 248 }
219 } 249 }
220 250
221 // Called from the SB Service on the IO thread, after the user has 251 // Called from the SB Service on the IO thread, after the user has
222 // closed the tab, or clicked proceed or goback. Since the user needs 252 // closed the tab, or clicked proceed or goback. Since the user needs
223 // to take an action, we expect this to be called after 253 // to take an action, we expect this to be called after
224 // OnReceivedMalwareDOMDetails in most cases. If not, we don't include 254 // OnReceivedMalwareDOMDetails in most cases. If not, we don't include
225 // the DOM data in our report. 255 // the DOM data in our report.
226 const std::string* MalwareDetails::GetSerializedReport() { 256 void MalwareDetails::StartCacheCollection(MalwareDetailsCallback* callback) {
227 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
228 // The |report_| protocol buffer is now generated: We add all the 258
229 // urls in our |resources_| maps. 259 // Start the data collection from the HTTP cache.
260 DVLOG(1) << "Getting disk_cache data for all urls...";
261 resources_it_ = resources_.begin();
262 cache_state_ = STATE_OPEN_CACHE;
263 callback_ = callback;
264
265 // Post a task in the message loop, so the callers don't need to
266 // check if we call their callback immediately.
267 BrowserThread::PostTask(
268 BrowserThread::IO, FROM_HERE,
269 NewRunnableMethod(
270 this, &MalwareDetails::CacheLoop, net::OK));
271 }
272
273 // The IO thread loop. Called once the DOM details are callected,
274 // and then is passed to all the cache lookups as a callback.
275 void MalwareDetails::CacheLoop(int result) {
276 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
277 DCHECK(cache_state_ != STATE_NONE);
278
279 int rv = result;
280 do {
281 DVLOG(1) << "cache_state_:" << cache_state_;
282 CacheState state = cache_state_;
283 cache_state_ = STATE_NONE;
284 switch (state) {
285 case STATE_OPEN_CACHE:
286 rv = DoOpenCache();
287 break;
288 case STATE_OPEN_CACHE_COMPLETE:
289 rv = DoOpenCacheComplete(rv);
290 break;
291 case STATE_OPEN_ENTRY:
292 rv = DoOpenEntry();
293 break;
294 case STATE_OPEN_ENTRY_COMPLETE:
295 rv = DoOpenEntryComplete(rv);
296 break;
297 case STATE_READ_RESPONSE:
298 rv = DoReadResponse();
299 break;
300 case STATE_READ_RESPONSE_COMPLETE:
301 rv = DoReadResponseComplete(rv);
302 break;
303 case STATE_READ_DATA:
304 rv = DoReadData();
305 break;
306 case STATE_READ_DATA_COMPLETE:
307 rv = DoReadDataComplete(rv);
308 break;
309 default:
310 NOTREACHED() << "Bad state:" << state;
311 rv = net::ERR_FAILED;
312 break;
313 }
314 } while (rv != net::ERR_IO_PENDING && cache_state_ != STATE_NONE);
315
316 if (rv != net::ERR_IO_PENDING) {
317 DVLOG(1) << "CacheIO done.";
318 DoCacheDone(rv);
319 }
320 }
321
322 net::URLRequestContext* MalwareDetails::GetURLRequestContext() {
323 if (!request_context_getter_) {
324 DVLOG(1) << "Missing request context getter";
325 return NULL;
326 }
327 return request_context_getter_->GetURLRequestContext();
328 }
329
330 int MalwareDetails::DoOpenCache() {
331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
332 cache_state_ = STATE_OPEN_CACHE_COMPLETE;
333
334 net::URLRequestContext* context = GetURLRequestContext();
335 if (!context) {
336 LOG(ERROR) << "Could not get URLRequestContext";
337 cache_state_ = STATE_NONE;
338 return net::ERR_FAILED;
339 }
340
341 if (!context->http_transaction_factory()) {
342 DVLOG(1) << "Could not get HTTP transaction factory";
343 cache_state_ = STATE_NONE;
344 return net::ERR_FAILED;
345 }
346
347 net::HttpCache* http_cache =
348 context->http_transaction_factory()->GetCache();
349 if (!http_cache) {
350 DVLOG(1) << "Could not get http cache";
351 cache_state_ = STATE_NONE;
352 return net::ERR_FAILED;
353 }
354
355 return http_cache->GetBackend(&cache_, &cache_callback_);
356 }
357
358 int MalwareDetails::DoOpenCacheComplete(int result) {
359 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
360 cache_state_ = STATE_OPEN_ENTRY;
361
362 if (result == net::ERR_FAILED || !cache_) {
363 LOG(ERROR) << "Could not get disk cache";
364 cache_state_ = STATE_NONE;
365 return net::ERR_FAILED;
366 }
367
368 return net::OK;
369 }
370
371 int MalwareDetails::DoOpenEntry() {
372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
373 cache_state_ = STATE_OPEN_ENTRY_COMPLETE;
374
375 if (resources_it_ == resources_.end()) {
376 cache_state_ = STATE_NONE;
377 return net::OK;
378 }
379
380 entry_ = NULL; // Reset the entry.
381 return cache_->OpenEntry(resources_it_->first, &entry_, &cache_callback_);
382 }
383
384 int MalwareDetails::DoOpenEntryComplete(int rv) {
385 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
386 cache_state_ = STATE_READ_RESPONSE;
387 return net::OK;
388 }
389
390 int MalwareDetails::DoReadResponse() {
391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
392 cache_state_ = STATE_READ_RESPONSE_COMPLETE;
393 entry_callback_->AddRef();
lzheng 2011/03/05 00:37:50 Since MalwareDetails is refcounted, while waiting
panayiotis 2011/03/29 18:14:55 I managed to not refcount this callback. But the c
394
395 if (!entry_) { // No entry in the cache for this url.
396 return net::OK;
397 }
398
399 // net::HttpCache::kResponseInfoIndex = 0.
400 buf_len_ = entry_->GetDataSize(0);
401 if (!buf_len_) {
402 DVLOG(1) << "Empty response";
403 return buf_len_;
404 }
405
406 buf_ = new net::IOBuffer(buf_len_);
407 return entry_->ReadData(0, 0, buf_, buf_len_, entry_callback_);
408 }
409
410 int MalwareDetails::DoReadResponseComplete(int result) {
411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
412 cache_state_ = STATE_READ_DATA;
413 entry_callback_->Release();
414
415 if (!entry_ || !result || result != buf_len_) {
416 DVLOG(1) << "No entry";
417 } else {
418 linked_ptr<safe_browsing::ClientMalwareReportRequest::Resource>
419 pb_resource = resources_it_->second;
420 net::HttpResponseInfo response;
421 bool truncated;
422 if (net::HttpCache::ParseResponseInfo(buf_->data(), buf_len_, &response,
423 &truncated)) {
424 if (!truncated) {
425 if (response.headers) {
426 safe_browsing::ClientMalwareReportRequest::HTTPResponse* pb_response =
427 pb_resource->mutable_response();
428 pb_response->mutable_firstline()->set_code(
429 response.headers->response_code());
430 void* iter = NULL;
431 std::string name, value;
432 while (response.headers->EnumerateHeaderLines(&iter, &name, &value)) {
433 // Strip any Set-Cookie headers.
434 if (LowerCaseEqualsASCII(name, "set-cookie")) {
435 continue;
436 }
437 safe_browsing::ClientMalwareReportRequest::HTTPHeader* pb_header =
438 pb_response->add_headers();
439 pb_header->set_name(name);
440 pb_header->set_value(value);
441 }
442 } else {
443 DVLOG(1) << "Missing response headers.";
444 }
445 } else {
446 DVLOG(1) << "Response truncated";
447 }
448 } else {
449 LOG(ERROR) << "Could not parse HTTP cache entry";
450 }
451 }
452
453 return net::OK;
454 }
455
456 int MalwareDetails::DoReadData() {
457 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
458 cache_state_ = STATE_READ_DATA_COMPLETE;
459 entry_callback_->AddRef();
460
461 if (!entry_) { // No entry in the cache for this url.
462 return net::OK;
463 }
464
465 // net::HttpCache::kResponseContentIndex = 1.
466 buf_len_ = entry_->GetDataSize(1);
467 if (!buf_len_) {
468 DVLOG(1) << "Empty data";
469 return buf_len_;
470 }
471
472 buf_ = new net::IOBuffer(buf_len_);
473 return entry_->ReadData(1, 0, buf_, buf_len_, entry_callback_);
474 }
475
476 int MalwareDetails::DoReadDataComplete(int result) {
477 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
478 cache_state_ = STATE_OPEN_ENTRY;
479 entry_callback_->Release();
480
481 if (entry_ && result && result == buf_len_) {
482 linked_ptr<safe_browsing::ClientMalwareReportRequest::Resource>
483 pb_resource = resources_it_->second;
484 safe_browsing::ClientMalwareReportRequest::HTTPResponse* pb_response =
485 pb_resource->mutable_response();
486 if (buf_len_ <= kMaxBodySize) { // Only send small bodies for now.
487 pb_response->set_body(buf_->data(), buf_len_);
488 }
489 pb_response->set_bodylength(buf_len_);
490 MD5Digest digest;
491 MD5Sum(buf_->data(), buf_len_, &digest);
492 pb_response->set_bodydigest(MD5DigestToBase16(digest));
493 entry_->Close();
494 }
495
496 ++resources_it_; // Advance to the next entry.
497 return net::OK;
498 }
499
500 void MalwareDetails::DoCacheDone(int result) {
501 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
502 // Add all the urls in our |resources_| maps to the |report_| protocol buffer.
230 for (ResourceMap::const_iterator it = resources_.begin(); 503 for (ResourceMap::const_iterator it = resources_.begin();
231 it != resources_.end(); it++) { 504 it != resources_.end(); it++) {
232 ClientMalwareReportRequest::Resource* pb_resource = 505 ClientMalwareReportRequest::Resource* pb_resource =
233 report_->add_resources(); 506 report_->add_resources();
234 pb_resource->CopyFrom(*(it->second)); 507 pb_resource->CopyFrom(*(it->second));
235 } 508 }
236 509 report_->set_complete(result == net::OK);
510
511 callback_->Run(this);
512 }
513
514 const std::string* MalwareDetails::GetSerializedReport() {
515 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
237 scoped_ptr<std::string> request_data(new std::string()); 516 scoped_ptr<std::string> request_data(new std::string());
238 if (!report_->SerializeToString(request_data.get())) { 517 if (!report_->SerializeToString(request_data.get())) {
239 DLOG(ERROR) << "Unable to serialize the malware report."; 518 DLOG(ERROR) << "Unable to serialize the malware report.";
240 } 519 }
241 520
242 return request_data.release(); 521 return request_data.release();
243 } 522 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698