xref: /aosp_15_r20/external/openscreen/discovery/mdns/mdns_publisher.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_PUBLISHER_H_
6*3f982cf4SFabien Sanglard #define DISCOVERY_MDNS_MDNS_PUBLISHER_H_
7*3f982cf4SFabien Sanglard 
8*3f982cf4SFabien Sanglard #include <map>
9*3f982cf4SFabien Sanglard #include <memory>
10*3f982cf4SFabien Sanglard #include <utility>
11*3f982cf4SFabien Sanglard #include <vector>
12*3f982cf4SFabien Sanglard 
13*3f982cf4SFabien Sanglard #include "absl/types/optional.h"
14*3f982cf4SFabien Sanglard #include "discovery/mdns/mdns_records.h"
15*3f982cf4SFabien Sanglard #include "discovery/mdns/mdns_responder.h"
16*3f982cf4SFabien Sanglard #include "util/alarm.h"
17*3f982cf4SFabien Sanglard 
18*3f982cf4SFabien Sanglard namespace openscreen {
19*3f982cf4SFabien Sanglard 
20*3f982cf4SFabien Sanglard class TaskRunner;
21*3f982cf4SFabien Sanglard 
22*3f982cf4SFabien Sanglard namespace discovery {
23*3f982cf4SFabien Sanglard 
24*3f982cf4SFabien Sanglard struct Config;
25*3f982cf4SFabien Sanglard class MdnsProbeManager;
26*3f982cf4SFabien Sanglard class MdnsRandom;
27*3f982cf4SFabien Sanglard class MdnsSender;
28*3f982cf4SFabien Sanglard class MdnsQuerier;
29*3f982cf4SFabien Sanglard 
30*3f982cf4SFabien Sanglard // This class is responsible for both tracking what records have been registered
31*3f982cf4SFabien Sanglard // to mDNS as well as publishing new mDNS records to the network.
32*3f982cf4SFabien Sanglard // When a new record is published, it will be announced 8 times, starting at an
33*3f982cf4SFabien Sanglard // interval of 1 second, with the interval doubling each successive
34*3f982cf4SFabien Sanglard // announcement. This same announcement process is followed when an existing
35*3f982cf4SFabien Sanglard // record is updated. When it is removed, a Goodbye message must be sent if the
36*3f982cf4SFabien Sanglard // record is unique.
37*3f982cf4SFabien Sanglard //
38*3f982cf4SFabien Sanglard // Prior to publishing a record, the domain name for this service instance must
39*3f982cf4SFabien Sanglard // be claimed using the ClaimExclusiveOwnership() function. This function probes
40*3f982cf4SFabien Sanglard // the network to determine whether the chosen name exists, modifying the
41*3f982cf4SFabien Sanglard // chosen name as described in RFC 6762 if a collision is found.
42*3f982cf4SFabien Sanglard //
43*3f982cf4SFabien Sanglard // NOTE: All MdnsPublisher instances must be run on the same task runner thread,
44*3f982cf4SFabien Sanglard // due to the shared announce + goodbye message queue.
45*3f982cf4SFabien Sanglard class MdnsPublisher : public MdnsResponder::RecordHandler {
46*3f982cf4SFabien Sanglard  public:
47*3f982cf4SFabien Sanglard   // |sender|, |ownership_manager|, and  |task_runner| must all persist for the
48*3f982cf4SFabien Sanglard   // duration of this object's lifetime
49*3f982cf4SFabien Sanglard   MdnsPublisher(MdnsSender* sender,
50*3f982cf4SFabien Sanglard                 MdnsProbeManager* ownership_manager,
51*3f982cf4SFabien Sanglard                 TaskRunner* task_runner,
52*3f982cf4SFabien Sanglard                 ClockNowFunctionPtr now_function,
53*3f982cf4SFabien Sanglard                 const Config& config);
54*3f982cf4SFabien Sanglard   ~MdnsPublisher() override;
55*3f982cf4SFabien Sanglard 
56*3f982cf4SFabien Sanglard   // Registers a new mDNS record for advertisement by this service. For A, AAAA,
57*3f982cf4SFabien Sanglard   // SRV, and TXT records, the domain name must have already been claimed by the
58*3f982cf4SFabien Sanglard   // ClaimExclusiveOwnership() method and for PTR records the name being pointed
59*3f982cf4SFabien Sanglard   // to must have been claimed in the same fashion, but the domain name in the
60*3f982cf4SFabien Sanglard   // top-level MdnsRecord entity does not.
61*3f982cf4SFabien Sanglard   // NOTE: This call is only valid for |dns_type| values:
62*3f982cf4SFabien Sanglard   // - DnsType::kA
63*3f982cf4SFabien Sanglard   // - DnsType::kPTR
64*3f982cf4SFabien Sanglard   // - DnsType::kTXT
65*3f982cf4SFabien Sanglard   // - DnsType::kAAAA
66*3f982cf4SFabien Sanglard   // - DnsType::kSRV
67*3f982cf4SFabien Sanglard   // - DnsType::kANY
68*3f982cf4SFabien Sanglard   Error RegisterRecord(const MdnsRecord& record);
69*3f982cf4SFabien Sanglard 
70*3f982cf4SFabien Sanglard   // Updates the existing record with name matching the name of the new record.
71*3f982cf4SFabien Sanglard   // NOTE: This method is not valid for PTR records.
72*3f982cf4SFabien Sanglard   Error UpdateRegisteredRecord(const MdnsRecord& old_record,
73*3f982cf4SFabien Sanglard                                const MdnsRecord& new_record);
74*3f982cf4SFabien Sanglard 
75*3f982cf4SFabien Sanglard   // Stops advertising the provided record.
76*3f982cf4SFabien Sanglard   Error UnregisterRecord(const MdnsRecord& record);
77*3f982cf4SFabien Sanglard 
78*3f982cf4SFabien Sanglard   // Returns the total number of records currently registered;
79*3f982cf4SFabien Sanglard   size_t GetRecordCount() const;
80*3f982cf4SFabien Sanglard 
81*3f982cf4SFabien Sanglard   OSP_DISALLOW_COPY_AND_ASSIGN(MdnsPublisher);
82*3f982cf4SFabien Sanglard 
83*3f982cf4SFabien Sanglard  private:
84*3f982cf4SFabien Sanglard   // Class responsible for sending announcement and goodbye messages for
85*3f982cf4SFabien Sanglard   // MdnsRecord instances when they are published, updated, or unpublished. The
86*3f982cf4SFabien Sanglard   // announcement messages will be sent |target_announcement_attempts| times,
87*3f982cf4SFabien Sanglard   // first at an interval of 1 second apart, and then with delay increasing by a
88*3f982cf4SFabien Sanglard   // factor of 2 with each successive announcement.
89*3f982cf4SFabien Sanglard   // NOTE: |publisher| must be the MdnsPublisher instance from which this
90*3f982cf4SFabien Sanglard   // instance was created.
91*3f982cf4SFabien Sanglard   class RecordAnnouncer {
92*3f982cf4SFabien Sanglard    public:
93*3f982cf4SFabien Sanglard     RecordAnnouncer(MdnsRecord record,
94*3f982cf4SFabien Sanglard                     MdnsPublisher* publisher,
95*3f982cf4SFabien Sanglard                     TaskRunner* task_runner,
96*3f982cf4SFabien Sanglard                     ClockNowFunctionPtr now_function,
97*3f982cf4SFabien Sanglard                     int max_announcement_attempts);
98*3f982cf4SFabien Sanglard     RecordAnnouncer(const RecordAnnouncer& other) = delete;
99*3f982cf4SFabien Sanglard     RecordAnnouncer(RecordAnnouncer&& other) noexcept = delete;
100*3f982cf4SFabien Sanglard     ~RecordAnnouncer();
101*3f982cf4SFabien Sanglard 
102*3f982cf4SFabien Sanglard     RecordAnnouncer& operator=(const RecordAnnouncer& other) = delete;
103*3f982cf4SFabien Sanglard     RecordAnnouncer& operator=(RecordAnnouncer&& other) noexcept = delete;
104*3f982cf4SFabien Sanglard 
record()105*3f982cf4SFabien Sanglard     const MdnsRecord& record() const { return record_; }
106*3f982cf4SFabien Sanglard 
107*3f982cf4SFabien Sanglard     // Specifies whether goodbye messages should not be sent when this announcer
108*3f982cf4SFabien Sanglard     // is destroyed. This should only be called as part of the 'Update' flow,
109*3f982cf4SFabien Sanglard     // for records which should not send this message.
DisableGoodbyeMessageTransmission()110*3f982cf4SFabien Sanglard     void DisableGoodbyeMessageTransmission() {
111*3f982cf4SFabien Sanglard       should_send_goodbye_message_ = false;
112*3f982cf4SFabien Sanglard     }
113*3f982cf4SFabien Sanglard 
114*3f982cf4SFabien Sanglard    private:
115*3f982cf4SFabien Sanglard     // Gets the delay required before the next announcement message is sent.
116*3f982cf4SFabien Sanglard     Clock::duration GetNextAnnounceDelay();
117*3f982cf4SFabien Sanglard 
118*3f982cf4SFabien Sanglard     // When announce + goodbye messages are ready to be sent, they are queued
119*3f982cf4SFabien Sanglard     // up. Every 20ms, if there are any messages to send out, these records are
120*3f982cf4SFabien Sanglard     // batched up and sent out.
121*3f982cf4SFabien Sanglard     void QueueGoodbye();
122*3f982cf4SFabien Sanglard     void QueueAnnouncement();
123*3f982cf4SFabien Sanglard 
124*3f982cf4SFabien Sanglard     MdnsPublisher* const publisher_;
125*3f982cf4SFabien Sanglard     TaskRunner* const task_runner_;
126*3f982cf4SFabien Sanglard     const ClockNowFunctionPtr now_function_;
127*3f982cf4SFabien Sanglard 
128*3f982cf4SFabien Sanglard     // Whether or not goodbye messages should be sent.
129*3f982cf4SFabien Sanglard     bool should_send_goodbye_message_ = true;
130*3f982cf4SFabien Sanglard 
131*3f982cf4SFabien Sanglard     // Record to send.
132*3f982cf4SFabien Sanglard     const MdnsRecord record_;
133*3f982cf4SFabien Sanglard 
134*3f982cf4SFabien Sanglard     // Alarm used to cancel future resend attempts if this object is deleted.
135*3f982cf4SFabien Sanglard     Alarm alarm_;
136*3f982cf4SFabien Sanglard 
137*3f982cf4SFabien Sanglard     // Number of attempts at sending this record which have occurred so far.
138*3f982cf4SFabien Sanglard     int attempts_ = 0;
139*3f982cf4SFabien Sanglard 
140*3f982cf4SFabien Sanglard     // Number of times to announce a newly published record.
141*3f982cf4SFabien Sanglard     const int target_announcement_attempts_;
142*3f982cf4SFabien Sanglard   };
143*3f982cf4SFabien Sanglard 
144*3f982cf4SFabien Sanglard   using RecordAnnouncerPtr = std::unique_ptr<RecordAnnouncer>;
145*3f982cf4SFabien Sanglard 
146*3f982cf4SFabien Sanglard   friend class MdnsPublisherTesting;
147*3f982cf4SFabien Sanglard 
148*3f982cf4SFabien Sanglard   // Creates a new published from the provided record.
CreateAnnouncer(MdnsRecord record)149*3f982cf4SFabien Sanglard   RecordAnnouncerPtr CreateAnnouncer(MdnsRecord record) {
150*3f982cf4SFabien Sanglard     return std::make_unique<RecordAnnouncer>(std::move(record), this,
151*3f982cf4SFabien Sanglard                                              task_runner_, now_function_,
152*3f982cf4SFabien Sanglard                                              max_announcement_attempts_);
153*3f982cf4SFabien Sanglard   }
154*3f982cf4SFabien Sanglard 
155*3f982cf4SFabien Sanglard   // Removes the given record from the |records_| map. A goodbye record is only
156*3f982cf4SFabien Sanglard   // sent for this removal if |should_announce_deletion| is true.
157*3f982cf4SFabien Sanglard   Error RemoveRecord(const MdnsRecord& record, bool should_announce_deletion);
158*3f982cf4SFabien Sanglard 
159*3f982cf4SFabien Sanglard   // Returns whether the provided record has had its name claimed so far.
160*3f982cf4SFabien Sanglard   bool IsRecordNameClaimed(const MdnsRecord& record) const;
161*3f982cf4SFabien Sanglard 
162*3f982cf4SFabien Sanglard   // Processes the |records_to_send_| queue, sending out the records together as
163*3f982cf4SFabien Sanglard   // a single MdnsMessage.
164*3f982cf4SFabien Sanglard   void ProcessRecordQueue();
165*3f982cf4SFabien Sanglard 
166*3f982cf4SFabien Sanglard   // Adds a new record to the |records_to_send_| queue or ensures that the
167*3f982cf4SFabien Sanglard   // record with lower ttl is present if it differs from an existing record by
168*3f982cf4SFabien Sanglard   // only that one field.
169*3f982cf4SFabien Sanglard   void QueueRecord(MdnsRecord record);
170*3f982cf4SFabien Sanglard 
171*3f982cf4SFabien Sanglard   // MdnsResponder::RecordHandler overrides.
172*3f982cf4SFabien Sanglard   bool HasRecords(const DomainName& name,
173*3f982cf4SFabien Sanglard                   DnsType type,
174*3f982cf4SFabien Sanglard                   DnsClass clazz) override;
175*3f982cf4SFabien Sanglard   std::vector<MdnsRecord::ConstRef> GetRecords(const DomainName& name,
176*3f982cf4SFabien Sanglard                                                DnsType type,
177*3f982cf4SFabien Sanglard                                                DnsClass clazz) override;
178*3f982cf4SFabien Sanglard   std::vector<MdnsRecord::ConstRef> GetPtrRecords(DnsClass clazz) override;
179*3f982cf4SFabien Sanglard 
180*3f982cf4SFabien Sanglard   MdnsSender* const sender_;
181*3f982cf4SFabien Sanglard   MdnsProbeManager* const ownership_manager_;
182*3f982cf4SFabien Sanglard   TaskRunner* const task_runner_;
183*3f982cf4SFabien Sanglard   ClockNowFunctionPtr now_function_;
184*3f982cf4SFabien Sanglard 
185*3f982cf4SFabien Sanglard   // Alarm to cancel batching of records when this class is destroyed, and
186*3f982cf4SFabien Sanglard   // instead send them immediately. Variable is only set when it is in use.
187*3f982cf4SFabien Sanglard   absl::optional<Alarm> batch_records_alarm_;
188*3f982cf4SFabien Sanglard 
189*3f982cf4SFabien Sanglard   // Number of times to announce a newly published record.
190*3f982cf4SFabien Sanglard   const int max_announcement_attempts_;
191*3f982cf4SFabien Sanglard 
192*3f982cf4SFabien Sanglard   // The queue for announce and goodbye records to be sent periodically.
193*3f982cf4SFabien Sanglard   std::vector<MdnsRecord> records_to_send_;
194*3f982cf4SFabien Sanglard 
195*3f982cf4SFabien Sanglard   // Stores mDNS records that have been published. The keys here are domain
196*3f982cf4SFabien Sanglard   // names for valid mDNS Records, and the values are the RecordAnnouncer
197*3f982cf4SFabien Sanglard   // entities associated with all published MdnsRecords for the keyed domain.
198*3f982cf4SFabien Sanglard   // These are responsible for publishing a specific MdnsRecord, announcing it
199*3f982cf4SFabien Sanglard   // when its created and sending a goodbye record when it's deleted.
200*3f982cf4SFabien Sanglard   std::map<DomainName, std::vector<RecordAnnouncerPtr>> records_;
201*3f982cf4SFabien Sanglard };
202*3f982cf4SFabien Sanglard 
203*3f982cf4SFabien Sanglard }  // namespace discovery
204*3f982cf4SFabien Sanglard }  // namespace openscreen
205*3f982cf4SFabien Sanglard 
206*3f982cf4SFabien Sanglard #endif  // DISCOVERY_MDNS_MDNS_PUBLISHER_H_
207