aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Keane <rwkeane@google.com>2020-06-18 20:41:58 -0700
committerCommit Bot <commit-bot@chromium.org>2020-06-19 04:12:20 +0000
commitec44eb332577b1f3b8865fe78abec5c05c7727bf (patch)
tree43f472baf45fda386a67e3bb68584a9780fdef41
parent1cdd473e5bca0322bff2e2a8cd975389b02da3cf (diff)
downloadopenscreen-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.cc15
-rw-r--r--discovery/mdns/mdns_publisher.h8
-rw-r--r--discovery/mdns/mdns_querier.cc12
-rw-r--r--discovery/mdns/mdns_querier.h12
-rw-r--r--discovery/mdns/mdns_records.cc151
-rw-r--r--discovery/mdns/mdns_records.h84
-rw-r--r--discovery/mdns/mdns_records_unittest.cc62
-rw-r--r--discovery/mdns/mdns_responder.cc19
-rw-r--r--discovery/mdns/mdns_writer.cc11
-rw-r--r--discovery/mdns/mdns_writer.h3
-rw-r--r--discovery/mdns/public/mdns_constants.h23
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