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_TRACKERS_H_ 6*3f982cf4SFabien Sanglard #define DISCOVERY_MDNS_MDNS_TRACKERS_H_ 7*3f982cf4SFabien Sanglard 8*3f982cf4SFabien Sanglard #include <tuple> 9*3f982cf4SFabien Sanglard #include <unordered_map> 10*3f982cf4SFabien Sanglard #include <vector> 11*3f982cf4SFabien Sanglard 12*3f982cf4SFabien Sanglard #include "absl/hash/hash.h" 13*3f982cf4SFabien Sanglard #include "discovery/mdns/mdns_records.h" 14*3f982cf4SFabien Sanglard #include "platform/api/task_runner.h" 15*3f982cf4SFabien Sanglard #include "platform/base/error.h" 16*3f982cf4SFabien Sanglard #include "platform/base/trivial_clock_traits.h" 17*3f982cf4SFabien Sanglard #include "util/alarm.h" 18*3f982cf4SFabien Sanglard 19*3f982cf4SFabien Sanglard namespace openscreen { 20*3f982cf4SFabien Sanglard namespace discovery { 21*3f982cf4SFabien Sanglard 22*3f982cf4SFabien Sanglard struct Config; 23*3f982cf4SFabien Sanglard class MdnsRandom; 24*3f982cf4SFabien Sanglard class MdnsRecord; 25*3f982cf4SFabien Sanglard class MdnsRecordChangedCallback; 26*3f982cf4SFabien Sanglard class MdnsSender; 27*3f982cf4SFabien Sanglard 28*3f982cf4SFabien Sanglard // MdnsTracker is a base class for MdnsRecordTracker and MdnsQuestionTracker for 29*3f982cf4SFabien Sanglard // the purposes of common code sharing only. 30*3f982cf4SFabien Sanglard // 31*3f982cf4SFabien Sanglard // Instances of this class represent nodes of a bidirectional graph, such that 32*3f982cf4SFabien Sanglard // if node A is adjacent to node B, B is also adjacent to A. In this class, the 33*3f982cf4SFabien Sanglard // adjacent nodes are stored in adjacency list |associated_tracker_|, and 34*3f982cf4SFabien Sanglard // exposed methods to add and remove nodes from this list also modify the added 35*3f982cf4SFabien Sanglard // or removed node to remove this instance from its adjacency list. 36*3f982cf4SFabien Sanglard // 37*3f982cf4SFabien Sanglard // Because MdnsQuestionTracker::AddAssocaitedRecord() can only called on 38*3f982cf4SFabien Sanglard // MdnsRecordTracker objects and MdnsRecordTracker::AddAssociatedQuery() is 39*3f982cf4SFabien Sanglard // only called on MdnsQuestionTracker objects, this created graph is bipartite. 40*3f982cf4SFabien Sanglard // This means that MdnsRecordTracker objects are only adjacent to 41*3f982cf4SFabien Sanglard // MdnsQuestionTracker objects and the opposite. 42*3f982cf4SFabien Sanglard class MdnsTracker { 43*3f982cf4SFabien Sanglard public: 44*3f982cf4SFabien Sanglard enum class TrackerType { kRecordTracker, kQuestionTracker }; 45*3f982cf4SFabien Sanglard 46*3f982cf4SFabien Sanglard // MdnsTracker does not own |sender|, |task_runner| and |random_delay| 47*3f982cf4SFabien Sanglard // and expects that the lifetime of these objects exceeds the lifetime of 48*3f982cf4SFabien Sanglard // MdnsTracker. 49*3f982cf4SFabien Sanglard MdnsTracker(MdnsSender* sender, 50*3f982cf4SFabien Sanglard TaskRunner* task_runner, 51*3f982cf4SFabien Sanglard ClockNowFunctionPtr now_function, 52*3f982cf4SFabien Sanglard MdnsRandom* random_delay, 53*3f982cf4SFabien Sanglard TrackerType tracker_type); 54*3f982cf4SFabien Sanglard MdnsTracker(const MdnsTracker& other) = delete; 55*3f982cf4SFabien Sanglard MdnsTracker(MdnsTracker&& other) noexcept = delete; 56*3f982cf4SFabien Sanglard MdnsTracker& operator=(const MdnsTracker& other) = delete; 57*3f982cf4SFabien Sanglard MdnsTracker& operator=(MdnsTracker&& other) noexcept = delete; 58*3f982cf4SFabien Sanglard virtual ~MdnsTracker(); 59*3f982cf4SFabien Sanglard 60*3f982cf4SFabien Sanglard // Returns the record type represented by this tracker. tracker_type()61*3f982cf4SFabien Sanglard TrackerType tracker_type() const { return tracker_type_; } 62*3f982cf4SFabien Sanglard 63*3f982cf4SFabien Sanglard // Sends a query message via MdnsSender. Returns false if a follow up query 64*3f982cf4SFabien Sanglard // should NOT be scheduled and true otherwise. 65*3f982cf4SFabien Sanglard virtual bool SendQuery() const = 0; 66*3f982cf4SFabien Sanglard 67*3f982cf4SFabien Sanglard // Returns the records currently associated with this tracker. 68*3f982cf4SFabien Sanglard virtual std::vector<MdnsRecord> GetRecords() const = 0; 69*3f982cf4SFabien Sanglard 70*3f982cf4SFabien Sanglard protected: 71*3f982cf4SFabien Sanglard // Schedules a repeat query to be sent out. 72*3f982cf4SFabien Sanglard virtual void ScheduleFollowUpQuery() = 0; 73*3f982cf4SFabien Sanglard 74*3f982cf4SFabien Sanglard // These methods create a bidirectional adjacency with another node in the 75*3f982cf4SFabien Sanglard // graph. 76*3f982cf4SFabien Sanglard bool AddAdjacentNode(const MdnsTracker* tracker) const; 77*3f982cf4SFabien Sanglard bool RemoveAdjacentNode(const MdnsTracker* tracker) const; 78*3f982cf4SFabien Sanglard adjacent_nodes()79*3f982cf4SFabien Sanglard const std::vector<const MdnsTracker*>& adjacent_nodes() const { 80*3f982cf4SFabien Sanglard return adjacent_nodes_; 81*3f982cf4SFabien Sanglard } 82*3f982cf4SFabien Sanglard 83*3f982cf4SFabien Sanglard MdnsSender* const sender_; 84*3f982cf4SFabien Sanglard TaskRunner* const task_runner_; 85*3f982cf4SFabien Sanglard const ClockNowFunctionPtr now_function_; 86*3f982cf4SFabien Sanglard Alarm send_alarm_; 87*3f982cf4SFabien Sanglard MdnsRandom* const random_delay_; 88*3f982cf4SFabien Sanglard TrackerType tracker_type_; 89*3f982cf4SFabien Sanglard 90*3f982cf4SFabien Sanglard private: 91*3f982cf4SFabien Sanglard // These methods are used to ensure the bidirectional-ness of this graph. 92*3f982cf4SFabien Sanglard void AddReverseAdjacency(const MdnsTracker* tracker) const; 93*3f982cf4SFabien Sanglard void RemovedReverseAdjacency(const MdnsTracker* tracker) const; 94*3f982cf4SFabien Sanglard 95*3f982cf4SFabien Sanglard // Adjacency list for this graph node. 96*3f982cf4SFabien Sanglard mutable std::vector<const MdnsTracker*> adjacent_nodes_; 97*3f982cf4SFabien Sanglard }; 98*3f982cf4SFabien Sanglard 99*3f982cf4SFabien Sanglard class MdnsQuestionTracker; 100*3f982cf4SFabien Sanglard 101*3f982cf4SFabien Sanglard // MdnsRecordTracker manages automatic resending of mDNS queries for 102*3f982cf4SFabien Sanglard // refreshing records as they reach their expiration time. 103*3f982cf4SFabien Sanglard class MdnsRecordTracker : public MdnsTracker { 104*3f982cf4SFabien Sanglard public: 105*3f982cf4SFabien Sanglard using RecordExpiredCallback = 106*3f982cf4SFabien Sanglard std::function<void(const MdnsRecordTracker*, const MdnsRecord&)>; 107*3f982cf4SFabien Sanglard 108*3f982cf4SFabien Sanglard // NOTE: In the case that |record| is of type NSEC, |dns_type| is expected to 109*3f982cf4SFabien Sanglard // differ from |record|'s type. 110*3f982cf4SFabien Sanglard MdnsRecordTracker(MdnsRecord record, 111*3f982cf4SFabien Sanglard DnsType dns_type, 112*3f982cf4SFabien Sanglard MdnsSender* sender, 113*3f982cf4SFabien Sanglard TaskRunner* task_runner, 114*3f982cf4SFabien Sanglard ClockNowFunctionPtr now_function, 115*3f982cf4SFabien Sanglard MdnsRandom* random_delay, 116*3f982cf4SFabien Sanglard RecordExpiredCallback record_expired_callback); 117*3f982cf4SFabien Sanglard 118*3f982cf4SFabien Sanglard ~MdnsRecordTracker() override; 119*3f982cf4SFabien Sanglard 120*3f982cf4SFabien Sanglard // Possible outcomes from updating a tracked record. 121*3f982cf4SFabien Sanglard enum class UpdateType { 122*3f982cf4SFabien Sanglard kGoodbye, // The record has a TTL of 0 and will expire. 123*3f982cf4SFabien Sanglard kTTLOnly, // The record updated its TTL only. 124*3f982cf4SFabien Sanglard kRdata // The record updated its RDATA. 125*3f982cf4SFabien Sanglard }; 126*3f982cf4SFabien Sanglard 127*3f982cf4SFabien Sanglard // Updates record tracker with the new record: 128*3f982cf4SFabien Sanglard // 1. Resets TTL to the value specified in |new_record|. 129*3f982cf4SFabien Sanglard // 2. Schedules expiration in case of a goodbye record. 130*3f982cf4SFabien Sanglard // Returns Error::Code::kParameterInvalid if new_record is not a valid update 131*3f982cf4SFabien Sanglard // for the current tracked record. 132*3f982cf4SFabien Sanglard ErrorOr<UpdateType> Update(const MdnsRecord& new_record); 133*3f982cf4SFabien Sanglard 134*3f982cf4SFabien Sanglard // Adds or removed a question which this record answers. 135*3f982cf4SFabien Sanglard bool AddAssociatedQuery(const MdnsQuestionTracker* question_tracker) const; 136*3f982cf4SFabien Sanglard bool RemoveAssociatedQuery(const MdnsQuestionTracker* question_tracker) const; 137*3f982cf4SFabien Sanglard 138*3f982cf4SFabien Sanglard // Sets record to expire after 1 seconds as per RFC 6762 139*3f982cf4SFabien Sanglard void ExpireSoon(); 140*3f982cf4SFabien Sanglard 141*3f982cf4SFabien Sanglard // Expires the record now 142*3f982cf4SFabien Sanglard void ExpireNow(); 143*3f982cf4SFabien Sanglard 144*3f982cf4SFabien Sanglard // Returns true if half of the record's TTL has passed, and false otherwise. 145*3f982cf4SFabien Sanglard // Half is used due to specifications in RFC 6762 section 7.1. 146*3f982cf4SFabien Sanglard bool IsNearingExpiry() const; 147*3f982cf4SFabien Sanglard 148*3f982cf4SFabien Sanglard // Returns information about the stored record. 149*3f982cf4SFabien Sanglard // 150*3f982cf4SFabien Sanglard // NOTE: These methods are NOT all pass-through methods to |record_|. 151*3f982cf4SFabien Sanglard // specifically, dns_type() returns the DNS Type associated with this record 152*3f982cf4SFabien Sanglard // tracker, which may be different from the record type if |record_| is of 153*3f982cf4SFabien Sanglard // type NSEC. To avoid this case, direct access to the underlying |record_| 154*3f982cf4SFabien Sanglard // instance is not provided. 155*3f982cf4SFabien Sanglard // 156*3f982cf4SFabien Sanglard // In this case, creating an MdnsRecord with the below data will result in a 157*3f982cf4SFabien Sanglard // runtime error due to DCHECKS and that Rdata's associated type will not 158*3f982cf4SFabien Sanglard // match DnsType when |record_| is of type NSEC. Therefore, creating such 159*3f982cf4SFabien Sanglard // records should be guarded by is_negative_response() checks. name()160*3f982cf4SFabien Sanglard const DomainName& name() const { return record_.name(); } dns_type()161*3f982cf4SFabien Sanglard DnsType dns_type() const { return dns_type_; } dns_class()162*3f982cf4SFabien Sanglard DnsClass dns_class() const { return record_.dns_class(); } record_type()163*3f982cf4SFabien Sanglard RecordType record_type() const { return record_.record_type(); } ttl()164*3f982cf4SFabien Sanglard std::chrono::seconds ttl() const { return record_.ttl(); } rdata()165*3f982cf4SFabien Sanglard const Rdata& rdata() const { return record_.rdata(); } 166*3f982cf4SFabien Sanglard is_negative_response()167*3f982cf4SFabien Sanglard bool is_negative_response() const { 168*3f982cf4SFabien Sanglard return record_.dns_type() == DnsType::kNSEC; 169*3f982cf4SFabien Sanglard } 170*3f982cf4SFabien Sanglard 171*3f982cf4SFabien Sanglard private: 172*3f982cf4SFabien Sanglard using MdnsTracker::tracker_type; 173*3f982cf4SFabien Sanglard 174*3f982cf4SFabien Sanglard // Needed to provide the test class access to the record stored in this 175*3f982cf4SFabien Sanglard // tracker. 176*3f982cf4SFabien Sanglard friend class MdnsTrackerTest; 177*3f982cf4SFabien Sanglard 178*3f982cf4SFabien Sanglard Clock::time_point GetNextSendTime(); 179*3f982cf4SFabien Sanglard 180*3f982cf4SFabien Sanglard // MdnsTracker overrides. 181*3f982cf4SFabien Sanglard bool SendQuery() const override; 182*3f982cf4SFabien Sanglard void ScheduleFollowUpQuery() override; 183*3f982cf4SFabien Sanglard std::vector<MdnsRecord> GetRecords() const override; 184*3f982cf4SFabien Sanglard 185*3f982cf4SFabien Sanglard // Stores MdnsRecord provided to Start method call. 186*3f982cf4SFabien Sanglard MdnsRecord record_; 187*3f982cf4SFabien Sanglard 188*3f982cf4SFabien Sanglard // DnsType this record tracker represents. This may not match the type of 189*3f982cf4SFabien Sanglard // |record_| if it is an NSEC record. 190*3f982cf4SFabien Sanglard const DnsType dns_type_; 191*3f982cf4SFabien Sanglard 192*3f982cf4SFabien Sanglard // A point in time when the record was received and the tracking has started. 193*3f982cf4SFabien Sanglard Clock::time_point start_time_; 194*3f982cf4SFabien Sanglard 195*3f982cf4SFabien Sanglard // Number of times record refresh has been attempted. 196*3f982cf4SFabien Sanglard size_t attempt_count_ = 0; 197*3f982cf4SFabien Sanglard RecordExpiredCallback record_expired_callback_; 198*3f982cf4SFabien Sanglard }; 199*3f982cf4SFabien Sanglard 200*3f982cf4SFabien Sanglard // MdnsQuestionTracker manages automatic resending of mDNS queries for 201*3f982cf4SFabien Sanglard // continuous monitoring with exponential back-off as described in RFC 6762. 202*3f982cf4SFabien Sanglard class MdnsQuestionTracker : public MdnsTracker { 203*3f982cf4SFabien Sanglard public: 204*3f982cf4SFabien Sanglard // Supported query types, per RFC 6762 section 5. 205*3f982cf4SFabien Sanglard enum class QueryType { kOneShot, kContinuous }; 206*3f982cf4SFabien Sanglard 207*3f982cf4SFabien Sanglard MdnsQuestionTracker(MdnsQuestion question, 208*3f982cf4SFabien Sanglard MdnsSender* sender, 209*3f982cf4SFabien Sanglard TaskRunner* task_runner, 210*3f982cf4SFabien Sanglard ClockNowFunctionPtr now_function, 211*3f982cf4SFabien Sanglard MdnsRandom* random_delay, 212*3f982cf4SFabien Sanglard const Config& config, 213*3f982cf4SFabien Sanglard QueryType query_type = QueryType::kContinuous); 214*3f982cf4SFabien Sanglard 215*3f982cf4SFabien Sanglard ~MdnsQuestionTracker() override; 216*3f982cf4SFabien Sanglard 217*3f982cf4SFabien Sanglard // Adds or removed an answer to a the question posed by this tracker. 218*3f982cf4SFabien Sanglard bool AddAssociatedRecord(const MdnsRecordTracker* record_tracker) const; 219*3f982cf4SFabien Sanglard bool RemoveAssociatedRecord(const MdnsRecordTracker* record_tracker) const; 220*3f982cf4SFabien Sanglard 221*3f982cf4SFabien Sanglard // Returns a reference to the tracked question. question()222*3f982cf4SFabien Sanglard const MdnsQuestion& question() const { return question_; } 223*3f982cf4SFabien Sanglard 224*3f982cf4SFabien Sanglard private: 225*3f982cf4SFabien Sanglard using MdnsTracker::tracker_type; 226*3f982cf4SFabien Sanglard 227*3f982cf4SFabien Sanglard using RecordKey = std::tuple<DomainName, DnsType, DnsClass>; 228*3f982cf4SFabien Sanglard 229*3f982cf4SFabien Sanglard // Determines if all answers to this query have been received. 230*3f982cf4SFabien Sanglard bool HasReceivedAllResponses(); 231*3f982cf4SFabien Sanglard 232*3f982cf4SFabien Sanglard // MdnsTracker overrides. 233*3f982cf4SFabien Sanglard bool SendQuery() const override; 234*3f982cf4SFabien Sanglard void ScheduleFollowUpQuery() override; 235*3f982cf4SFabien Sanglard std::vector<MdnsRecord> GetRecords() const override; 236*3f982cf4SFabien Sanglard 237*3f982cf4SFabien Sanglard // Stores MdnsQuestion provided to Start method call. 238*3f982cf4SFabien Sanglard MdnsQuestion question_; 239*3f982cf4SFabien Sanglard 240*3f982cf4SFabien Sanglard // A delay between the currently scheduled and the next queries. 241*3f982cf4SFabien Sanglard Clock::duration send_delay_; 242*3f982cf4SFabien Sanglard 243*3f982cf4SFabien Sanglard // Last time that this tracker's question was asked. 244*3f982cf4SFabien Sanglard mutable TrivialClockTraits::time_point last_send_time_; 245*3f982cf4SFabien Sanglard 246*3f982cf4SFabien Sanglard // Specifies whether this query is intended to be a one-shot query, as defined 247*3f982cf4SFabien Sanglard // in RFC 6762 section 5.1. 248*3f982cf4SFabien Sanglard const QueryType query_type_; 249*3f982cf4SFabien Sanglard 250*3f982cf4SFabien Sanglard // Signifies the maximum number of times a record should be announced. 251*3f982cf4SFabien Sanglard int maximum_announcement_count_; 252*3f982cf4SFabien Sanglard 253*3f982cf4SFabien Sanglard // Number of times this query has been announced. 254*3f982cf4SFabien Sanglard int announcements_so_far_ = 0; 255*3f982cf4SFabien Sanglard }; 256*3f982cf4SFabien Sanglard 257*3f982cf4SFabien Sanglard } // namespace discovery 258*3f982cf4SFabien Sanglard } // namespace openscreen 259*3f982cf4SFabien Sanglard 260*3f982cf4SFabien Sanglard #endif // DISCOVERY_MDNS_MDNS_TRACKERS_H_ 261