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