blob: b90926977566ec9092fc19e0a54f903b51f98e21 [file] [log] [blame]
// 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 <map>
#include <memory>
#include <utility>
#include <vector>
#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<RecordAnnouncer>;
friend class MdnsPublisherTesting;
// Creates a new published from the provided record.
RecordAnnouncerPtr CreateAnnouncer(MdnsRecord record) {
return std::make_unique<RecordAnnouncer>(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<MdnsRecord::ConstRef> GetRecords(const DomainName& name,
DnsType type,
DnsClass clazz) override;
std::vector<MdnsRecord::ConstRef> 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<Alarm> 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<MdnsRecord> 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<DomainName, std::vector<RecordAnnouncerPtr>> records_;
};
} // namespace discovery
} // namespace openscreen
#endif // DISCOVERY_MDNS_MDNS_PUBLISHER_H_