1 // Copyright 2013 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 #include "chrome/browser/safe_browsing/download_feedback.h" 6 7 #include "base/bind.h" 8 #include "base/files/file_util_proxy.h" 9 #include "base/metrics/histogram.h" 10 #include "base/task_runner.h" 11 #include "chrome/common/safe_browsing/csd.pb.h" 12 #include "net/base/net_errors.h" 13 14 namespace safe_browsing { 15 16 namespace { 17 18 // This enum is used by histograms. Do not change the ordering or remove items. 19 enum UploadResultType { 20 UPLOAD_SUCCESS, 21 UPLOAD_CANCELLED, 22 UPLOAD_METADATA_NET_ERROR, 23 UPLOAD_METADATA_RESPONSE_ERROR, 24 UPLOAD_FILE_NET_ERROR, 25 UPLOAD_FILE_RESPONSE_ERROR, 26 UPLOAD_COMPLETE_RESPONSE_ERROR, 27 // Memory space for histograms is determined by the max. 28 // ALWAYS ADD NEW VALUES BEFORE THIS ONE. 29 UPLOAD_RESULT_MAX 30 }; 31 32 // Handles the uploading of a single downloaded binary to the safebrowsing 33 // download feedback service. 34 class DownloadFeedbackImpl : public DownloadFeedback { 35 public: 36 DownloadFeedbackImpl(net::URLRequestContextGetter* request_context_getter, 37 base::TaskRunner* file_task_runner, 38 const base::FilePath& file_path, 39 const std::string& ping_request, 40 const std::string& ping_response); 41 virtual ~DownloadFeedbackImpl(); 42 43 virtual void Start(const base::Closure& finish_callback) OVERRIDE; 44 45 virtual const std::string& GetPingRequestForTesting() const OVERRIDE { 46 return ping_request_; 47 } 48 49 virtual const std::string& GetPingResponseForTesting() const OVERRIDE { 50 return ping_response_; 51 } 52 53 private: 54 // Callback for TwoPhaseUploader completion. Relays the result to the 55 // |finish_callback|. 56 void FinishedUpload(base::Closure finish_callback, 57 TwoPhaseUploader::State state, 58 int net_error, 59 int response_code, 60 const std::string& response); 61 62 void RecordUploadResult(UploadResultType result); 63 64 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; 65 scoped_refptr<base::TaskRunner> file_task_runner_; 66 const base::FilePath file_path_; 67 int64 file_size_; 68 69 // The safebrowsing request and response of checking that this binary is 70 // unsafe. 71 std::string ping_request_; 72 std::string ping_response_; 73 74 scoped_ptr<TwoPhaseUploader> uploader_; 75 76 DISALLOW_COPY_AND_ASSIGN(DownloadFeedbackImpl); 77 }; 78 79 DownloadFeedbackImpl::DownloadFeedbackImpl( 80 net::URLRequestContextGetter* request_context_getter, 81 base::TaskRunner* file_task_runner, 82 const base::FilePath& file_path, 83 const std::string& ping_request, 84 const std::string& ping_response) 85 : request_context_getter_(request_context_getter), 86 file_task_runner_(file_task_runner), 87 file_path_(file_path), 88 file_size_(-1), 89 ping_request_(ping_request), 90 ping_response_(ping_response) { 91 DVLOG(1) << "DownloadFeedback constructed " << this << " for " 92 << file_path.AsUTF8Unsafe(); 93 } 94 95 DownloadFeedbackImpl::~DownloadFeedbackImpl() { 96 DCHECK(CalledOnValidThread()); 97 DVLOG(1) << "DownloadFeedback destructed " << this; 98 99 if (uploader_) { 100 RecordUploadResult(UPLOAD_CANCELLED); 101 // Destroy the uploader before attempting to delete the file. 102 uploader_.reset(); 103 } 104 105 base::FileUtilProxy::DeleteFile(file_task_runner_.get(), 106 file_path_, 107 false, 108 base::FileUtilProxy::StatusCallback()); 109 } 110 111 void DownloadFeedbackImpl::Start(const base::Closure& finish_callback) { 112 DCHECK(CalledOnValidThread()); 113 DCHECK(!uploader_); 114 115 ClientDownloadReport report_metadata; 116 117 bool r = report_metadata.mutable_download_request()->ParseFromString( 118 ping_request_); 119 DCHECK(r); 120 r = report_metadata.mutable_download_response()->ParseFromString( 121 ping_response_); 122 DCHECK(r); 123 file_size_ = report_metadata.download_request().length(); 124 125 std::string metadata_string; 126 bool ok = report_metadata.SerializeToString(&metadata_string); 127 DCHECK(ok); 128 uploader_.reset( 129 TwoPhaseUploader::Create(request_context_getter_.get(), 130 file_task_runner_.get(), 131 GURL(kSbFeedbackURL), 132 metadata_string, 133 file_path_, 134 TwoPhaseUploader::ProgressCallback(), 135 base::Bind(&DownloadFeedbackImpl::FinishedUpload, 136 base::Unretained(this), 137 finish_callback))); 138 uploader_->Start(); 139 } 140 141 void DownloadFeedbackImpl::FinishedUpload(base::Closure finish_callback, 142 TwoPhaseUploader::State state, 143 int net_error, 144 int response_code, 145 const std::string& response_data) { 146 DCHECK(CalledOnValidThread()); 147 DVLOG(1) << __FUNCTION__ << " " << state << " rlen=" << response_data.size(); 148 149 switch (state) { 150 case TwoPhaseUploader::STATE_SUCCESS: { 151 ClientUploadResponse response; 152 if (!response.ParseFromString(response_data) || 153 response.status() != ClientUploadResponse::SUCCESS) 154 RecordUploadResult(UPLOAD_COMPLETE_RESPONSE_ERROR); 155 else 156 RecordUploadResult(UPLOAD_SUCCESS); 157 break; 158 } 159 case TwoPhaseUploader::UPLOAD_FILE: 160 if (net_error != net::OK) 161 RecordUploadResult(UPLOAD_FILE_NET_ERROR); 162 else 163 RecordUploadResult(UPLOAD_FILE_RESPONSE_ERROR); 164 break; 165 case TwoPhaseUploader::UPLOAD_METADATA: 166 if (net_error != net::OK) 167 RecordUploadResult(UPLOAD_METADATA_NET_ERROR); 168 else 169 RecordUploadResult(UPLOAD_METADATA_RESPONSE_ERROR); 170 break; 171 default: 172 NOTREACHED(); 173 } 174 175 uploader_.reset(); 176 177 finish_callback.Run(); 178 // We may be deleted here. 179 } 180 181 void DownloadFeedbackImpl::RecordUploadResult(UploadResultType result) { 182 if (result == UPLOAD_SUCCESS) 183 UMA_HISTOGRAM_CUSTOM_COUNTS( 184 "SBDownloadFeedback.SizeSuccess", file_size_, 1, kMaxUploadSize, 50); 185 else 186 UMA_HISTOGRAM_CUSTOM_COUNTS( 187 "SBDownloadFeedback.SizeFailure", file_size_, 1, kMaxUploadSize, 50); 188 UMA_HISTOGRAM_ENUMERATION( 189 "SBDownloadFeedback.UploadResult", result, UPLOAD_RESULT_MAX); 190 } 191 192 } // namespace 193 194 // static 195 const int64 DownloadFeedback::kMaxUploadSize = 50 * 1024 * 1024; 196 197 // static 198 const char DownloadFeedback::kSbFeedbackURL[] = 199 "https://safebrowsing.google.com/safebrowsing/uploads/chrome"; 200 201 // static 202 DownloadFeedbackFactory* DownloadFeedback::factory_ = NULL; 203 204 // static 205 DownloadFeedback* DownloadFeedback::Create( 206 net::URLRequestContextGetter* request_context_getter, 207 base::TaskRunner* file_task_runner, 208 const base::FilePath& file_path, 209 const std::string& ping_request, 210 const std::string& ping_response) { 211 if (!DownloadFeedback::factory_) 212 return new DownloadFeedbackImpl( 213 request_context_getter, file_task_runner, file_path, ping_request, 214 ping_response); 215 return DownloadFeedback::factory_->CreateDownloadFeedback( 216 request_context_getter, file_task_runner, file_path, ping_request, 217 ping_response); 218 } 219 220 } // namespace safe_browsing 221 222