diff options
author | Ryan Keane <rwkeane@google.com> | 2020-06-18 20:41:58 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-06-19 04:12:20 +0000 |
commit | ec44eb332577b1f3b8865fe78abec5c05c7727bf (patch) | |
tree | 43f472baf45fda386a67e3bb68584a9780fdef41 | |
parent | 1cdd473e5bca0322bff2e2a8cd975389b02da3cf (diff) | |
download | openscreen-ec44eb332577b1f3b8865fe78abec5c05c7727bf.tar.gz |
mDNS: Add new OPT record type
This CL adds support for the OPT record type, as defined in RFC 6891.
Bug: b/157683753
Change-Id: I65b45c4e8c423174825522c83aa8f00b26d94c9c
Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/2233144
Commit-Queue: Ryan Keane <rwkeane@google.com>
Reviewed-by: Jordan Bayles <jophba@chromium.org>
-rw-r--r-- | discovery/mdns/mdns_publisher.cc | 15 | ||||
-rw-r--r-- | discovery/mdns/mdns_publisher.h | 8 | ||||
-rw-r--r-- | discovery/mdns/mdns_querier.cc | 12 | ||||
-rw-r--r-- | discovery/mdns/mdns_querier.h | 12 | ||||
-rw-r--r-- | discovery/mdns/mdns_records.cc | 151 | ||||
-rw-r--r-- | discovery/mdns/mdns_records.h | 84 | ||||
-rw-r--r-- | discovery/mdns/mdns_records_unittest.cc | 62 | ||||
-rw-r--r-- | discovery/mdns/mdns_responder.cc | 19 | ||||
-rw-r--r-- | discovery/mdns/mdns_writer.cc | 11 | ||||
-rw-r--r-- | discovery/mdns/mdns_writer.h | 3 | ||||
-rw-r--r-- | discovery/mdns/public/mdns_constants.h | 23 |
11 files changed, 379 insertions, 21 deletions
diff --git a/discovery/mdns/mdns_publisher.cc b/discovery/mdns/mdns_publisher.cc index 97a72a6c..4c09d169 100644 --- a/discovery/mdns/mdns_publisher.cc +++ b/discovery/mdns/mdns_publisher.cc @@ -42,11 +42,6 @@ inline MdnsRecord CreateGoodbyeRecord(const MdnsRecord& record) { record.record_type(), kGoodbyeTtl, record.rdata()); } -inline void ValidateRecord(const MdnsRecord& record) { - OSP_DCHECK(record.dns_type() != DnsType::kANY); - OSP_DCHECK(record.dns_class() != DnsClass::kANY); -} - } // namespace MdnsPublisher::MdnsPublisher(MdnsSender* sender, @@ -74,11 +69,11 @@ MdnsPublisher::~MdnsPublisher() { Error MdnsPublisher::RegisterRecord(const MdnsRecord& record) { OSP_DCHECK(task_runner_->IsRunningOnTaskRunner()); + OSP_DCHECK(record.dns_class() != DnsClass::kANY); - if (record.dns_type() == DnsType::kNSEC) { + if (!CanBePublished(record.dns_type())) { return Error::Code::kParameterInvalid; } - ValidateRecord(record); if (!IsRecordNameClaimed(record)) { return Error::Code::kParameterInvalid; @@ -101,11 +96,11 @@ Error MdnsPublisher::RegisterRecord(const MdnsRecord& record) { Error MdnsPublisher::UnregisterRecord(const MdnsRecord& record) { OSP_DCHECK(task_runner_->IsRunningOnTaskRunner()); + OSP_DCHECK(record.dns_class() != DnsClass::kANY); - if (record.dns_type() == DnsType::kNSEC) { + if (!CanBePublished(record.dns_type())) { return Error::Code::kParameterInvalid; } - ValidateRecord(record); OSP_DVLOG << "Unregistering record of type '" << record.dns_type() << "'"; @@ -116,7 +111,7 @@ Error MdnsPublisher::UpdateRegisteredRecord(const MdnsRecord& old_record, const MdnsRecord& new_record) { OSP_DCHECK(task_runner_->IsRunningOnTaskRunner()); - if (old_record.dns_type() == DnsType::kNSEC) { + if (!CanBePublished(new_record.dns_type())) { return Error::Code::kParameterInvalid; } diff --git a/discovery/mdns/mdns_publisher.h b/discovery/mdns/mdns_publisher.h index 4b418312..b9092697 100644 --- a/discovery/mdns/mdns_publisher.h +++ b/discovery/mdns/mdns_publisher.h @@ -58,7 +58,13 @@ class MdnsPublisher : public MdnsResponder::RecordHandler { // 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: NSEC records cannot be registered, and doing so will return an error. + // 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. diff --git a/discovery/mdns/mdns_querier.cc b/discovery/mdns/mdns_querier.cc index 7152be24..2ae7260e 100644 --- a/discovery/mdns/mdns_querier.cc +++ b/discovery/mdns/mdns_querier.cc @@ -242,7 +242,7 @@ void MdnsQuerier::StartQuery(const DomainName& name, MdnsRecordChangedCallback* callback) { OSP_DCHECK(task_runner_->IsRunningOnTaskRunner()); OSP_DCHECK(callback); - OSP_DCHECK(dns_type != DnsType::kNSEC); + OSP_DCHECK(CanBeQueried(dns_type)); // Add a new callback if haven't seen it before auto callbacks_it = callbacks_.equal_range(name); @@ -300,7 +300,10 @@ void MdnsQuerier::StopQuery(const DomainName& name, MdnsRecordChangedCallback* callback) { OSP_DCHECK(task_runner_->IsRunningOnTaskRunner()); OSP_DCHECK(callback); - OSP_DCHECK(dns_type != DnsType::kNSEC); + + if (!CanBeQueried(dns_type)) { + return; + } // Find and remove the callback. int callbacks_for_key = 0; @@ -451,6 +454,11 @@ void MdnsQuerier::OnRecordExpired(const MdnsRecordTracker* tracker, void MdnsQuerier::ProcessRecord(const MdnsRecord& record) { OSP_DCHECK(task_runner_->IsRunningOnTaskRunner()); + // Skip all records that can't be processed. + if (!CanBeProcessed(record.dns_type())) { + return; + } + // Get the types which the received record is associated with. In most cases // this will only be the type of the provided record, but in the case of // NSEC records this will be all records which the record dictates the diff --git a/discovery/mdns/mdns_querier.h b/discovery/mdns/mdns_querier.h index 8f17790b..07f1cbf7 100644 --- a/discovery/mdns/mdns_querier.h +++ b/discovery/mdns/mdns_querier.h @@ -7,6 +7,8 @@ #include <list> #include <map> +#include <memory> +#include <vector> #include "discovery/common/config.h" #include "discovery/mdns/mdns_receiver.h" @@ -42,7 +44,13 @@ class MdnsQuerier : public MdnsReceiver::ResponseClient { // Starts an mDNS query with the given name, DNS type, and DNS class. Updated // records are passed to |callback|. The caller must ensure |callback| // remains alive while it is registered with a query. - // NOTE: NSEC records cannot be queried for. + // NOTE: This call is only valid for |dns_type| values: + // - DnsType::kA + // - DnsType::kPTR + // - DnsType::kTXT + // - DnsType::kAAAA + // - DnsType::kSRV + // - DnsType::kANY void StartQuery(const DomainName& name, DnsType dns_type, DnsClass dns_class, @@ -164,6 +172,8 @@ class MdnsQuerier : public MdnsReceiver::ResponseClient { bool ShouldAnswerRecordBeProcessed(const MdnsRecord& answer); // Processes any record update, calling into the below methods as needed. + // NOTE: All records of type OPT are dropped, as they should not be cached per + // RFC6891. void ProcessRecord(const MdnsRecord& records); // Processes a shared record update as a record of type |type|. diff --git a/discovery/mdns/mdns_records.cc b/discovery/mdns/mdns_records.cc index 8be7fe00..eadbff9c 100644 --- a/discovery/mdns/mdns_records.cc +++ b/discovery/mdns/mdns_records.cc @@ -4,6 +4,7 @@ #include "discovery/mdns/mdns_records.h" +#include <algorithm> #include <cctype> #include <limits> #include <sstream> @@ -458,6 +459,83 @@ size_t NsecRecordRdata::MaxWireSize() const { return next_domain_name_.MaxWireSize() + encoded_types_.size(); } +size_t OptRecordRdata::Option::MaxWireSize() const { + // One uint16_t for each of OPTION-LENGTH and OPTION-CODE as defined in RFC + // 6891 section 6.1.2. + constexpr size_t kOptionLengthAndCodeSize = 2 * sizeof(uint16_t); + return data.size() + kOptionLengthAndCodeSize; +} + +bool OptRecordRdata::Option::operator>( + const OptRecordRdata::Option& rhs) const { + if (code != rhs.code) { + return code > rhs.code; + } else if (length != rhs.length) { + return length > rhs.length; + } else if (data.size() != rhs.data.size()) { + return data.size() > rhs.data.size(); + } + + for (int i = 0; i < static_cast<int>(data.size()); i++) { + if (data[i] != rhs.data[i]) { + return data[i] > rhs.data[i]; + } + } + + return false; +} + +bool OptRecordRdata::Option::operator<( + const OptRecordRdata::Option& rhs) const { + return rhs > *this; +} + +bool OptRecordRdata::Option::operator>=( + const OptRecordRdata::Option& rhs) const { + return !(*this < rhs); +} + +bool OptRecordRdata::Option::operator<=( + const OptRecordRdata::Option& rhs) const { + return !(*this > rhs); +} + +bool OptRecordRdata::Option::operator==( + const OptRecordRdata::Option& rhs) const { + return *this >= rhs && *this <= rhs; +} + +bool OptRecordRdata::Option::operator!=( + const OptRecordRdata::Option& rhs) const { + return !(*this == rhs); +} + +OptRecordRdata::OptRecordRdata() = default; + +OptRecordRdata::OptRecordRdata(std::vector<Option> options) + : options_(std::move(options)) { + for (const auto& option : options_) { + max_wire_size_ += option.MaxWireSize(); + } + std::sort(options_.begin(), options_.end()); +} + +OptRecordRdata::OptRecordRdata(const OptRecordRdata& other) = default; + +OptRecordRdata::OptRecordRdata(OptRecordRdata&& other) = default; + +OptRecordRdata& OptRecordRdata::operator=(const OptRecordRdata& rhs) = default; + +OptRecordRdata& OptRecordRdata::operator=(OptRecordRdata&& rhs) = default; + +bool OptRecordRdata::operator==(const OptRecordRdata& rhs) const { + return options_ == rhs.options_; +} + +bool OptRecordRdata::operator!=(const OptRecordRdata& rhs) const { + return !(*this == rhs); +} + // static ErrorOr<MdnsRecord> MdnsRecord::TryCreate(DomainName name, DnsType dns_type, @@ -503,7 +581,12 @@ bool MdnsRecord::IsValidConfig(const DomainName& name, DnsType dns_type, std::chrono::seconds ttl, const Rdata& rdata) { - return !name.empty() && ttl.count() <= std::numeric_limits<uint32_t>::max() && + // NOTE: Although the name_ field was initially expected to be non-empty, this + // validation is no longer accurate for some record types (such as OPT + // records). To ensure that future record types correctly parse into + // RawRecordData types and do not invalidate the received message, this check + // has been removed. + return ttl.count() <= std::numeric_limits<uint32_t>::max() && ((dns_type == DnsType::kSRV && absl::holds_alternative<SrvRecordRdata>(rdata)) || (dns_type == DnsType::kA && @@ -516,6 +599,8 @@ bool MdnsRecord::IsValidConfig(const DomainName& name, absl::holds_alternative<TxtRecordRdata>(rdata)) || (dns_type == DnsType::kNSEC && absl::holds_alternative<NsecRecordRdata>(rdata)) || + (dns_type == DnsType::kOPT && + absl::holds_alternative<OptRecordRdata>(rdata)) || absl::holds_alternative<RawRecordRdata>(rdata)); } @@ -772,5 +857,69 @@ uint16_t CreateMessageId() { return id++; } +bool CanBePublished(DnsType type) { + // NOTE: A 'default' switch statement has intentionally been avoided below to + // enforce that new DnsTypes added must be added below through a compile-time + // check. + switch (type) { + case DnsType::kA: + case DnsType::kAAAA: + case DnsType::kPTR: + case DnsType::kTXT: + case DnsType::kSRV: + return true; + case DnsType::kOPT: + case DnsType::kNSEC: + case DnsType::kANY: + break; + } + + return false; +} + +bool CanBePublished(const MdnsRecord& record) { + return CanBePublished(record.dns_type()); +} + +bool CanBeQueried(DnsType type) { + // NOTE: A 'default' switch statement has intentionally been avoided below to + // enforce that new DnsTypes added must be added below through a compile-time + // check. + switch (type) { + case DnsType::kA: + case DnsType::kAAAA: + case DnsType::kPTR: + case DnsType::kTXT: + case DnsType::kSRV: + case DnsType::kANY: + return true; + case DnsType::kOPT: + case DnsType::kNSEC: + break; + } + + return false; +} + +bool CanBeProcessed(DnsType type) { + // NOTE: A 'default' switch statement has intentionally been avoided below to + // enforce that new DnsTypes added must be added below through a compile-time + // check. + switch (type) { + case DnsType::kA: + case DnsType::kAAAA: + case DnsType::kPTR: + case DnsType::kTXT: + case DnsType::kSRV: + case DnsType::kNSEC: + return true; + case DnsType::kOPT: + case DnsType::kANY: + break; + } + + return false; +} + } // namespace discovery } // namespace openscreen diff --git a/discovery/mdns/mdns_records.h b/discovery/mdns/mdns_records.h index 696eaf8c..9b2df04e 100644 --- a/discovery/mdns/mdns_records.h +++ b/discovery/mdns/mdns_records.h @@ -83,6 +83,7 @@ class DomainName { // compression the actual space taken in on-the-wire format is smaller. size_t MaxWireSize() const; bool empty() const { return labels_.empty(); } + bool IsRoot() const { return labels_.empty(); } const std::vector<std::string>& labels() const { return labels_; } template <typename H> @@ -359,13 +360,84 @@ class NsecRecordRdata { DomainName next_domain_name_; }; +// The OPT pseudo-record / meta-record as defined by RFC6891. +class OptRecordRdata { + public: + // A single option as defined in RFC6891 section 6.1.2. + struct Option { + size_t MaxWireSize() const; + + bool operator>(const Option& rhs) const; + bool operator<(const Option& rhs) const; + bool operator>=(const Option& rhs) const; + bool operator<=(const Option& rhs) const; + bool operator==(const Option& rhs) const; + bool operator!=(const Option& rhs) const; + + template <typename H> + friend H AbslHashValue(H h, const Option& option) { + return H::combine(std::move(h), option.code, option.length, option.data); + } + + // Code assigned by the Expert Review process as defined by the DNSEXT + // working group and the IESG, as specified in RFC6891 section 9.1. For + // specific assignments, see: + // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml + uint16_t code; + + // Size (in octets) of |data|. + uint16_t length; + + // Bit Field with meaning varying based on |code|. + std::vector<uint8_t> data; + }; + + OptRecordRdata(); + + // Constructor that takes zero or more Option parameters. + template <typename... Types> + explicit OptRecordRdata(Types... types) + : OptRecordRdata(std::vector<Option>{std::move(types)...}) {} + explicit OptRecordRdata(std::vector<Option> options); + OptRecordRdata(const OptRecordRdata& other); + OptRecordRdata(OptRecordRdata&& other); + + OptRecordRdata& operator=(const OptRecordRdata& rhs); + OptRecordRdata& operator=(OptRecordRdata&& rhs); + + // NOTE: Only the options field is technically considered part of the rdata, + // so only this field is considered for equality comparison. The other fields + // are included here solely because their meaning differs for OPT pseudo- + // records and normal record types. + bool operator==(const OptRecordRdata& rhs) const; + bool operator!=(const OptRecordRdata& rhs) const; + + size_t MaxWireSize() const { return max_wire_size_; } + + // Set of options stored in this OPT record. + const std::vector<Option>& options() { return options_; } + + template <typename H> + friend H AbslHashValue(H h, const OptRecordRdata& rdata) { + return H::combine(std::move(h), rdata.options_); + } + + private: + // NOTE: The elements of |options_| are stored is sorted order to simplify the + // comparison operators of OptRecordRdata. + std::vector<Option> options_; + + size_t max_wire_size_ = 0; +}; + using Rdata = absl::variant<RawRecordRdata, SrvRecordRdata, ARecordRdata, AAAARecordRdata, PtrRecordRdata, TxtRecordRdata, - NsecRecordRdata>; + NsecRecordRdata, + OptRecordRdata>; // Resource record top level format (http://www.ietf.org/rfc/rfc1035.txt): // name: the name of the node to which this resource record pertains. @@ -567,6 +639,16 @@ class MdnsMessage { uint16_t CreateMessageId(); +// Determines whether a record of the given type can be published. +bool CanBePublished(DnsType type); + +// Determines whether a record of the given type can be queried for. +bool CanBeQueried(DnsType type); + +// Determines whether a record of the given type received over the network +// should be processed. +bool CanBeProcessed(DnsType type); + } // namespace discovery } // namespace openscreen diff --git a/discovery/mdns/mdns_records_unittest.cc b/discovery/mdns/mdns_records_unittest.cc index 6531697f..395c9259 100644 --- a/discovery/mdns/mdns_records_unittest.cc +++ b/discovery/mdns/mdns_records_unittest.cc @@ -4,6 +4,11 @@ #include "discovery/mdns/mdns_records.h" +#include <limits> +#include <string> +#include <utility> +#include <vector> + #include "absl/hash/hash_testing.h" #include "discovery/mdns/mdns_reader.h" #include "discovery/mdns/mdns_writer.h" @@ -460,6 +465,57 @@ TEST(MdnsNsecRecordRdataTest, CopyAndMove) { DnsType::kSRV)); } +TEST(MdnsOptRecordRdataTest, Construct) { + OptRecordRdata rdata1; + EXPECT_EQ(rdata1.MaxWireSize(), size_t{0}); + EXPECT_EQ(rdata1.options().size(), size_t{0}); + + OptRecordRdata::Option opt1{12, 34, {0x12, 0x34}}; + OptRecordRdata::Option opt2{12, 34, {0x12, 0x34}}; + OptRecordRdata::Option opt3{12, 34, {0x12, 0x34, 0x56}}; + OptRecordRdata::Option opt4{34, 12, {0x00}}; + OptRecordRdata::Option opt5{12, 12, {0x12, 0x34}}; + rdata1 = OptRecordRdata(opt1, opt2, opt3, opt4, opt5); + EXPECT_EQ(rdata1.MaxWireSize(), size_t{30}); + + ASSERT_EQ(rdata1.options().size(), size_t{5}); + EXPECT_EQ(rdata1.options()[0], opt5); + EXPECT_EQ(rdata1.options()[1], opt1); + EXPECT_EQ(rdata1.options()[2], opt2); + EXPECT_EQ(rdata1.options()[3], opt3); + EXPECT_EQ(rdata1.options()[4], opt4); +} + +TEST(MdnsOptRecordRdataTest, Compare) { + OptRecordRdata::Option opt1{12, 34, {0x12, 0x34}}; + OptRecordRdata::Option opt2{12, 34, {0x12, 0x34}}; + OptRecordRdata::Option opt3{12, 34, {0x12, 0x56}}; + OptRecordRdata rdata1(opt1); + OptRecordRdata rdata2(opt2); + OptRecordRdata rdata3(opt3); + OptRecordRdata rdata4; + + EXPECT_EQ(rdata1, rdata1); + EXPECT_EQ(rdata2, rdata2); + EXPECT_EQ(rdata3, rdata3); + EXPECT_EQ(rdata4, rdata4); + + EXPECT_EQ(rdata1, rdata2); + EXPECT_NE(rdata1, rdata3); + EXPECT_NE(rdata1, rdata4); + EXPECT_NE(rdata2, rdata3); + EXPECT_NE(rdata2, rdata4); + EXPECT_NE(rdata3, rdata4); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly( + {rdata1, rdata2, rdata3, rdata4})); +} + +TEST(MdnsOptRecordRdataTest, CopyAndMove) { + OptRecordRdata::Option opt1{12, 34, {0x12, 0x34}}; + TestCopyAndMove(OptRecordRdata(opt1)); +} + TEST(MdnsRecordTest, Construct) { MdnsRecord record1; EXPECT_EQ(record1.MaxWireSize(), UINT64_C(11)); @@ -712,5 +768,11 @@ TEST(MdnsMessageTest, CopyAndMove) { TestCopyAndMove(message); } +TEST(MdnsRecordOperations, CanBeProcessed) { + EXPECT_FALSE(CanBeProcessed(static_cast<DnsType>(1234))); + EXPECT_FALSE(CanBeProcessed(static_cast<DnsType>(222))); + EXPECT_FALSE(CanBeProcessed(static_cast<DnsType>(8973))); +} + } // namespace discovery } // namespace openscreen diff --git a/discovery/mdns/mdns_responder.cc b/discovery/mdns/mdns_responder.cc index faef9a18..953828e3 100644 --- a/discovery/mdns/mdns_responder.cc +++ b/discovery/mdns/mdns_responder.cc @@ -25,7 +25,10 @@ const std::array<std::string, 3> kServiceEnumerationDomainLabels{ enum AddResult { kNonePresent = 0, kAdded, kAlreadyKnown }; -std::chrono::seconds GetTtlForRecordType(DnsType type) { +std::chrono::seconds GetTtlForNsecTargetingType(DnsType type) { + // NOTE: A 'default' switch statement has intentionally been avoided below to + // enforce that new DnsTypes added must be added below through a compile-time + // check. switch (type) { case DnsType::kA: return kARecordTtl; @@ -41,17 +44,23 @@ std::chrono::seconds GetTtlForRecordType(DnsType type) { // If no records are present, re-querying should happen at the minimum // of any record that might be retrieved at that time. return kSrvRecordTtl; - default: - OSP_NOTREACHED(); - return std::chrono::seconds(0); + case DnsType::kNSEC: + case DnsType::kOPT: + // Neither of these types should ever be hit. We should never be creating + // an NSEC record for type NSEC, and OPT record querying is not supported, + // so creating NSEC records for type OPT is not valid. + break; } + + OSP_NOTREACHED() << "NSEC records do not support type " << type; + return std::chrono::seconds(0); } MdnsRecord CreateNsecRecord(DomainName target_name, DnsType target_type, DnsClass target_class) { auto rdata = NsecRecordRdata(target_name, target_type); - std::chrono::seconds ttl = GetTtlForRecordType(target_type); + std::chrono::seconds ttl = GetTtlForNsecTargetingType(target_type); return MdnsRecord(std::move(target_name), DnsType::kNSEC, target_class, RecordType::kUnique, ttl, std::move(rdata)); } diff --git a/discovery/mdns/mdns_writer.cc b/discovery/mdns/mdns_writer.cc index a95605b8..05880cb6 100644 --- a/discovery/mdns/mdns_writer.cc +++ b/discovery/mdns/mdns_writer.cc @@ -4,6 +4,11 @@ #include "discovery/mdns/mdns_writer.h" +#include <limits> +#include <string> +#include <utility> +#include <vector> + #include "absl/hash/hash.h" #include "absl/strings/ascii.h" #include "util/hashing.h" @@ -202,6 +207,12 @@ bool MdnsWriter::Write(const NsecRecordRdata& rdata) { return false; } +bool MdnsWriter::Write(const OptRecordRdata& rdata) { + // OPT records are currently not supported for outgoing messages. + OSP_UNIMPLEMENTED(); + return false; +} + bool MdnsWriter::Write(const MdnsRecord& record) { Cursor cursor(this); if (Write(record.name()) && Write(static_cast<uint16_t>(record.dns_type())) && diff --git a/discovery/mdns/mdns_writer.h b/discovery/mdns/mdns_writer.h index 3b8a3b08..8dad9f06 100644 --- a/discovery/mdns/mdns_writer.h +++ b/discovery/mdns/mdns_writer.h @@ -5,7 +5,9 @@ #ifndef DISCOVERY_MDNS_MDNS_WRITER_H_ #define DISCOVERY_MDNS_MDNS_WRITER_H_ +#include <string> #include <unordered_map> +#include <vector> #include "discovery/mdns/mdns_records.h" #include "util/big_endian.h" @@ -32,6 +34,7 @@ class MdnsWriter : public BigEndianWriter { bool Write(const PtrRecordRdata& rdata); bool Write(const TxtRecordRdata& rdata); bool Write(const NsecRecordRdata& rdata); + bool Write(const OptRecordRdata& rdata); // Writes a DNS resource record with its RDATA. // The correct type of RDATA to be written is contained in the type // specified in the record. diff --git a/discovery/mdns/public/mdns_constants.h b/discovery/mdns/public/mdns_constants.h index fd5f00f4..ecaa18bb 100644 --- a/discovery/mdns/public/mdns_constants.h +++ b/discovery/mdns/public/mdns_constants.h @@ -303,6 +303,7 @@ enum class DnsType : uint16_t { kTXT = 16, kAAAA = 28, kSRV = 33, + kOPT = 41, kNSEC = 47, kANY = 255, // Only allowed for QTYPE }; @@ -319,6 +320,8 @@ inline std::ostream& operator<<(std::ostream& output, DnsType type) { return output << "AAAA"; case DnsType::kSRV: return output << "SRV"; + case DnsType::kOPT: + return output << "OPT"; case DnsType::kNSEC: return output << "NSEC"; case DnsType::kANY: @@ -435,6 +438,26 @@ constexpr Clock::duration kDelayBetweenProbeQueries = // requests 3 times before treating the probe as completed. constexpr int kProbeIterationCountBeforeSuccess = 3; +// ============================================================================ +// OPT Pseudo-Record Constants +// ============================================================================ + +// For OPT records, the TTL field has been re-purposed as follows: +// +// +0 (MSB) +1 (LSB) +// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +// 0: | EXTENDED-RCODE | VERSION | +// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ +// 2: | DO| Z | +// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + +constexpr uint32_t kExtendedRcodeMask = 0xFF000000; +constexpr int kExtendedRcodeShift = 24; +constexpr uint32_t kVersionMask = 0x00FF0000; +constexpr int kVersionShift = 16; +constexpr uint32_t kDnssecOkBitMask = 0x00008000; +constexpr uint8_t kVersionBadvers = 0x10; + } // namespace discovery } // namespace openscreen |