// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef DISCOVERY_MDNS_MDNS_PUBLISHER_H_ #define DISCOVERY_MDNS_MDNS_PUBLISHER_H_ #include #include #include #include #include "absl/types/optional.h" #include "discovery/mdns/mdns_records.h" #include "discovery/mdns/mdns_responder.h" #include "util/alarm.h" namespace openscreen { class TaskRunner; namespace discovery { struct Config; class MdnsProbeManager; class MdnsRandom; class MdnsSender; class MdnsQuerier; // This class is responsible for both tracking what records have been registered // to mDNS as well as publishing new mDNS records to the network. // When a new record is published, it will be announced 8 times, starting at an // interval of 1 second, with the interval doubling each successive // announcement. This same announcement process is followed when an existing // record is updated. When it is removed, a Goodbye message must be sent if the // record is unique. // // Prior to publishing a record, the domain name for this service instance must // be claimed using the ClaimExclusiveOwnership() function. This function probes // the network to determine whether the chosen name exists, modifying the // chosen name as described in RFC 6762 if a collision is found. // // NOTE: All MdnsPublisher instances must be run on the same task runner thread, // due to the shared announce + goodbye message queue. class MdnsPublisher : public MdnsResponder::RecordHandler { public: // |sender|, |ownership_manager|, and |task_runner| must all persist for the // duration of this object's lifetime MdnsPublisher(MdnsSender* sender, MdnsProbeManager* ownership_manager, TaskRunner* task_runner, ClockNowFunctionPtr now_function, const Config& config); ~MdnsPublisher() override; // Registers a new mDNS record for advertisement by this service. For A, AAAA, // SRV, and TXT records, the domain name must have already been claimed by the // ClaimExclusiveOwnership() method and for PTR records the name being pointed // to must have been claimed in the same fashion, but the domain name in the // top-level MdnsRecord entity does not. // NOTE: This call is only valid for |dns_type| values: // - DnsType::kA // - DnsType::kPTR // - DnsType::kTXT // - DnsType::kAAAA // - DnsType::kSRV // - DnsType::kANY Error RegisterRecord(const MdnsRecord& record); // Updates the existing record with name matching the name of the new record. // NOTE: This method is not valid for PTR records. Error UpdateRegisteredRecord(const MdnsRecord& old_record, const MdnsRecord& new_record); // Stops advertising the provided record. Error UnregisterRecord(const MdnsRecord& record); // Returns the total number of records currently registered; size_t GetRecordCount() const; OSP_DISALLOW_COPY_AND_ASSIGN(MdnsPublisher); private: // Class responsible for sending announcement and goodbye messages for // MdnsRecord instances when they are published, updated, or unpublished. The // announcement messages will be sent |target_announcement_attempts| times, // first at an interval of 1 second apart, and then with delay increasing by a // factor of 2 with each successive announcement. // NOTE: |publisher| must be the MdnsPublisher instance from which this // instance was created. class RecordAnnouncer { public: RecordAnnouncer(MdnsRecord record, MdnsPublisher* publisher, TaskRunner* task_runner, ClockNowFunctionPtr now_function, int max_announcement_attempts); RecordAnnouncer(const RecordAnnouncer& other) = delete; RecordAnnouncer(RecordAnnouncer&& other) noexcept = delete; ~RecordAnnouncer(); RecordAnnouncer& operator=(const RecordAnnouncer& other) = delete; RecordAnnouncer& operator=(RecordAnnouncer&& other) noexcept = delete; const MdnsRecord& record() const { return record_; } // Specifies whether goodbye messages should not be sent when this announcer // is destroyed. This should only be called as part of the 'Update' flow, // for records which should not send this message. void DisableGoodbyeMessageTransmission() { should_send_goodbye_message_ = false; } private: // Gets the delay required before the next announcement message is sent. Clock::duration GetNextAnnounceDelay(); // When announce + goodbye messages are ready to be sent, they are queued // up. Every 20ms, if there are any messages to send out, these records are // batched up and sent out. void QueueGoodbye(); void QueueAnnouncement(); MdnsPublisher* const publisher_; TaskRunner* const task_runner_; const ClockNowFunctionPtr now_function_; // Whether or not goodbye messages should be sent. bool should_send_goodbye_message_ = true; // Record to send. const MdnsRecord record_; // Alarm used to cancel future resend attempts if this object is deleted. Alarm alarm_; // Number of attempts at sending this record which have occurred so far. int attempts_ = 0; // Number of times to announce a newly published record. const int target_announcement_attempts_; }; using RecordAnnouncerPtr = std::unique_ptr; friend class MdnsPublisherTesting; // Creates a new published from the provided record. RecordAnnouncerPtr CreateAnnouncer(MdnsRecord record) { return std::make_unique(std::move(record), this, task_runner_, now_function_, max_announcement_attempts_); } // Removes the given record from the |records_| map. A goodbye record is only // sent for this removal if |should_announce_deletion| is true. Error RemoveRecord(const MdnsRecord& record, bool should_announce_deletion); // Returns whether the provided record has had its name claimed so far. bool IsRecordNameClaimed(const MdnsRecord& record) const; // Processes the |records_to_send_| queue, sending out the records together as // a single MdnsMessage. void ProcessRecordQueue(); // Adds a new record to the |records_to_send_| queue or ensures that the // record with lower ttl is present if it differs from an existing record by // only that one field. void QueueRecord(MdnsRecord record); // MdnsResponder::RecordHandler overrides. bool HasRecords(const DomainName& name, DnsType type, DnsClass clazz) override; std::vector GetRecords(const DomainName& name, DnsType type, DnsClass clazz) override; std::vector GetPtrRecords(DnsClass clazz) override; MdnsSender* const sender_; MdnsProbeManager* const ownership_manager_; TaskRunner* const task_runner_; ClockNowFunctionPtr now_function_; // Alarm to cancel batching of records when this class is destroyed, and // instead send them immediately. Variable is only set when it is in use. absl::optional batch_records_alarm_; // Number of times to announce a newly published record. const int max_announcement_attempts_; // The queue for announce and goodbye records to be sent periodically. std::vector records_to_send_; // Stores mDNS records that have been published. The keys here are domain // names for valid mDNS Records, and the values are the RecordAnnouncer // entities associated with all published MdnsRecords for the keyed domain. // These are responsible for publishing a specific MdnsRecord, announcing it // when its created and sending a goodbye record when it's deleted. std::map> records_; }; } // namespace discovery } // namespace openscreen #endif // DISCOVERY_MDNS_MDNS_PUBLISHER_H_