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