1 // Copyright (c) 2012 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.
5 // Implementation of the MalwareDetails class.
7 #include "chrome/browser/safe_browsing/malware_details.h"
10 #include "base/lazy_instance.h"
12 #include "base/strings/string_util.h"
13 #include "chrome/browser/net/chrome_url_request_context_getter.h"
14 #include "chrome/browser/safe_browsing/malware_details_cache.h"
15 #include "chrome/browser/safe_browsing/report.pb.h"
16 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "net/base/host_port_pair.h"
19 #include "net/base/load_flags.h"
20 #include "net/base/net_errors.h"
21 #include "net/http/http_response_headers.h"
22 #include "net/url_request/url_fetcher.h"
23 #include "net/url_request/url_request_context_getter.h"
24 #include "net/url_request/url_request_status.h"
26 using content::BrowserThread
;
27 using safe_browsing::ClientMalwareReportRequest
;
29 // Only send small files for now, a better strategy would use the size
30 // of the whole report and the user's bandwidth.
31 static const uint32 kMaxBodySizeBytes
= 1024;
33 MalwareDetailsCacheCollector::MalwareDetailsCacheCollector()
34 : resources_(NULL
), result_(NULL
), has_started_(false) {}
36 void MalwareDetailsCacheCollector::StartCacheCollection(
37 net::URLRequestContextGetter
* request_context_getter
,
38 safe_browsing::ResourceMap
* resources
,
40 const base::Closure
& callback
) {
41 // Start the data collection from the HTTP cache. We use a URLFetcher
42 // and set the right flags so we only hit the cache.
43 DVLOG(1) << "Getting cache data for all urls...";
44 request_context_getter_
= request_context_getter
;
45 resources_
= resources
;
46 resources_it_
= resources_
->begin();
51 // Post a task in the message loop, so the callers don't need to
52 // check if we call their callback immediately.
53 BrowserThread::PostTask(
54 BrowserThread::IO
, FROM_HERE
,
55 base::Bind(&MalwareDetailsCacheCollector::OpenEntry
, this));
58 bool MalwareDetailsCacheCollector::HasStarted() {
59 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
63 MalwareDetailsCacheCollector::~MalwareDetailsCacheCollector() {}
65 // Fetch a URL and advance to the next one when done.
66 void MalwareDetailsCacheCollector::OpenEntry() {
67 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
68 DVLOG(1) << "OpenEntry";
70 if (resources_it_
== resources_
->end()) {
75 if (!request_context_getter_
.get()) {
76 DVLOG(1) << "Missing request context getter";
81 current_fetch_
= net::URLFetcher::Create(GURL(resources_it_
->first
),
82 net::URLFetcher::GET
, this);
83 current_fetch_
->SetRequestContext(request_context_getter_
.get());
84 // Only from cache, and don't save cookies.
85 current_fetch_
->SetLoadFlags(net::LOAD_ONLY_FROM_CACHE
|
86 net::LOAD_DO_NOT_SAVE_COOKIES
);
87 current_fetch_
->SetAutomaticallyRetryOn5xx(false); // No retries.
88 current_fetch_
->Start(); // OnURLFetchComplete will be called when done.
91 ClientMalwareReportRequest::Resource
* MalwareDetailsCacheCollector::GetResource(
93 safe_browsing::ResourceMap::iterator it
= resources_
->find(url
.spec());
94 if (it
!= resources_
->end()) {
95 return it
->second
.get();
100 void MalwareDetailsCacheCollector::OnURLFetchComplete(
101 const net::URLFetcher
* source
) {
102 DVLOG(1) << "OnUrlFetchComplete";
103 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
104 DCHECK(current_fetch_
.get());
105 if (source
->GetStatus().status() != net::URLRequestStatus::SUCCESS
&&
106 source
->GetStatus().error() == net::ERR_CACHE_MISS
) {
107 // Cache miss, skip this resource.
108 DVLOG(1) << "Cache miss for url: " << source
->GetURL();
113 if (source
->GetStatus().status() != net::URLRequestStatus::SUCCESS
) {
114 // Some other error occurred, e.g. the request could have been cancelled.
115 DVLOG(1) << "Unsuccessful fetch: " << source
->GetURL();
120 // Set the response headers and body to the right resource, which
121 // might not be the same as the one we asked for.
122 // For redirects, resources_it_->first != url.spec().
123 ClientMalwareReportRequest::Resource
* resource
=
124 GetResource(source
->GetURL());
126 DVLOG(1) << "Cannot find resource for url:" << source
->GetURL();
131 ReadResponse(resource
, source
);
133 source
->GetResponseAsString(&data
);
134 ReadData(resource
, data
);
138 void MalwareDetailsCacheCollector::ReadResponse(
139 ClientMalwareReportRequest::Resource
* pb_resource
,
140 const net::URLFetcher
* source
) {
141 DVLOG(1) << "ReadResponse";
142 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
143 net::HttpResponseHeaders
* headers
= source
->GetResponseHeaders();
145 DVLOG(1) << "Missing response headers.";
149 ClientMalwareReportRequest::HTTPResponse
* pb_response
=
150 pb_resource
->mutable_response();
151 pb_response
->mutable_firstline()->set_code(headers
->response_code());
153 std::string name
, value
;
154 while (headers
->EnumerateHeaderLines(&iter
, &name
, &value
)) {
155 ClientMalwareReportRequest::HTTPHeader
* pb_header
=
156 pb_response
->add_headers();
157 pb_header
->set_name(name
);
158 // Strip any Set-Cookie headers.
159 if (base::LowerCaseEqualsASCII(name
, "set-cookie")) {
160 pb_header
->set_value("");
162 pb_header
->set_value(value
);
166 if (!source
->WasFetchedViaProxy()) {
167 pb_response
->set_remote_ip(source
->GetSocketAddress().ToString());
171 void MalwareDetailsCacheCollector::ReadData(
172 ClientMalwareReportRequest::Resource
* pb_resource
,
173 const std::string
& data
) {
174 DVLOG(1) << "ReadData";
175 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
176 ClientMalwareReportRequest::HTTPResponse
* pb_response
=
177 pb_resource
->mutable_response();
178 if (data
.size() <= kMaxBodySizeBytes
) { // Only send small bodies for now.
179 pb_response
->set_body(data
);
181 pb_response
->set_bodylength(data
.size());
182 pb_response
->set_bodydigest(base::MD5String(data
));
185 void MalwareDetailsCacheCollector::AdvanceEntry() {
186 DVLOG(1) << "AdvanceEntry";
187 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
188 // Advance to the next resource.
190 current_fetch_
.reset(NULL
);
192 // Create a task so we don't take over the IO thread for too long.
193 BrowserThread::PostTask(
194 BrowserThread::IO
, FROM_HERE
,
195 base::Bind(&MalwareDetailsCacheCollector::OpenEntry
, this));
198 void MalwareDetailsCacheCollector::AllDone(bool success
) {
199 DVLOG(1) << "AllDone";
200 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
202 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, callback_
);