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