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.
4
5#include "chrome/browser/safe_browsing/ping_manager.h"
6
7#include "base/logging.h"
8#include "base/stl_util.h"
9#include "base/strings/string_util.h"
10#include "base/strings/stringprintf.h"
11#include "chrome/common/env_vars.h"
12#include "content/public/browser/browser_thread.h"
13#include "google_apis/google_api_keys.h"
14#include "net/base/escape.h"
15#include "net/base/load_flags.h"
16#include "net/url_request/url_fetcher.h"
17#include "net/url_request/url_request_context_getter.h"
18#include "net/url_request/url_request_status.h"
19
20using content::BrowserThread;
21
22// SafeBrowsingPingManager implementation ----------------------------------
23
24// static
25SafeBrowsingPingManager* SafeBrowsingPingManager::Create(
26    net::URLRequestContextGetter* request_context_getter,
27    const SafeBrowsingProtocolConfig& config) {
28  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
29  return new SafeBrowsingPingManager(request_context_getter, config);
30}
31
32SafeBrowsingPingManager::SafeBrowsingPingManager(
33    net::URLRequestContextGetter* request_context_getter,
34    const SafeBrowsingProtocolConfig& config)
35    : client_name_(config.client_name),
36      request_context_getter_(request_context_getter),
37      url_prefix_(config.url_prefix) {
38  DCHECK(!url_prefix_.empty());
39
40  version_ = SafeBrowsingProtocolManagerHelper::Version();
41}
42
43SafeBrowsingPingManager::~SafeBrowsingPingManager() {
44  // Delete in-progress safebrowsing reports (hits and details).
45  STLDeleteContainerPointers(safebrowsing_reports_.begin(),
46                             safebrowsing_reports_.end());
47}
48
49// net::URLFetcherDelegate implementation ----------------------------------
50
51// All SafeBrowsing request responses are handled here.
52void SafeBrowsingPingManager::OnURLFetchComplete(
53    const net::URLFetcher* source) {
54  Reports::iterator sit = safebrowsing_reports_.find(source);
55  DCHECK(sit != safebrowsing_reports_.end());
56  delete *sit;
57  safebrowsing_reports_.erase(sit);
58}
59
60// Sends a SafeBrowsing "hit" for UMA users.
61void SafeBrowsingPingManager::ReportSafeBrowsingHit(
62    const GURL& malicious_url,
63    const GURL& page_url,
64    const GURL& referrer_url,
65    bool is_subresource,
66    SBThreatType threat_type,
67    const std::string& post_data) {
68  GURL report_url = SafeBrowsingHitUrl(malicious_url, page_url,
69                                       referrer_url, is_subresource,
70                                       threat_type);
71  net::URLFetcher* report = net::URLFetcher::Create(
72      report_url,
73      post_data.empty() ? net::URLFetcher::GET : net::URLFetcher::POST,
74      this);
75  report->SetLoadFlags(net::LOAD_DISABLE_CACHE);
76  report->SetRequestContext(request_context_getter_.get());
77  if (!post_data.empty())
78    report->SetUploadData("text/plain", post_data);
79  safebrowsing_reports_.insert(report);
80  report->Start();
81}
82
83// Sends malware details for users who opt-in.
84void SafeBrowsingPingManager::ReportMalwareDetails(
85    const std::string& report) {
86  GURL report_url = MalwareDetailsUrl();
87  net::URLFetcher* fetcher = net::URLFetcher::Create(
88      report_url, net::URLFetcher::POST, this);
89  fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE);
90  fetcher->SetRequestContext(request_context_getter_.get());
91  fetcher->SetUploadData("application/octet-stream", report);
92  // Don't try too hard to send reports on failures.
93  fetcher->SetAutomaticallyRetryOn5xx(false);
94  fetcher->Start();
95  safebrowsing_reports_.insert(fetcher);
96}
97
98GURL SafeBrowsingPingManager::SafeBrowsingHitUrl(
99    const GURL& malicious_url, const GURL& page_url,
100    const GURL& referrer_url, bool is_subresource,
101    SBThreatType threat_type) const {
102  DCHECK(threat_type == SB_THREAT_TYPE_URL_MALWARE ||
103         threat_type == SB_THREAT_TYPE_URL_PHISHING ||
104         threat_type == SB_THREAT_TYPE_BINARY_MALWARE_URL ||
105         threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL ||
106         threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL);
107  std::string url = SafeBrowsingProtocolManagerHelper::ComposeUrl(
108      url_prefix_, "report", client_name_, version_, std::string());
109  std::string threat_list = "none";
110  switch (threat_type) {
111    case SB_THREAT_TYPE_URL_MALWARE:
112      threat_list = "malblhit";
113      break;
114    case SB_THREAT_TYPE_URL_PHISHING:
115      threat_list = "phishblhit";
116      break;
117    case SB_THREAT_TYPE_BINARY_MALWARE_URL:
118      threat_list = "binurlhit";
119      break;
120    case SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL:
121      threat_list = "phishcsdhit";
122      break;
123    case SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL:
124      threat_list = "malcsdhit";
125      break;
126    default:
127      NOTREACHED();
128  }
129  return GURL(base::StringPrintf("%s&evts=%s&evtd=%s&evtr=%s&evhr=%s&evtb=%d",
130      url.c_str(), threat_list.c_str(),
131      net::EscapeQueryParamValue(malicious_url.spec(), true).c_str(),
132      net::EscapeQueryParamValue(page_url.spec(), true).c_str(),
133      net::EscapeQueryParamValue(referrer_url.spec(), true).c_str(),
134      is_subresource));
135}
136
137GURL SafeBrowsingPingManager::MalwareDetailsUrl() const {
138  std::string url = base::StringPrintf(
139          "%s/clientreport/malware?client=%s&appver=%s&pver=1.0",
140          url_prefix_.c_str(),
141          client_name_.c_str(),
142          version_.c_str());
143  std::string api_key = google_apis::GetAPIKey();
144  if (!api_key.empty()) {
145    base::StringAppendF(&url, "&key=%s",
146                        net::EscapeQueryParamValue(api_key, true).c_str());
147  }
148  return GURL(url);
149}
150