xref: /aosp_15_r20/external/cronet/net/dns/dns_udp_tracker.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2020 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker #include "net/dns/dns_udp_tracker.h"
6*6777b538SAndroid Build Coastguard Worker 
7*6777b538SAndroid Build Coastguard Worker #include <utility>
8*6777b538SAndroid Build Coastguard Worker 
9*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_macros.h"
10*6777b538SAndroid Build Coastguard Worker #include "base/numerics/safe_conversions.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/ranges/algorithm.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/time/tick_clock.h"
13*6777b538SAndroid Build Coastguard Worker #include "net/base/net_errors.h"
14*6777b538SAndroid Build Coastguard Worker 
15*6777b538SAndroid Build Coastguard Worker namespace net {
16*6777b538SAndroid Build Coastguard Worker 
17*6777b538SAndroid Build Coastguard Worker namespace {
18*6777b538SAndroid Build Coastguard Worker // Used in UMA (DNS.UdpLowEntropyReason). Do not renumber or remove values.
19*6777b538SAndroid Build Coastguard Worker enum class LowEntropyReason {
20*6777b538SAndroid Build Coastguard Worker   kPortReuse = 0,
21*6777b538SAndroid Build Coastguard Worker   kRecognizedIdMismatch = 1,
22*6777b538SAndroid Build Coastguard Worker   kUnrecognizedIdMismatch = 2,
23*6777b538SAndroid Build Coastguard Worker   kSocketLimitExhaustion = 3,
24*6777b538SAndroid Build Coastguard Worker   kMaxValue = kSocketLimitExhaustion,
25*6777b538SAndroid Build Coastguard Worker };
26*6777b538SAndroid Build Coastguard Worker 
RecordLowEntropyUma(LowEntropyReason reason)27*6777b538SAndroid Build Coastguard Worker void RecordLowEntropyUma(LowEntropyReason reason) {
28*6777b538SAndroid Build Coastguard Worker   UMA_HISTOGRAM_ENUMERATION("Net.DNS.DnsTransaction.UDP.LowEntropyReason",
29*6777b538SAndroid Build Coastguard Worker                             reason);
30*6777b538SAndroid Build Coastguard Worker }
31*6777b538SAndroid Build Coastguard Worker 
32*6777b538SAndroid Build Coastguard Worker }  // namespace
33*6777b538SAndroid Build Coastguard Worker 
34*6777b538SAndroid Build Coastguard Worker // static
35*6777b538SAndroid Build Coastguard Worker constexpr base::TimeDelta DnsUdpTracker::kMaxAge;
36*6777b538SAndroid Build Coastguard Worker 
37*6777b538SAndroid Build Coastguard Worker // static
38*6777b538SAndroid Build Coastguard Worker constexpr size_t DnsUdpTracker::kMaxRecordedQueries;
39*6777b538SAndroid Build Coastguard Worker 
40*6777b538SAndroid Build Coastguard Worker // static
41*6777b538SAndroid Build Coastguard Worker constexpr base::TimeDelta DnsUdpTracker::kMaxRecognizedIdAge;
42*6777b538SAndroid Build Coastguard Worker 
43*6777b538SAndroid Build Coastguard Worker // static
44*6777b538SAndroid Build Coastguard Worker constexpr size_t DnsUdpTracker::kUnrecognizedIdMismatchThreshold;
45*6777b538SAndroid Build Coastguard Worker 
46*6777b538SAndroid Build Coastguard Worker // static
47*6777b538SAndroid Build Coastguard Worker constexpr size_t DnsUdpTracker::kRecognizedIdMismatchThreshold;
48*6777b538SAndroid Build Coastguard Worker 
49*6777b538SAndroid Build Coastguard Worker // static
50*6777b538SAndroid Build Coastguard Worker constexpr int DnsUdpTracker::kPortReuseThreshold;
51*6777b538SAndroid Build Coastguard Worker 
52*6777b538SAndroid Build Coastguard Worker struct DnsUdpTracker::QueryData {
53*6777b538SAndroid Build Coastguard Worker   uint16_t port;
54*6777b538SAndroid Build Coastguard Worker   uint16_t query_id;
55*6777b538SAndroid Build Coastguard Worker   base::TimeTicks time;
56*6777b538SAndroid Build Coastguard Worker };
57*6777b538SAndroid Build Coastguard Worker 
58*6777b538SAndroid Build Coastguard Worker DnsUdpTracker::DnsUdpTracker() = default;
59*6777b538SAndroid Build Coastguard Worker DnsUdpTracker::~DnsUdpTracker() = default;
60*6777b538SAndroid Build Coastguard Worker DnsUdpTracker::DnsUdpTracker(DnsUdpTracker&&) = default;
61*6777b538SAndroid Build Coastguard Worker DnsUdpTracker& DnsUdpTracker::operator=(DnsUdpTracker&&) = default;
62*6777b538SAndroid Build Coastguard Worker 
RecordQuery(uint16_t port,uint16_t query_id)63*6777b538SAndroid Build Coastguard Worker void DnsUdpTracker::RecordQuery(uint16_t port, uint16_t query_id) {
64*6777b538SAndroid Build Coastguard Worker   PurgeOldRecords();
65*6777b538SAndroid Build Coastguard Worker 
66*6777b538SAndroid Build Coastguard Worker   int reused_port_count = base::checked_cast<int>(
67*6777b538SAndroid Build Coastguard Worker       base::ranges::count(recent_queries_, port, &QueryData::port));
68*6777b538SAndroid Build Coastguard Worker 
69*6777b538SAndroid Build Coastguard Worker   if (reused_port_count >= kPortReuseThreshold && !low_entropy_) {
70*6777b538SAndroid Build Coastguard Worker     low_entropy_ = true;
71*6777b538SAndroid Build Coastguard Worker     RecordLowEntropyUma(LowEntropyReason::kPortReuse);
72*6777b538SAndroid Build Coastguard Worker   }
73*6777b538SAndroid Build Coastguard Worker 
74*6777b538SAndroid Build Coastguard Worker   SaveQuery({port, query_id, tick_clock_->NowTicks()});
75*6777b538SAndroid Build Coastguard Worker }
76*6777b538SAndroid Build Coastguard Worker 
RecordResponseId(uint16_t query_id,uint16_t response_id)77*6777b538SAndroid Build Coastguard Worker void DnsUdpTracker::RecordResponseId(uint16_t query_id, uint16_t response_id) {
78*6777b538SAndroid Build Coastguard Worker   PurgeOldRecords();
79*6777b538SAndroid Build Coastguard Worker 
80*6777b538SAndroid Build Coastguard Worker   if (query_id != response_id) {
81*6777b538SAndroid Build Coastguard Worker     SaveIdMismatch(response_id);
82*6777b538SAndroid Build Coastguard Worker   }
83*6777b538SAndroid Build Coastguard Worker }
84*6777b538SAndroid Build Coastguard Worker 
RecordConnectionError(int connection_error)85*6777b538SAndroid Build Coastguard Worker void DnsUdpTracker::RecordConnectionError(int connection_error) {
86*6777b538SAndroid Build Coastguard Worker   if (!low_entropy_ && connection_error == ERR_INSUFFICIENT_RESOURCES) {
87*6777b538SAndroid Build Coastguard Worker     // On UDP connection, this error signifies that the process is using an
88*6777b538SAndroid Build Coastguard Worker     // unreasonably large number of UDP sockets, potentially a deliberate
89*6777b538SAndroid Build Coastguard Worker     // attack to reduce DNS port entropy.
90*6777b538SAndroid Build Coastguard Worker     low_entropy_ = true;
91*6777b538SAndroid Build Coastguard Worker     RecordLowEntropyUma(LowEntropyReason::kSocketLimitExhaustion);
92*6777b538SAndroid Build Coastguard Worker   }
93*6777b538SAndroid Build Coastguard Worker }
94*6777b538SAndroid Build Coastguard Worker 
PurgeOldRecords()95*6777b538SAndroid Build Coastguard Worker void DnsUdpTracker::PurgeOldRecords() {
96*6777b538SAndroid Build Coastguard Worker   base::TimeTicks now = tick_clock_->NowTicks();
97*6777b538SAndroid Build Coastguard Worker 
98*6777b538SAndroid Build Coastguard Worker   while (!recent_queries_.empty() &&
99*6777b538SAndroid Build Coastguard Worker          (now - recent_queries_.front().time) > kMaxAge) {
100*6777b538SAndroid Build Coastguard Worker     recent_queries_.pop_front();
101*6777b538SAndroid Build Coastguard Worker   }
102*6777b538SAndroid Build Coastguard Worker   while (!recent_unrecognized_id_hits_.empty() &&
103*6777b538SAndroid Build Coastguard Worker          now - recent_unrecognized_id_hits_.front() > kMaxAge) {
104*6777b538SAndroid Build Coastguard Worker     recent_unrecognized_id_hits_.pop_front();
105*6777b538SAndroid Build Coastguard Worker   }
106*6777b538SAndroid Build Coastguard Worker   while (!recent_recognized_id_hits_.empty() &&
107*6777b538SAndroid Build Coastguard Worker          now - recent_recognized_id_hits_.front() > kMaxAge) {
108*6777b538SAndroid Build Coastguard Worker     recent_recognized_id_hits_.pop_front();
109*6777b538SAndroid Build Coastguard Worker   }
110*6777b538SAndroid Build Coastguard Worker }
111*6777b538SAndroid Build Coastguard Worker 
SaveQuery(QueryData query)112*6777b538SAndroid Build Coastguard Worker void DnsUdpTracker::SaveQuery(QueryData query) {
113*6777b538SAndroid Build Coastguard Worker   if (recent_queries_.size() == kMaxRecordedQueries)
114*6777b538SAndroid Build Coastguard Worker     recent_queries_.pop_front();
115*6777b538SAndroid Build Coastguard Worker   DCHECK_LT(recent_queries_.size(), kMaxRecordedQueries);
116*6777b538SAndroid Build Coastguard Worker 
117*6777b538SAndroid Build Coastguard Worker   DCHECK(recent_queries_.empty() || query.time >= recent_queries_.back().time);
118*6777b538SAndroid Build Coastguard Worker   recent_queries_.push_back(std::move(query));
119*6777b538SAndroid Build Coastguard Worker }
120*6777b538SAndroid Build Coastguard Worker 
SaveIdMismatch(uint16_t id)121*6777b538SAndroid Build Coastguard Worker void DnsUdpTracker::SaveIdMismatch(uint16_t id) {
122*6777b538SAndroid Build Coastguard Worker   // No need to track mismatches if already flagged for low entropy.
123*6777b538SAndroid Build Coastguard Worker   if (low_entropy_)
124*6777b538SAndroid Build Coastguard Worker     return;
125*6777b538SAndroid Build Coastguard Worker 
126*6777b538SAndroid Build Coastguard Worker   base::TimeTicks now = tick_clock_->NowTicks();
127*6777b538SAndroid Build Coastguard Worker   base::TimeTicks time_cutoff = now - kMaxRecognizedIdAge;
128*6777b538SAndroid Build Coastguard Worker   bool is_recognized =
129*6777b538SAndroid Build Coastguard Worker       base::ranges::any_of(recent_queries_, [&](const auto& recent_query) {
130*6777b538SAndroid Build Coastguard Worker         return recent_query.query_id == id && recent_query.time >= time_cutoff;
131*6777b538SAndroid Build Coastguard Worker       });
132*6777b538SAndroid Build Coastguard Worker 
133*6777b538SAndroid Build Coastguard Worker   if (is_recognized) {
134*6777b538SAndroid Build Coastguard Worker     DCHECK_LT(recent_recognized_id_hits_.size(),
135*6777b538SAndroid Build Coastguard Worker               kRecognizedIdMismatchThreshold);
136*6777b538SAndroid Build Coastguard Worker     if (recent_recognized_id_hits_.size() ==
137*6777b538SAndroid Build Coastguard Worker         kRecognizedIdMismatchThreshold - 1) {
138*6777b538SAndroid Build Coastguard Worker       low_entropy_ = true;
139*6777b538SAndroid Build Coastguard Worker       RecordLowEntropyUma(LowEntropyReason::kRecognizedIdMismatch);
140*6777b538SAndroid Build Coastguard Worker       return;
141*6777b538SAndroid Build Coastguard Worker     }
142*6777b538SAndroid Build Coastguard Worker 
143*6777b538SAndroid Build Coastguard Worker     DCHECK(recent_recognized_id_hits_.empty() ||
144*6777b538SAndroid Build Coastguard Worker            now >= recent_recognized_id_hits_.back());
145*6777b538SAndroid Build Coastguard Worker     recent_recognized_id_hits_.push_back(now);
146*6777b538SAndroid Build Coastguard Worker   } else {
147*6777b538SAndroid Build Coastguard Worker     DCHECK_LT(recent_unrecognized_id_hits_.size(),
148*6777b538SAndroid Build Coastguard Worker               kUnrecognizedIdMismatchThreshold);
149*6777b538SAndroid Build Coastguard Worker     if (recent_unrecognized_id_hits_.size() ==
150*6777b538SAndroid Build Coastguard Worker         kUnrecognizedIdMismatchThreshold - 1) {
151*6777b538SAndroid Build Coastguard Worker       low_entropy_ = true;
152*6777b538SAndroid Build Coastguard Worker       RecordLowEntropyUma(LowEntropyReason::kUnrecognizedIdMismatch);
153*6777b538SAndroid Build Coastguard Worker       return;
154*6777b538SAndroid Build Coastguard Worker     }
155*6777b538SAndroid Build Coastguard Worker 
156*6777b538SAndroid Build Coastguard Worker     DCHECK(recent_unrecognized_id_hits_.empty() ||
157*6777b538SAndroid Build Coastguard Worker            now >= recent_unrecognized_id_hits_.back());
158*6777b538SAndroid Build Coastguard Worker     recent_unrecognized_id_hits_.push_back(now);
159*6777b538SAndroid Build Coastguard Worker   }
160*6777b538SAndroid Build Coastguard Worker }
161*6777b538SAndroid Build Coastguard Worker 
162*6777b538SAndroid Build Coastguard Worker }  // namespace net
163