• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium Authors
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 "net/reporting/reporting_delivery_agent.h"
6 
7 #include <algorithm>
8 #include <map>
9 #include <set>
10 #include <string>
11 #include <utility>
12 #include <vector>
13 
14 #include "base/check.h"
15 #include "base/containers/contains.h"
16 #include "base/functional/bind.h"
17 #include "base/json/json_writer.h"
18 #include "base/memory/raw_ptr.h"
19 #include "base/metrics/histogram_functions.h"
20 #include "base/numerics/safe_conversions.h"
21 #include "base/time/tick_clock.h"
22 #include "base/timer/timer.h"
23 #include "base/values.h"
24 #include "net/base/isolation_info.h"
25 #include "net/base/network_anonymization_key.h"
26 #include "net/base/url_util.h"
27 #include "net/reporting/reporting_cache.h"
28 #include "net/reporting/reporting_cache_observer.h"
29 #include "net/reporting/reporting_context.h"
30 #include "net/reporting/reporting_delegate.h"
31 #include "net/reporting/reporting_endpoint_manager.h"
32 #include "net/reporting/reporting_report.h"
33 #include "net/reporting/reporting_uploader.h"
34 #include "url/gurl.h"
35 #include "url/origin.h"
36 
37 namespace net {
38 
39 namespace {
40 
41 using ReportList = std::vector<const ReportingReport*>;
42 using ReportingUploadHeaderType =
43     ReportingDeliveryAgent::ReportingUploadHeaderType;
44 
RecordReportingUploadHeaderType(ReportingUploadHeaderType header_type)45 void RecordReportingUploadHeaderType(ReportingUploadHeaderType header_type) {
46   base::UmaHistogramEnumeration("Net.Reporting.UploadHeaderType", header_type);
47 }
48 
SerializeReports(const ReportList & reports,base::TimeTicks now)49 std::string SerializeReports(const ReportList& reports, base::TimeTicks now) {
50   base::Value::List reports_value;
51 
52   for (const ReportingReport* report : reports) {
53     base::Value::Dict report_value;
54 
55     report_value.Set("age", base::saturated_cast<int>(
56                                 (now - report->queued).InMilliseconds()));
57     report_value.Set("type", report->type);
58     report_value.Set("url", report->url.spec());
59     report_value.Set("user_agent", report->user_agent);
60     report_value.Set("body", report->body.Clone());
61 
62     reports_value.Append(std::move(report_value));
63   }
64 
65   std::string json_out;
66   bool json_written = base::JSONWriter::Write(reports_value, &json_out);
67   DCHECK(json_written);
68   return json_out;
69 }
70 
CompareReportGroupKeys(const ReportingReport * lhs,const ReportingReport * rhs)71 bool CompareReportGroupKeys(const ReportingReport* lhs,
72                             const ReportingReport* rhs) {
73   return lhs->GetGroupKey() < rhs->GetGroupKey();
74 }
75 
76 // Each Delivery corresponds to one upload URLRequest.
77 class Delivery {
78  public:
79   // The target of a delivery. All reports uploaded together must share the
80   // same values for these parameters.
81   // Note that |origin| here (which matches the report's |origin|) is not
82   // necessarily the same as the |origin| of the ReportingEndpoint's group key
83   // (if the endpoint is configured to include subdomains). Reports with
84   // different group keys can be in the same delivery, as long as the NIK,
85   // report origin and reporting source are the same, and they all get assigned
86   // to the same endpoint URL.
87   // |isolation_info| is the IsolationInfo struct associated with the reporting
88   // endpoint, and is used to determine appropriate credentials for the upload.
89   // |network_anonymization_key| is the NIK from the ReportingEndpoint, which
90   // may have been cleared in the ReportingService if reports are not being
91   // partitioned by NIK. (This is why a separate parameter is used here, rather
92   // than simply using the computed NIK from |isolation_info|.)
93   struct Target {
Targetnet::__anon6ecaebbd0111::Delivery::Target94     Target(const IsolationInfo& isolation_info,
95            const NetworkAnonymizationKey& network_anonymization_key,
96            const url::Origin& origin,
97            const GURL& endpoint_url,
98            const absl::optional<base::UnguessableToken> reporting_source)
99         : isolation_info(isolation_info),
100           network_anonymization_key(network_anonymization_key),
101           origin(origin),
102           endpoint_url(endpoint_url),
103           reporting_source(reporting_source) {
104       DCHECK(network_anonymization_key.IsEmpty() ||
105              network_anonymization_key ==
106                  isolation_info.network_anonymization_key());
107     }
108 
109     ~Target() = default;
110 
operator <net::__anon6ecaebbd0111::Delivery::Target111     bool operator<(const Target& other) const {
112       // Note that sorting by NIK here is required for V0 reports; V1 reports
113       // should not need this (but it doesn't hurt). We can remove that as a
114       // comparison key when V0 reporting endpoints are removed.
115       return std::tie(network_anonymization_key, origin, endpoint_url,
116                       reporting_source) <
117              std::tie(other.network_anonymization_key, other.origin,
118                       other.endpoint_url, other.reporting_source);
119     }
120 
121     IsolationInfo isolation_info;
122     NetworkAnonymizationKey network_anonymization_key;
123     url::Origin origin;
124     GURL endpoint_url;
125     absl::optional<base::UnguessableToken> reporting_source;
126   };
127 
Delivery(const Target & target)128   explicit Delivery(const Target& target) : target_(target) {}
129 
130   ~Delivery() = default;
131 
132   // Add the reports in [reports_begin, reports_end) into this delivery.
133   // Modify the report counter for the |endpoint| to which this delivery is
134   // destined.
AddReports(const ReportingEndpoint & endpoint,const ReportList::const_iterator reports_begin,const ReportList::const_iterator reports_end)135   void AddReports(const ReportingEndpoint& endpoint,
136                   const ReportList::const_iterator reports_begin,
137                   const ReportList::const_iterator reports_end) {
138     DCHECK(reports_begin != reports_end);
139     DCHECK(endpoint.group_key.network_anonymization_key ==
140            network_anonymization_key());
141     DCHECK(IsSubdomainOf(target_.origin.host() /* subdomain */,
142                          endpoint.group_key.origin.host() /* superdomain */));
143     for (auto it = reports_begin; it != reports_end; ++it) {
144       DCHECK_EQ((*reports_begin)->GetGroupKey(), (*it)->GetGroupKey());
145       DCHECK((*it)->network_anonymization_key == network_anonymization_key());
146       DCHECK_EQ(url::Origin::Create((*it)->url), target_.origin);
147       DCHECK_EQ((*it)->group, endpoint.group_key.group_name);
148       // Report origin is equal to, or a subdomain of, the endpoint
149       // configuration's origin.
150       DCHECK(IsSubdomainOf((*it)->url.host_piece() /* subdomain */,
151                            endpoint.group_key.origin.host() /* superdomain */));
152     }
153 
154     reports_per_group_[endpoint.group_key] +=
155         std::distance(reports_begin, reports_end);
156     reports_.insert(reports_.end(), reports_begin, reports_end);
157   }
158 
159   // Records statistics for reports after an upload has completed.
160   // Either removes successfully delivered reports, or increments the failure
161   // counter if delivery was unsuccessful.
ProcessOutcome(ReportingCache * cache,bool success)162   void ProcessOutcome(ReportingCache* cache, bool success) {
163     for (const auto& group_name_and_count : reports_per_group_) {
164       cache->IncrementEndpointDeliveries(group_name_and_count.first,
165                                          target_.endpoint_url,
166                                          group_name_and_count.second, success);
167     }
168     if (success) {
169       ReportingUploadHeaderType upload_type =
170           target_.reporting_source.has_value()
171               ? ReportingUploadHeaderType::kReportingEndpoints
172               : ReportingUploadHeaderType::kReportTo;
173       for (size_t i = 0; i < reports_.size(); ++i) {
174         RecordReportingUploadHeaderType(upload_type);
175       }
176       cache->RemoveReports(reports_, /* delivery_success */ true);
177     } else {
178       cache->IncrementReportsAttempts(reports_);
179     }
180   }
181 
network_anonymization_key() const182   const NetworkAnonymizationKey& network_anonymization_key() const {
183     return target_.network_anonymization_key;
184   }
endpoint_url() const185   const GURL& endpoint_url() const { return target_.endpoint_url; }
reports() const186   const ReportList& reports() const { return reports_; }
187 
188  private:
189   const Target target_;
190   ReportList reports_;
191 
192   // Used to track statistics for each ReportingEndpoint.
193   // The endpoint is uniquely identified by the key in conjunction with
194   // |target_.endpoint_url|. See ProcessOutcome().
195   std::map<ReportingEndpointGroupKey, int> reports_per_group_;
196 };
197 
198 class ReportingDeliveryAgentImpl : public ReportingDeliveryAgent,
199                                    public ReportingCacheObserver {
200  public:
ReportingDeliveryAgentImpl(ReportingContext * context,const RandIntCallback & rand_callback)201   ReportingDeliveryAgentImpl(ReportingContext* context,
202                              const RandIntCallback& rand_callback)
203       : context_(context),
204         timer_(std::make_unique<base::OneShotTimer>()),
205         endpoint_manager_(
206             ReportingEndpointManager::Create(&context->policy(),
207                                              &context->tick_clock(),
208                                              context->delegate(),
209                                              context->cache(),
210                                              rand_callback)) {
211     context_->AddCacheObserver(this);
212   }
213 
214   ReportingDeliveryAgentImpl(const ReportingDeliveryAgentImpl&) = delete;
215   ReportingDeliveryAgentImpl& operator=(const ReportingDeliveryAgentImpl&) =
216       delete;
217 
218   // ReportingDeliveryAgent implementation:
219 
~ReportingDeliveryAgentImpl()220   ~ReportingDeliveryAgentImpl() override {
221     context_->RemoveCacheObserver(this);
222   }
223 
SetTimerForTesting(std::unique_ptr<base::OneShotTimer> timer)224   void SetTimerForTesting(std::unique_ptr<base::OneShotTimer> timer) override {
225     DCHECK(!timer_->IsRunning());
226     timer_ = std::move(timer);
227   }
228 
SendReportsForSource(base::UnguessableToken reporting_source)229   void SendReportsForSource(base::UnguessableToken reporting_source) override {
230     DCHECK(!reporting_source.is_empty());
231     ReportList reports =
232         cache()->GetReportsToDeliverForSource(reporting_source);
233     if (reports.empty())
234       return;
235     DoSendReports(std::move(reports));
236   }
237 
238   // ReportingCacheObserver implementation:
OnReportsUpdated()239   void OnReportsUpdated() override {
240     if (CacheHasReports() && !timer_->IsRunning()) {
241       SendReports();
242       StartTimer();
243     }
244   }
245 
246  private:
CacheHasReports()247   bool CacheHasReports() {
248     ReportList reports;
249     context_->cache()->GetReports(&reports);
250     return !reports.empty();
251   }
252 
StartTimer()253   void StartTimer() {
254     timer_->Start(FROM_HERE, policy().delivery_interval,
255                   base::BindOnce(&ReportingDeliveryAgentImpl::OnTimerFired,
256                                  base::Unretained(this)));
257   }
258 
OnTimerFired()259   void OnTimerFired() {
260     if (CacheHasReports()) {
261       SendReports();
262       StartTimer();
263     }
264   }
265 
SendReports()266   void SendReports() {
267     ReportList reports = cache()->GetReportsToDeliver();
268     if (reports.empty())
269       return;
270     DoSendReports(std::move(reports));
271   }
272 
DoSendReports(ReportList reports)273   void DoSendReports(ReportList reports) {
274     // First determine which origins we're allowed to upload reports about.
275     std::set<url::Origin> report_origins;
276     for (const ReportingReport* report : reports) {
277       report_origins.insert(url::Origin::Create(report->url));
278     }
279     delegate()->CanSendReports(
280         std::move(report_origins),
281         base::BindOnce(&ReportingDeliveryAgentImpl::OnSendPermissionsChecked,
282                        weak_factory_.GetWeakPtr(), std::move(reports)));
283   }
284 
OnSendPermissionsChecked(ReportList reports,std::set<url::Origin> allowed_report_origins)285   void OnSendPermissionsChecked(ReportList reports,
286                                 std::set<url::Origin> allowed_report_origins) {
287     DCHECK(!reports.empty());
288     std::map<Delivery::Target, std::unique_ptr<Delivery>> deliveries;
289 
290     // Sort by group key
291     std::sort(reports.begin(), reports.end(), &CompareReportGroupKeys);
292 
293     // Iterate over "buckets" of reports with the same group key.
294     for (auto bucket_it = reports.begin(); bucket_it != reports.end();) {
295       auto bucket_start = bucket_it;
296       // Set the iterator to the beginning of the next group bucket.
297       bucket_it = std::upper_bound(bucket_it, reports.end(), *bucket_it,
298                                    &CompareReportGroupKeys);
299 
300       // Skip this group if we don't have origin permissions for this origin.
301       const ReportingEndpointGroupKey& report_group_key =
302           (*bucket_start)->GetGroupKey();
303       if (!base::Contains(allowed_report_origins, report_group_key.origin))
304         continue;
305 
306       // Skip this group if there is already a pending upload for it.
307       // We don't allow multiple concurrent uploads for the same group.
308       if (base::Contains(pending_groups_, report_group_key))
309         continue;
310 
311       // Find an endpoint to deliver these reports to.
312       const ReportingEndpoint endpoint =
313           endpoint_manager_->FindEndpointForDelivery(report_group_key);
314       // TODO(chlily): Remove reports for which there are no valid delivery
315       // endpoints.
316       if (!endpoint)
317         continue;
318 
319       pending_groups_.insert(report_group_key);
320 
321       IsolationInfo isolation_info =
322           cache()->GetIsolationInfoForEndpoint(endpoint);
323 
324       // Add the reports to the appropriate delivery.
325       Delivery::Target target(isolation_info,
326                               report_group_key.network_anonymization_key,
327                               report_group_key.origin, endpoint.info.url,
328                               endpoint.group_key.reporting_source);
329       auto delivery_it = deliveries.find(target);
330       if (delivery_it == deliveries.end()) {
331         bool inserted;
332         auto new_delivery = std::make_unique<Delivery>(target);
333         std::tie(delivery_it, inserted) = deliveries.insert(
334             std::make_pair(std::move(target), std::move(new_delivery)));
335         DCHECK(inserted);
336       }
337       delivery_it->second->AddReports(endpoint, bucket_start, bucket_it);
338     }
339 
340     // Keep track of which of these reports we don't queue for delivery; we'll
341     // need to mark them as not-pending.
342     std::set<const ReportingReport*> undelivered_reports(reports.begin(),
343                                                          reports.end());
344 
345     // Start an upload for each delivery.
346     for (auto& target_and_delivery : deliveries) {
347       const Delivery::Target& target = target_and_delivery.first;
348       std::unique_ptr<Delivery>& delivery = target_and_delivery.second;
349 
350       int max_depth = 0;
351       for (const ReportingReport* report : delivery->reports()) {
352         undelivered_reports.erase(report);
353         max_depth = std::max(report->depth, max_depth);
354       }
355 
356       std::string upload_data =
357           SerializeReports(delivery->reports(), tick_clock().NowTicks());
358 
359       // TODO: Calculate actual max depth.
360       uploader()->StartUpload(
361           target.origin, target.endpoint_url, target.isolation_info,
362           upload_data, max_depth,
363           /*eligible_for_credentials=*/target.reporting_source.has_value(),
364           base::BindOnce(&ReportingDeliveryAgentImpl::OnUploadComplete,
365                          weak_factory_.GetWeakPtr(), std::move(delivery)));
366     }
367 
368     cache()->ClearReportsPending(
369         {undelivered_reports.begin(), undelivered_reports.end()});
370   }
371 
OnUploadComplete(std::unique_ptr<Delivery> delivery,ReportingUploader::Outcome outcome)372   void OnUploadComplete(std::unique_ptr<Delivery> delivery,
373                         ReportingUploader::Outcome outcome) {
374     bool success = outcome == ReportingUploader::Outcome::SUCCESS;
375     delivery->ProcessOutcome(cache(), success);
376 
377     endpoint_manager_->InformOfEndpointRequest(
378         delivery->network_anonymization_key(), delivery->endpoint_url(),
379         success);
380 
381     // TODO(chlily): This leaks information across NIKs. If the endpoint URL is
382     // configured for both NIK1 and NIK2, and it responds with a 410 on a NIK1
383     // connection, then the change in configuration will be detectable on a NIK2
384     // connection.
385     // TODO(rodneyding): Handle Remove endpoint for Reporting-Endpoints header.
386     if (outcome == ReportingUploader::Outcome::REMOVE_ENDPOINT)
387       cache()->RemoveEndpointsForUrl(delivery->endpoint_url());
388 
389     for (const ReportingReport* report : delivery->reports()) {
390       pending_groups_.erase(report->GetGroupKey());
391     }
392 
393     cache()->ClearReportsPending(delivery->reports());
394   }
395 
policy() const396   const ReportingPolicy& policy() const { return context_->policy(); }
tick_clock() const397   const base::TickClock& tick_clock() const { return context_->tick_clock(); }
delegate()398   ReportingDelegate* delegate() { return context_->delegate(); }
cache()399   ReportingCache* cache() { return context_->cache(); }
uploader()400   ReportingUploader* uploader() { return context_->uploader(); }
401 
402   raw_ptr<ReportingContext> context_;
403 
404   std::unique_ptr<base::OneShotTimer> timer_;
405 
406   // Tracks endpoint groups for which there is a pending delivery running.
407   std::set<ReportingEndpointGroupKey> pending_groups_;
408 
409   std::unique_ptr<ReportingEndpointManager> endpoint_manager_;
410 
411   base::WeakPtrFactory<ReportingDeliveryAgentImpl> weak_factory_{this};
412 };
413 
414 }  // namespace
415 
416 // static
Create(ReportingContext * context,const RandIntCallback & rand_callback)417 std::unique_ptr<ReportingDeliveryAgent> ReportingDeliveryAgent::Create(
418     ReportingContext* context,
419     const RandIntCallback& rand_callback) {
420   return std::make_unique<ReportingDeliveryAgentImpl>(context, rand_callback);
421 }
422 
423 ReportingDeliveryAgent::~ReportingDeliveryAgent() = default;
424 
425 }  // namespace net
426