You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
261 lines
9.4 KiB
261 lines
9.4 KiB
// 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_TRACKERS_H_
|
|
#define DISCOVERY_MDNS_MDNS_TRACKERS_H_
|
|
|
|
#include <tuple>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include "absl/hash/hash.h"
|
|
#include "discovery/mdns/mdns_records.h"
|
|
#include "platform/api/task_runner.h"
|
|
#include "platform/base/error.h"
|
|
#include "platform/base/trivial_clock_traits.h"
|
|
#include "util/alarm.h"
|
|
|
|
namespace openscreen {
|
|
namespace discovery {
|
|
|
|
struct Config;
|
|
class MdnsRandom;
|
|
class MdnsRecord;
|
|
class MdnsRecordChangedCallback;
|
|
class MdnsSender;
|
|
|
|
// MdnsTracker is a base class for MdnsRecordTracker and MdnsQuestionTracker for
|
|
// the purposes of common code sharing only.
|
|
//
|
|
// Instances of this class represent nodes of a bidirectional graph, such that
|
|
// if node A is adjacent to node B, B is also adjacent to A. In this class, the
|
|
// adjacent nodes are stored in adjacency list |associated_tracker_|, and
|
|
// exposed methods to add and remove nodes from this list also modify the added
|
|
// or removed node to remove this instance from its adjacency list.
|
|
//
|
|
// Because MdnsQuestionTracker::AddAssocaitedRecord() can only called on
|
|
// MdnsRecordTracker objects and MdnsRecordTracker::AddAssociatedQuery() is
|
|
// only called on MdnsQuestionTracker objects, this created graph is bipartite.
|
|
// This means that MdnsRecordTracker objects are only adjacent to
|
|
// MdnsQuestionTracker objects and the opposite.
|
|
class MdnsTracker {
|
|
public:
|
|
enum class TrackerType { kRecordTracker, kQuestionTracker };
|
|
|
|
// MdnsTracker does not own |sender|, |task_runner| and |random_delay|
|
|
// and expects that the lifetime of these objects exceeds the lifetime of
|
|
// MdnsTracker.
|
|
MdnsTracker(MdnsSender* sender,
|
|
TaskRunner* task_runner,
|
|
ClockNowFunctionPtr now_function,
|
|
MdnsRandom* random_delay,
|
|
TrackerType tracker_type);
|
|
MdnsTracker(const MdnsTracker& other) = delete;
|
|
MdnsTracker(MdnsTracker&& other) noexcept = delete;
|
|
MdnsTracker& operator=(const MdnsTracker& other) = delete;
|
|
MdnsTracker& operator=(MdnsTracker&& other) noexcept = delete;
|
|
virtual ~MdnsTracker();
|
|
|
|
// Returns the record type represented by this tracker.
|
|
TrackerType tracker_type() const { return tracker_type_; }
|
|
|
|
// Sends a query message via MdnsSender. Returns false if a follow up query
|
|
// should NOT be scheduled and true otherwise.
|
|
virtual bool SendQuery() const = 0;
|
|
|
|
// Returns the records currently associated with this tracker.
|
|
virtual std::vector<MdnsRecord> GetRecords() const = 0;
|
|
|
|
protected:
|
|
// Schedules a repeat query to be sent out.
|
|
virtual void ScheduleFollowUpQuery() = 0;
|
|
|
|
// These methods create a bidirectional adjacency with another node in the
|
|
// graph.
|
|
bool AddAdjacentNode(const MdnsTracker* tracker) const;
|
|
bool RemoveAdjacentNode(const MdnsTracker* tracker) const;
|
|
|
|
const std::vector<const MdnsTracker*>& adjacent_nodes() const {
|
|
return adjacent_nodes_;
|
|
}
|
|
|
|
MdnsSender* const sender_;
|
|
TaskRunner* const task_runner_;
|
|
const ClockNowFunctionPtr now_function_;
|
|
Alarm send_alarm_;
|
|
MdnsRandom* const random_delay_;
|
|
TrackerType tracker_type_;
|
|
|
|
private:
|
|
// These methods are used to ensure the bidirectional-ness of this graph.
|
|
void AddReverseAdjacency(const MdnsTracker* tracker) const;
|
|
void RemovedReverseAdjacency(const MdnsTracker* tracker) const;
|
|
|
|
// Adjacency list for this graph node.
|
|
mutable std::vector<const MdnsTracker*> adjacent_nodes_;
|
|
};
|
|
|
|
class MdnsQuestionTracker;
|
|
|
|
// MdnsRecordTracker manages automatic resending of mDNS queries for
|
|
// refreshing records as they reach their expiration time.
|
|
class MdnsRecordTracker : public MdnsTracker {
|
|
public:
|
|
using RecordExpiredCallback =
|
|
std::function<void(const MdnsRecordTracker*, const MdnsRecord&)>;
|
|
|
|
// NOTE: In the case that |record| is of type NSEC, |dns_type| is expected to
|
|
// differ from |record|'s type.
|
|
MdnsRecordTracker(MdnsRecord record,
|
|
DnsType dns_type,
|
|
MdnsSender* sender,
|
|
TaskRunner* task_runner,
|
|
ClockNowFunctionPtr now_function,
|
|
MdnsRandom* random_delay,
|
|
RecordExpiredCallback record_expired_callback);
|
|
|
|
~MdnsRecordTracker() override;
|
|
|
|
// Possible outcomes from updating a tracked record.
|
|
enum class UpdateType {
|
|
kGoodbye, // The record has a TTL of 0 and will expire.
|
|
kTTLOnly, // The record updated its TTL only.
|
|
kRdata // The record updated its RDATA.
|
|
};
|
|
|
|
// Updates record tracker with the new record:
|
|
// 1. Resets TTL to the value specified in |new_record|.
|
|
// 2. Schedules expiration in case of a goodbye record.
|
|
// Returns Error::Code::kParameterInvalid if new_record is not a valid update
|
|
// for the current tracked record.
|
|
ErrorOr<UpdateType> Update(const MdnsRecord& new_record);
|
|
|
|
// Adds or removed a question which this record answers.
|
|
bool AddAssociatedQuery(const MdnsQuestionTracker* question_tracker) const;
|
|
bool RemoveAssociatedQuery(const MdnsQuestionTracker* question_tracker) const;
|
|
|
|
// Sets record to expire after 1 seconds as per RFC 6762
|
|
void ExpireSoon();
|
|
|
|
// Expires the record now
|
|
void ExpireNow();
|
|
|
|
// Returns true if half of the record's TTL has passed, and false otherwise.
|
|
// Half is used due to specifications in RFC 6762 section 7.1.
|
|
bool IsNearingExpiry() const;
|
|
|
|
// Returns information about the stored record.
|
|
//
|
|
// NOTE: These methods are NOT all pass-through methods to |record_|.
|
|
// specifically, dns_type() returns the DNS Type associated with this record
|
|
// tracker, which may be different from the record type if |record_| is of
|
|
// type NSEC. To avoid this case, direct access to the underlying |record_|
|
|
// instance is not provided.
|
|
//
|
|
// In this case, creating an MdnsRecord with the below data will result in a
|
|
// runtime error due to DCHECKS and that Rdata's associated type will not
|
|
// match DnsType when |record_| is of type NSEC. Therefore, creating such
|
|
// records should be guarded by is_negative_response() checks.
|
|
const DomainName& name() const { return record_.name(); }
|
|
DnsType dns_type() const { return dns_type_; }
|
|
DnsClass dns_class() const { return record_.dns_class(); }
|
|
RecordType record_type() const { return record_.record_type(); }
|
|
std::chrono::seconds ttl() const { return record_.ttl(); }
|
|
const Rdata& rdata() const { return record_.rdata(); }
|
|
|
|
bool is_negative_response() const {
|
|
return record_.dns_type() == DnsType::kNSEC;
|
|
}
|
|
|
|
private:
|
|
using MdnsTracker::tracker_type;
|
|
|
|
// Needed to provide the test class access to the record stored in this
|
|
// tracker.
|
|
friend class MdnsTrackerTest;
|
|
|
|
Clock::time_point GetNextSendTime();
|
|
|
|
// MdnsTracker overrides.
|
|
bool SendQuery() const override;
|
|
void ScheduleFollowUpQuery() override;
|
|
std::vector<MdnsRecord> GetRecords() const override;
|
|
|
|
// Stores MdnsRecord provided to Start method call.
|
|
MdnsRecord record_;
|
|
|
|
// DnsType this record tracker represents. This may not match the type of
|
|
// |record_| if it is an NSEC record.
|
|
const DnsType dns_type_;
|
|
|
|
// A point in time when the record was received and the tracking has started.
|
|
Clock::time_point start_time_;
|
|
|
|
// Number of times record refresh has been attempted.
|
|
size_t attempt_count_ = 0;
|
|
RecordExpiredCallback record_expired_callback_;
|
|
};
|
|
|
|
// MdnsQuestionTracker manages automatic resending of mDNS queries for
|
|
// continuous monitoring with exponential back-off as described in RFC 6762.
|
|
class MdnsQuestionTracker : public MdnsTracker {
|
|
public:
|
|
// Supported query types, per RFC 6762 section 5.
|
|
enum class QueryType { kOneShot, kContinuous };
|
|
|
|
MdnsQuestionTracker(MdnsQuestion question,
|
|
MdnsSender* sender,
|
|
TaskRunner* task_runner,
|
|
ClockNowFunctionPtr now_function,
|
|
MdnsRandom* random_delay,
|
|
const Config& config,
|
|
QueryType query_type = QueryType::kContinuous);
|
|
|
|
~MdnsQuestionTracker() override;
|
|
|
|
// Adds or removed an answer to a the question posed by this tracker.
|
|
bool AddAssociatedRecord(const MdnsRecordTracker* record_tracker) const;
|
|
bool RemoveAssociatedRecord(const MdnsRecordTracker* record_tracker) const;
|
|
|
|
// Returns a reference to the tracked question.
|
|
const MdnsQuestion& question() const { return question_; }
|
|
|
|
private:
|
|
using MdnsTracker::tracker_type;
|
|
|
|
using RecordKey = std::tuple<DomainName, DnsType, DnsClass>;
|
|
|
|
// Determines if all answers to this query have been received.
|
|
bool HasReceivedAllResponses();
|
|
|
|
// MdnsTracker overrides.
|
|
bool SendQuery() const override;
|
|
void ScheduleFollowUpQuery() override;
|
|
std::vector<MdnsRecord> GetRecords() const override;
|
|
|
|
// Stores MdnsQuestion provided to Start method call.
|
|
MdnsQuestion question_;
|
|
|
|
// A delay between the currently scheduled and the next queries.
|
|
Clock::duration send_delay_;
|
|
|
|
// Last time that this tracker's question was asked.
|
|
mutable TrivialClockTraits::time_point last_send_time_;
|
|
|
|
// Specifies whether this query is intended to be a one-shot query, as defined
|
|
// in RFC 6762 section 5.1.
|
|
const QueryType query_type_;
|
|
|
|
// Signifies the maximum number of times a record should be announced.
|
|
int maximum_announcement_count_;
|
|
|
|
// Number of times this query has been announced.
|
|
int announcements_so_far_ = 0;
|
|
};
|
|
|
|
} // namespace discovery
|
|
} // namespace openscreen
|
|
|
|
#endif // DISCOVERY_MDNS_MDNS_TRACKERS_H_
|