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#include "chrome/browser/renderer_host/safe_browsing_resource_handler.h"
6
7#include "base/logging.h"
8#include "content/browser/renderer_host/global_request_id.h"
9#include "content/browser/renderer_host/resource_dispatcher_host.h"
10#include "content/browser/renderer_host/resource_message_filter.h"
11#include "content/common/resource_response.h"
12#include "net/base/io_buffer.h"
13#include "net/base/load_flags.h"
14#include "net/base/net_errors.h"
15#include "net/url_request/url_request.h"
16
17// Maximum time in milliseconds to wait for the safe browsing service to
18// verify a URL. After this amount of time the outstanding check will be
19// aborted, and the URL will be treated as if it were safe.
20static const int kCheckUrlTimeoutMs = 5000;
21
22// TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more
23//               unit test coverage.
24
25SafeBrowsingResourceHandler::SafeBrowsingResourceHandler(
26    ResourceHandler* handler,
27    int render_process_host_id,
28    int render_view_id,
29    ResourceType::Type resource_type,
30    SafeBrowsingService* safe_browsing,
31    ResourceDispatcherHost* resource_dispatcher_host)
32    : state_(STATE_NONE),
33      defer_state_(DEFERRED_NONE),
34      safe_browsing_result_(SafeBrowsingService::SAFE),
35      deferred_request_id_(-1),
36      next_handler_(handler),
37      render_process_host_id_(render_process_host_id),
38      render_view_id_(render_view_id),
39      safe_browsing_(safe_browsing),
40      rdh_(resource_dispatcher_host),
41      resource_type_(resource_type) {
42}
43
44SafeBrowsingResourceHandler::~SafeBrowsingResourceHandler() {
45}
46
47bool SafeBrowsingResourceHandler::OnUploadProgress(int request_id,
48                                                   uint64 position,
49                                                   uint64 size) {
50  return next_handler_->OnUploadProgress(request_id, position, size);
51}
52
53bool SafeBrowsingResourceHandler::OnRequestRedirected(
54    int request_id,
55    const GURL& new_url,
56    ResourceResponse* response,
57    bool* defer) {
58  CHECK(state_ == STATE_NONE);
59  CHECK(defer_state_ == DEFERRED_NONE);
60
61  // Save the redirect urls for possible malware detail reporting later.
62  redirect_urls_.push_back(new_url);
63
64  // We need to check the new URL before following the redirect.
65  if (CheckUrl(new_url)) {
66    return next_handler_->OnRequestRedirected(
67        request_id, new_url, response, defer);
68  }
69
70  // If the URL couldn't be verified synchronously, defer following the
71  // redirect until the SafeBrowsing check is complete. Store the redirect
72  // context so we can pass it on to other handlers once we have completed
73  // our check.
74  defer_state_ = DEFERRED_REDIRECT;
75  deferred_request_id_ = request_id;
76  deferred_url_ = new_url;
77  deferred_redirect_response_ = response;
78  *defer = true;
79
80  return true;
81}
82
83bool SafeBrowsingResourceHandler::OnResponseStarted(
84    int request_id, ResourceResponse* response) {
85  CHECK(state_ == STATE_NONE);
86  CHECK(defer_state_ == DEFERRED_NONE);
87  return next_handler_->OnResponseStarted(request_id, response);
88}
89
90void SafeBrowsingResourceHandler::OnCheckUrlTimeout() {
91  CHECK(state_ == STATE_CHECKING_URL);
92  CHECK(defer_state_ != DEFERRED_NONE);
93  safe_browsing_->CancelCheck(this);
94  OnBrowseUrlCheckResult(deferred_url_, SafeBrowsingService::SAFE);
95}
96
97bool SafeBrowsingResourceHandler::OnWillStart(int request_id,
98                                              const GURL& url,
99                                              bool* defer) {
100  // We need to check the new URL before starting the request.
101  if (CheckUrl(url))
102    return next_handler_->OnWillStart(request_id, url, defer);
103
104  // If the URL couldn't be verified synchronously, defer starting the
105  // request until the check has completed.
106  defer_state_ = DEFERRED_START;
107  deferred_request_id_ = request_id;
108  deferred_url_ = url;
109  *defer = true;
110
111  return true;
112}
113
114bool SafeBrowsingResourceHandler::OnWillRead(int request_id,
115                                             net::IOBuffer** buf, int* buf_size,
116                                             int min_size) {
117  CHECK(state_ == STATE_NONE);
118  CHECK(defer_state_ == DEFERRED_NONE);
119  return next_handler_->OnWillRead(request_id, buf, buf_size, min_size);
120}
121
122bool SafeBrowsingResourceHandler::OnReadCompleted(int request_id,
123                                                  int* bytes_read) {
124  CHECK(state_ == STATE_NONE);
125  CHECK(defer_state_ == DEFERRED_NONE);
126  return next_handler_->OnReadCompleted(request_id, bytes_read);
127}
128
129bool SafeBrowsingResourceHandler::OnResponseCompleted(
130    int request_id, const net::URLRequestStatus& status,
131    const std::string& security_info) {
132  Shutdown();
133  return next_handler_->OnResponseCompleted(request_id, status, security_info);
134}
135
136void SafeBrowsingResourceHandler::OnRequestClosed() {
137  Shutdown();
138  next_handler_->OnRequestClosed();
139}
140
141// SafeBrowsingService::Client implementation, called on the IO thread once
142// the URL has been classified.
143void SafeBrowsingResourceHandler::OnBrowseUrlCheckResult(
144    const GURL& url, SafeBrowsingService::UrlCheckResult result) {
145  CHECK(state_ == STATE_CHECKING_URL);
146  CHECK(defer_state_ != DEFERRED_NONE);
147  CHECK(url == deferred_url_) << "Was expecting: " << deferred_url_
148                              << " but got: " << url;
149
150  timer_.Stop();  // Cancel the timeout timer.
151  safe_browsing_result_ = result;
152  state_ = STATE_NONE;
153
154  if (result == SafeBrowsingService::SAFE) {
155    // Log how much time the safe browsing check cost us.
156    base::TimeDelta pause_delta;
157    pause_delta = base::TimeTicks::Now() - url_check_start_time_;
158    safe_browsing_->LogPauseDelay(pause_delta);
159
160    // Continue the request.
161    ResumeRequest();
162  } else {
163    const net::URLRequest* request = rdh_->GetURLRequest(
164        GlobalRequestID(render_process_host_id_, deferred_request_id_));
165    if (request->load_flags() & net::LOAD_PREFETCH) {
166      // Don't prefetch resources that fail safe browsing, disallow
167      // them.
168      rdh_->CancelRequest(render_process_host_id_, deferred_request_id_, false);
169    } else {
170      StartDisplayingBlockingPage(url, result);
171    }
172  }
173
174  Release();  // Balances the AddRef() in CheckingUrl().
175}
176
177void SafeBrowsingResourceHandler::StartDisplayingBlockingPage(
178    const GURL& url,
179    SafeBrowsingService::UrlCheckResult result) {
180  CHECK(state_ == STATE_NONE);
181  CHECK(defer_state_ != DEFERRED_NONE);
182  CHECK(deferred_request_id_ != -1);
183
184  state_ = STATE_DISPLAYING_BLOCKING_PAGE;
185  AddRef();  // Balanced in OnBlockingPageComplete().
186
187  // Grab the original url of this request as well.
188  GURL original_url;
189  net::URLRequest* request = rdh_->GetURLRequest(
190      GlobalRequestID(render_process_host_id_, deferred_request_id_));
191  if (request)
192    original_url = request->original_url();
193  else
194    original_url = url;
195
196  safe_browsing_->DisplayBlockingPage(
197      url, original_url, redirect_urls_, resource_type_,
198      result, this, render_process_host_id_, render_view_id_);
199}
200
201// SafeBrowsingService::Client implementation, called on the IO thread when
202// the user has decided to proceed with the current request, or go back.
203void SafeBrowsingResourceHandler::OnBlockingPageComplete(bool proceed) {
204  CHECK(state_ == STATE_DISPLAYING_BLOCKING_PAGE);
205  state_ = STATE_NONE;
206
207  if (proceed) {
208    safe_browsing_result_ = SafeBrowsingService::SAFE;
209    net::URLRequest* request = rdh_->GetURLRequest(
210        GlobalRequestID(render_process_host_id_, deferred_request_id_));
211
212    // The request could be canceled by renderer at this stage.
213    // As a result, click proceed will do nothing (crbug.com/76460).
214    if (request)
215      ResumeRequest();
216  } else {
217    rdh_->CancelRequest(render_process_host_id_, deferred_request_id_, false);
218  }
219
220  Release();  // Balances the AddRef() in StartDisplayingBlockingPage().
221}
222
223void SafeBrowsingResourceHandler::Shutdown() {
224  if (state_ == STATE_CHECKING_URL) {
225    timer_.Stop();
226    safe_browsing_->CancelCheck(this);
227    state_ = STATE_NONE;
228    // Balance the AddRef() from CheckUrl() which would ordinarily be
229    // balanced by OnUrlCheckResult().
230    Release();
231  }
232}
233
234bool SafeBrowsingResourceHandler::CheckUrl(const GURL& url) {
235  CHECK(state_ == STATE_NONE);
236  bool succeeded_synchronously = safe_browsing_->CheckBrowseUrl(url, this);
237  if (succeeded_synchronously) {
238    safe_browsing_result_ = SafeBrowsingService::SAFE;
239    safe_browsing_->LogPauseDelay(base::TimeDelta());  // No delay.
240    return true;
241  }
242
243  AddRef();  // Balanced in OnUrlCheckResult().
244  state_ = STATE_CHECKING_URL;
245
246  // Record the start time of the check.
247  url_check_start_time_ = base::TimeTicks::Now();
248
249  // Start a timer to abort the check if it takes too long.
250  timer_.Start(base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs),
251               this, &SafeBrowsingResourceHandler::OnCheckUrlTimeout);
252
253  return false;
254}
255
256void SafeBrowsingResourceHandler::ResumeRequest() {
257  CHECK(state_ == STATE_NONE);
258  CHECK(defer_state_ != DEFERRED_NONE);
259
260  // Resume whatever stage got paused by the safe browsing check.
261  switch (defer_state_) {
262    case DEFERRED_START:
263      ResumeStart();
264      break;
265    case DEFERRED_REDIRECT:
266      ResumeRedirect();
267      break;
268    case DEFERRED_NONE:
269      NOTREACHED();
270      break;
271  }
272}
273
274void SafeBrowsingResourceHandler::ResumeStart() {
275  CHECK(defer_state_ == DEFERRED_START);
276  CHECK(deferred_request_id_ != -1);
277  defer_state_ = DEFERRED_NONE;
278
279  // Retrieve the details for the paused OnWillStart().
280  int request_id = deferred_request_id_;
281  GURL url = deferred_url_;
282
283  ClearDeferredRequestInfo();
284
285  // Give the other resource handlers a chance to defer starting.
286  bool defer = false;
287  // TODO(eroman): the return value is being lost here. Should
288  // use it to cancel the request.
289  next_handler_->OnWillStart(request_id, url, &defer);
290  if (!defer)
291    rdh_->StartDeferredRequest(render_process_host_id_, request_id);
292}
293
294void SafeBrowsingResourceHandler::ResumeRedirect() {
295  CHECK(defer_state_ == DEFERRED_REDIRECT);
296  defer_state_ = DEFERRED_NONE;
297
298  // Retrieve the details for the paused OnReceivedRedirect().
299  int request_id = deferred_request_id_;
300  GURL redirect_url = deferred_url_;
301  scoped_refptr<ResourceResponse> redirect_response =
302      deferred_redirect_response_;
303
304  ClearDeferredRequestInfo();
305
306  // Give the other resource handlers a chance to handle the redirect.
307  bool defer = false;
308  // TODO(eroman): the return value is being lost here. Should
309  // use it to cancel the request.
310  next_handler_->OnRequestRedirected(request_id, redirect_url,
311                                     redirect_response, &defer);
312  if (!defer) {
313    rdh_->FollowDeferredRedirect(render_process_host_id_, request_id,
314                                 false, GURL());
315  }
316}
317
318void SafeBrowsingResourceHandler::ClearDeferredRequestInfo() {
319  deferred_request_id_ = -1;
320  deferred_url_ = GURL();
321  deferred_redirect_response_ = NULL;
322}
323