xref: /aosp_15_r20/external/openscreen/discovery/mdns/mdns_trackers.h (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1*3f982cf4SFabien Sanglard // Copyright 2019 The Chromium Authors. All rights reserved.
2*3f982cf4SFabien Sanglard // Use of this source code is governed by a BSD-style license that can be
3*3f982cf4SFabien Sanglard // found in the LICENSE file.
4*3f982cf4SFabien Sanglard 
5*3f982cf4SFabien Sanglard #ifndef DISCOVERY_MDNS_MDNS_TRACKERS_H_
6*3f982cf4SFabien Sanglard #define DISCOVERY_MDNS_MDNS_TRACKERS_H_
7*3f982cf4SFabien Sanglard 
8*3f982cf4SFabien Sanglard #include <tuple>
9*3f982cf4SFabien Sanglard #include <unordered_map>
10*3f982cf4SFabien Sanglard #include <vector>
11*3f982cf4SFabien Sanglard 
12*3f982cf4SFabien Sanglard #include "absl/hash/hash.h"
13*3f982cf4SFabien Sanglard #include "discovery/mdns/mdns_records.h"
14*3f982cf4SFabien Sanglard #include "platform/api/task_runner.h"
15*3f982cf4SFabien Sanglard #include "platform/base/error.h"
16*3f982cf4SFabien Sanglard #include "platform/base/trivial_clock_traits.h"
17*3f982cf4SFabien Sanglard #include "util/alarm.h"
18*3f982cf4SFabien Sanglard 
19*3f982cf4SFabien Sanglard namespace openscreen {
20*3f982cf4SFabien Sanglard namespace discovery {
21*3f982cf4SFabien Sanglard 
22*3f982cf4SFabien Sanglard struct Config;
23*3f982cf4SFabien Sanglard class MdnsRandom;
24*3f982cf4SFabien Sanglard class MdnsRecord;
25*3f982cf4SFabien Sanglard class MdnsRecordChangedCallback;
26*3f982cf4SFabien Sanglard class MdnsSender;
27*3f982cf4SFabien Sanglard 
28*3f982cf4SFabien Sanglard // MdnsTracker is a base class for MdnsRecordTracker and MdnsQuestionTracker for
29*3f982cf4SFabien Sanglard // the purposes of common code sharing only.
30*3f982cf4SFabien Sanglard //
31*3f982cf4SFabien Sanglard // Instances of this class represent nodes of a bidirectional graph, such that
32*3f982cf4SFabien Sanglard // if node A is adjacent to node B, B is also adjacent to A. In this class, the
33*3f982cf4SFabien Sanglard // adjacent nodes are stored in adjacency list |associated_tracker_|, and
34*3f982cf4SFabien Sanglard // exposed methods to add and remove nodes from this list also modify the added
35*3f982cf4SFabien Sanglard // or removed node to remove this instance from its adjacency list.
36*3f982cf4SFabien Sanglard //
37*3f982cf4SFabien Sanglard // Because MdnsQuestionTracker::AddAssocaitedRecord() can only called on
38*3f982cf4SFabien Sanglard // MdnsRecordTracker objects and MdnsRecordTracker::AddAssociatedQuery() is
39*3f982cf4SFabien Sanglard // only called on MdnsQuestionTracker objects, this created graph is bipartite.
40*3f982cf4SFabien Sanglard // This means that MdnsRecordTracker objects are only adjacent to
41*3f982cf4SFabien Sanglard // MdnsQuestionTracker objects and the opposite.
42*3f982cf4SFabien Sanglard class MdnsTracker {
43*3f982cf4SFabien Sanglard  public:
44*3f982cf4SFabien Sanglard   enum class TrackerType { kRecordTracker, kQuestionTracker };
45*3f982cf4SFabien Sanglard 
46*3f982cf4SFabien Sanglard   // MdnsTracker does not own |sender|, |task_runner| and |random_delay|
47*3f982cf4SFabien Sanglard   // and expects that the lifetime of these objects exceeds the lifetime of
48*3f982cf4SFabien Sanglard   // MdnsTracker.
49*3f982cf4SFabien Sanglard   MdnsTracker(MdnsSender* sender,
50*3f982cf4SFabien Sanglard               TaskRunner* task_runner,
51*3f982cf4SFabien Sanglard               ClockNowFunctionPtr now_function,
52*3f982cf4SFabien Sanglard               MdnsRandom* random_delay,
53*3f982cf4SFabien Sanglard               TrackerType tracker_type);
54*3f982cf4SFabien Sanglard   MdnsTracker(const MdnsTracker& other) = delete;
55*3f982cf4SFabien Sanglard   MdnsTracker(MdnsTracker&& other) noexcept = delete;
56*3f982cf4SFabien Sanglard   MdnsTracker& operator=(const MdnsTracker& other) = delete;
57*3f982cf4SFabien Sanglard   MdnsTracker& operator=(MdnsTracker&& other) noexcept = delete;
58*3f982cf4SFabien Sanglard   virtual ~MdnsTracker();
59*3f982cf4SFabien Sanglard 
60*3f982cf4SFabien Sanglard   // Returns the record type represented by this tracker.
tracker_type()61*3f982cf4SFabien Sanglard   TrackerType tracker_type() const { return tracker_type_; }
62*3f982cf4SFabien Sanglard 
63*3f982cf4SFabien Sanglard   // Sends a query message via MdnsSender. Returns false if a follow up query
64*3f982cf4SFabien Sanglard   // should NOT be scheduled and true otherwise.
65*3f982cf4SFabien Sanglard   virtual bool SendQuery() const = 0;
66*3f982cf4SFabien Sanglard 
67*3f982cf4SFabien Sanglard   // Returns the records currently associated with this tracker.
68*3f982cf4SFabien Sanglard   virtual std::vector<MdnsRecord> GetRecords() const = 0;
69*3f982cf4SFabien Sanglard 
70*3f982cf4SFabien Sanglard  protected:
71*3f982cf4SFabien Sanglard   // Schedules a repeat query to be sent out.
72*3f982cf4SFabien Sanglard   virtual void ScheduleFollowUpQuery() = 0;
73*3f982cf4SFabien Sanglard 
74*3f982cf4SFabien Sanglard   // These methods create a bidirectional adjacency with another node in the
75*3f982cf4SFabien Sanglard   // graph.
76*3f982cf4SFabien Sanglard   bool AddAdjacentNode(const MdnsTracker* tracker) const;
77*3f982cf4SFabien Sanglard   bool RemoveAdjacentNode(const MdnsTracker* tracker) const;
78*3f982cf4SFabien Sanglard 
adjacent_nodes()79*3f982cf4SFabien Sanglard   const std::vector<const MdnsTracker*>& adjacent_nodes() const {
80*3f982cf4SFabien Sanglard     return adjacent_nodes_;
81*3f982cf4SFabien Sanglard   }
82*3f982cf4SFabien Sanglard 
83*3f982cf4SFabien Sanglard   MdnsSender* const sender_;
84*3f982cf4SFabien Sanglard   TaskRunner* const task_runner_;
85*3f982cf4SFabien Sanglard   const ClockNowFunctionPtr now_function_;
86*3f982cf4SFabien Sanglard   Alarm send_alarm_;
87*3f982cf4SFabien Sanglard   MdnsRandom* const random_delay_;
88*3f982cf4SFabien Sanglard   TrackerType tracker_type_;
89*3f982cf4SFabien Sanglard 
90*3f982cf4SFabien Sanglard  private:
91*3f982cf4SFabien Sanglard   // These methods are used to ensure the bidirectional-ness of this graph.
92*3f982cf4SFabien Sanglard   void AddReverseAdjacency(const MdnsTracker* tracker) const;
93*3f982cf4SFabien Sanglard   void RemovedReverseAdjacency(const MdnsTracker* tracker) const;
94*3f982cf4SFabien Sanglard 
95*3f982cf4SFabien Sanglard   // Adjacency list for this graph node.
96*3f982cf4SFabien Sanglard   mutable std::vector<const MdnsTracker*> adjacent_nodes_;
97*3f982cf4SFabien Sanglard };
98*3f982cf4SFabien Sanglard 
99*3f982cf4SFabien Sanglard class MdnsQuestionTracker;
100*3f982cf4SFabien Sanglard 
101*3f982cf4SFabien Sanglard // MdnsRecordTracker manages automatic resending of mDNS queries for
102*3f982cf4SFabien Sanglard // refreshing records as they reach their expiration time.
103*3f982cf4SFabien Sanglard class MdnsRecordTracker : public MdnsTracker {
104*3f982cf4SFabien Sanglard  public:
105*3f982cf4SFabien Sanglard   using RecordExpiredCallback =
106*3f982cf4SFabien Sanglard       std::function<void(const MdnsRecordTracker*, const MdnsRecord&)>;
107*3f982cf4SFabien Sanglard 
108*3f982cf4SFabien Sanglard   // NOTE: In the case that |record| is of type NSEC, |dns_type| is expected to
109*3f982cf4SFabien Sanglard   // differ from |record|'s type.
110*3f982cf4SFabien Sanglard   MdnsRecordTracker(MdnsRecord record,
111*3f982cf4SFabien Sanglard                     DnsType dns_type,
112*3f982cf4SFabien Sanglard                     MdnsSender* sender,
113*3f982cf4SFabien Sanglard                     TaskRunner* task_runner,
114*3f982cf4SFabien Sanglard                     ClockNowFunctionPtr now_function,
115*3f982cf4SFabien Sanglard                     MdnsRandom* random_delay,
116*3f982cf4SFabien Sanglard                     RecordExpiredCallback record_expired_callback);
117*3f982cf4SFabien Sanglard 
118*3f982cf4SFabien Sanglard   ~MdnsRecordTracker() override;
119*3f982cf4SFabien Sanglard 
120*3f982cf4SFabien Sanglard   // Possible outcomes from updating a tracked record.
121*3f982cf4SFabien Sanglard   enum class UpdateType {
122*3f982cf4SFabien Sanglard     kGoodbye,  // The record has a TTL of 0 and will expire.
123*3f982cf4SFabien Sanglard     kTTLOnly,  // The record updated its TTL only.
124*3f982cf4SFabien Sanglard     kRdata     // The record updated its RDATA.
125*3f982cf4SFabien Sanglard   };
126*3f982cf4SFabien Sanglard 
127*3f982cf4SFabien Sanglard   // Updates record tracker with the new record:
128*3f982cf4SFabien Sanglard   // 1. Resets TTL to the value specified in |new_record|.
129*3f982cf4SFabien Sanglard   // 2. Schedules expiration in case of a goodbye record.
130*3f982cf4SFabien Sanglard   // Returns Error::Code::kParameterInvalid if new_record is not a valid update
131*3f982cf4SFabien Sanglard   // for the current tracked record.
132*3f982cf4SFabien Sanglard   ErrorOr<UpdateType> Update(const MdnsRecord& new_record);
133*3f982cf4SFabien Sanglard 
134*3f982cf4SFabien Sanglard   // Adds or removed a question which this record answers.
135*3f982cf4SFabien Sanglard   bool AddAssociatedQuery(const MdnsQuestionTracker* question_tracker) const;
136*3f982cf4SFabien Sanglard   bool RemoveAssociatedQuery(const MdnsQuestionTracker* question_tracker) const;
137*3f982cf4SFabien Sanglard 
138*3f982cf4SFabien Sanglard   // Sets record to expire after 1 seconds as per RFC 6762
139*3f982cf4SFabien Sanglard   void ExpireSoon();
140*3f982cf4SFabien Sanglard 
141*3f982cf4SFabien Sanglard   // Expires the record now
142*3f982cf4SFabien Sanglard   void ExpireNow();
143*3f982cf4SFabien Sanglard 
144*3f982cf4SFabien Sanglard   // Returns true if half of the record's TTL has passed, and false otherwise.
145*3f982cf4SFabien Sanglard   // Half is used due to specifications in RFC 6762 section 7.1.
146*3f982cf4SFabien Sanglard   bool IsNearingExpiry() const;
147*3f982cf4SFabien Sanglard 
148*3f982cf4SFabien Sanglard   // Returns information about the stored record.
149*3f982cf4SFabien Sanglard   //
150*3f982cf4SFabien Sanglard   // NOTE: These methods are NOT all pass-through methods to |record_|.
151*3f982cf4SFabien Sanglard   // specifically, dns_type() returns the DNS Type associated with this record
152*3f982cf4SFabien Sanglard   // tracker, which may be different from the record type if |record_| is of
153*3f982cf4SFabien Sanglard   // type NSEC. To avoid this case, direct access to the underlying |record_|
154*3f982cf4SFabien Sanglard   // instance is not provided.
155*3f982cf4SFabien Sanglard   //
156*3f982cf4SFabien Sanglard   // In this case, creating an MdnsRecord with the below data will result in a
157*3f982cf4SFabien Sanglard   // runtime error due to DCHECKS and that Rdata's associated type will not
158*3f982cf4SFabien Sanglard   // match DnsType when |record_| is of type NSEC. Therefore, creating such
159*3f982cf4SFabien Sanglard   // records should be guarded by is_negative_response() checks.
name()160*3f982cf4SFabien Sanglard   const DomainName& name() const { return record_.name(); }
dns_type()161*3f982cf4SFabien Sanglard   DnsType dns_type() const { return dns_type_; }
dns_class()162*3f982cf4SFabien Sanglard   DnsClass dns_class() const { return record_.dns_class(); }
record_type()163*3f982cf4SFabien Sanglard   RecordType record_type() const { return record_.record_type(); }
ttl()164*3f982cf4SFabien Sanglard   std::chrono::seconds ttl() const { return record_.ttl(); }
rdata()165*3f982cf4SFabien Sanglard   const Rdata& rdata() const { return record_.rdata(); }
166*3f982cf4SFabien Sanglard 
is_negative_response()167*3f982cf4SFabien Sanglard   bool is_negative_response() const {
168*3f982cf4SFabien Sanglard     return record_.dns_type() == DnsType::kNSEC;
169*3f982cf4SFabien Sanglard   }
170*3f982cf4SFabien Sanglard 
171*3f982cf4SFabien Sanglard  private:
172*3f982cf4SFabien Sanglard   using MdnsTracker::tracker_type;
173*3f982cf4SFabien Sanglard 
174*3f982cf4SFabien Sanglard   // Needed to provide the test class access to the record stored in this
175*3f982cf4SFabien Sanglard   // tracker.
176*3f982cf4SFabien Sanglard   friend class MdnsTrackerTest;
177*3f982cf4SFabien Sanglard 
178*3f982cf4SFabien Sanglard   Clock::time_point GetNextSendTime();
179*3f982cf4SFabien Sanglard 
180*3f982cf4SFabien Sanglard   // MdnsTracker overrides.
181*3f982cf4SFabien Sanglard   bool SendQuery() const override;
182*3f982cf4SFabien Sanglard   void ScheduleFollowUpQuery() override;
183*3f982cf4SFabien Sanglard   std::vector<MdnsRecord> GetRecords() const override;
184*3f982cf4SFabien Sanglard 
185*3f982cf4SFabien Sanglard   // Stores MdnsRecord provided to Start method call.
186*3f982cf4SFabien Sanglard   MdnsRecord record_;
187*3f982cf4SFabien Sanglard 
188*3f982cf4SFabien Sanglard   // DnsType this record tracker represents. This may not match the type of
189*3f982cf4SFabien Sanglard   // |record_| if it is an NSEC record.
190*3f982cf4SFabien Sanglard   const DnsType dns_type_;
191*3f982cf4SFabien Sanglard 
192*3f982cf4SFabien Sanglard   // A point in time when the record was received and the tracking has started.
193*3f982cf4SFabien Sanglard   Clock::time_point start_time_;
194*3f982cf4SFabien Sanglard 
195*3f982cf4SFabien Sanglard   // Number of times record refresh has been attempted.
196*3f982cf4SFabien Sanglard   size_t attempt_count_ = 0;
197*3f982cf4SFabien Sanglard   RecordExpiredCallback record_expired_callback_;
198*3f982cf4SFabien Sanglard };
199*3f982cf4SFabien Sanglard 
200*3f982cf4SFabien Sanglard // MdnsQuestionTracker manages automatic resending of mDNS queries for
201*3f982cf4SFabien Sanglard // continuous monitoring with exponential back-off as described in RFC 6762.
202*3f982cf4SFabien Sanglard class MdnsQuestionTracker : public MdnsTracker {
203*3f982cf4SFabien Sanglard  public:
204*3f982cf4SFabien Sanglard   // Supported query types, per RFC 6762 section 5.
205*3f982cf4SFabien Sanglard   enum class QueryType { kOneShot, kContinuous };
206*3f982cf4SFabien Sanglard 
207*3f982cf4SFabien Sanglard   MdnsQuestionTracker(MdnsQuestion question,
208*3f982cf4SFabien Sanglard                       MdnsSender* sender,
209*3f982cf4SFabien Sanglard                       TaskRunner* task_runner,
210*3f982cf4SFabien Sanglard                       ClockNowFunctionPtr now_function,
211*3f982cf4SFabien Sanglard                       MdnsRandom* random_delay,
212*3f982cf4SFabien Sanglard                       const Config& config,
213*3f982cf4SFabien Sanglard                       QueryType query_type = QueryType::kContinuous);
214*3f982cf4SFabien Sanglard 
215*3f982cf4SFabien Sanglard   ~MdnsQuestionTracker() override;
216*3f982cf4SFabien Sanglard 
217*3f982cf4SFabien Sanglard   // Adds or removed an answer to a the question posed by this tracker.
218*3f982cf4SFabien Sanglard   bool AddAssociatedRecord(const MdnsRecordTracker* record_tracker) const;
219*3f982cf4SFabien Sanglard   bool RemoveAssociatedRecord(const MdnsRecordTracker* record_tracker) const;
220*3f982cf4SFabien Sanglard 
221*3f982cf4SFabien Sanglard   // Returns a reference to the tracked question.
question()222*3f982cf4SFabien Sanglard   const MdnsQuestion& question() const { return question_; }
223*3f982cf4SFabien Sanglard 
224*3f982cf4SFabien Sanglard  private:
225*3f982cf4SFabien Sanglard   using MdnsTracker::tracker_type;
226*3f982cf4SFabien Sanglard 
227*3f982cf4SFabien Sanglard   using RecordKey = std::tuple<DomainName, DnsType, DnsClass>;
228*3f982cf4SFabien Sanglard 
229*3f982cf4SFabien Sanglard   // Determines if all answers to this query have been received.
230*3f982cf4SFabien Sanglard   bool HasReceivedAllResponses();
231*3f982cf4SFabien Sanglard 
232*3f982cf4SFabien Sanglard   // MdnsTracker overrides.
233*3f982cf4SFabien Sanglard   bool SendQuery() const override;
234*3f982cf4SFabien Sanglard   void ScheduleFollowUpQuery() override;
235*3f982cf4SFabien Sanglard   std::vector<MdnsRecord> GetRecords() const override;
236*3f982cf4SFabien Sanglard 
237*3f982cf4SFabien Sanglard   // Stores MdnsQuestion provided to Start method call.
238*3f982cf4SFabien Sanglard   MdnsQuestion question_;
239*3f982cf4SFabien Sanglard 
240*3f982cf4SFabien Sanglard   // A delay between the currently scheduled and the next queries.
241*3f982cf4SFabien Sanglard   Clock::duration send_delay_;
242*3f982cf4SFabien Sanglard 
243*3f982cf4SFabien Sanglard   // Last time that this tracker's question was asked.
244*3f982cf4SFabien Sanglard   mutable TrivialClockTraits::time_point last_send_time_;
245*3f982cf4SFabien Sanglard 
246*3f982cf4SFabien Sanglard   // Specifies whether this query is intended to be a one-shot query, as defined
247*3f982cf4SFabien Sanglard   // in RFC 6762 section 5.1.
248*3f982cf4SFabien Sanglard   const QueryType query_type_;
249*3f982cf4SFabien Sanglard 
250*3f982cf4SFabien Sanglard   // Signifies the maximum number of times a record should be announced.
251*3f982cf4SFabien Sanglard   int maximum_announcement_count_;
252*3f982cf4SFabien Sanglard 
253*3f982cf4SFabien Sanglard   // Number of times this query has been announced.
254*3f982cf4SFabien Sanglard   int announcements_so_far_ = 0;
255*3f982cf4SFabien Sanglard };
256*3f982cf4SFabien Sanglard 
257*3f982cf4SFabien Sanglard }  // namespace discovery
258*3f982cf4SFabien Sanglard }  // namespace openscreen
259*3f982cf4SFabien Sanglard 
260*3f982cf4SFabien Sanglard #endif  // DISCOVERY_MDNS_MDNS_TRACKERS_H_
261