Add the multicast DNS message format.
This CL adds the utilities to generate and parse mDNS messages (RFC 1035
and RFC 6762).
TBR=phoglund@webrtc.org
Bug: webrtc:9605
Change-Id: Id6121c17926887cd3a41a2dfc829462fd15f3a4c
Reviewed-on: https://webrtc-review.googlesource.com/93241
Commit-Queue: Qingsi Wang <qingsi@google.com>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Qingsi Wang <qingsi@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Alex Loiko <aleloi@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24505}
diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn
index f8f70d9..74a124c 100644
--- a/p2p/BUILD.gn
+++ b/p2p/BUILD.gn
@@ -31,6 +31,8 @@
"base/dtlstransportinternal.h",
"base/icetransportinternal.cc",
"base/icetransportinternal.h",
+ "base/mdns_message.cc",
+ "base/mdns_message.h",
"base/p2pconstants.cc",
"base/p2pconstants.h",
"base/p2ptransportchannel.cc",
@@ -150,6 +152,7 @@
"base/asyncstuntcpsocket_unittest.cc",
"base/basicasyncresolverfactory_unittest.cc",
"base/dtlstransport_unittest.cc",
+ "base/mdns_message_unittest.cc",
"base/p2ptransportchannel_unittest.cc",
"base/packetlossestimator_unittest.cc",
"base/port_unittest.cc",
diff --git a/p2p/base/mdns_message.cc b/p2p/base/mdns_message.cc
new file mode 100644
index 0000000..61af6f3
--- /dev/null
+++ b/p2p/base/mdns_message.cc
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2018 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "p2p/base/mdns_message.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/nethelpers.h"
+#include "rtc_base/stringencode.h"
+
+namespace webrtc {
+
+namespace {
+// RFC 1035, Section 4.1.1.
+//
+// QR bit.
+constexpr uint16_t kMDnsFlagMaskQueryOrResponse = 0x8000;
+// AA bit.
+constexpr uint16_t kMDnsFlagMaskAuthoritative = 0x0400;
+// RFC 1035, Section 4.1.2, QCLASS and RFC 6762, Section 18.12, repurposing of
+// top bit of QCLASS as the unicast response bit.
+constexpr uint16_t kMDnsQClassMaskUnicastResponse = 0x8000;
+constexpr size_t kMDnsHeaderSizeBytes = 12;
+
+bool ReadDomainName(MessageBufferReader* buf, std::string* name) {
+ size_t name_start_pos = buf->CurrentOffset();
+ uint8_t label_length;
+ if (!buf->ReadUInt8(&label_length)) {
+ return false;
+ }
+ // RFC 1035, Section 4.1.4.
+ //
+ // If the first two bits of the length octet are ones, the name is compressed
+ // and the rest six bits with the next octet denotes its position in the
+ // message by the offset from the start of the message.
+ auto is_pointer = [](uint8_t octet) {
+ return (octet & 0x80) && (octet & 0x40);
+ };
+ while (label_length && !is_pointer(label_length)) {
+ // RFC 1035, Section 2.3.1, labels are restricted to 63 octets or less.
+ if (label_length > 63) {
+ return false;
+ }
+ std::string label;
+ if (!buf->ReadString(&label, label_length)) {
+ return false;
+ }
+ (*name) += label + ".";
+ if (!buf->ReadUInt8(&label_length)) {
+ return false;
+ }
+ }
+ if (is_pointer(label_length)) {
+ uint8_t next_octet;
+ if (!buf->ReadUInt8(&next_octet)) {
+ return false;
+ }
+ size_t pos_jump_to = ((label_length & 0x3f) << 8) | next_octet;
+ // A legitimate pointer only refers to a prior occurrence of the same name,
+ // and we should only move strictly backward to a prior name field after the
+ // header.
+ if (pos_jump_to >= name_start_pos || pos_jump_to < kMDnsHeaderSizeBytes) {
+ return false;
+ }
+ MessageBufferReader new_buf(buf->MessageData(), buf->MessageLength());
+ if (!new_buf.Consume(pos_jump_to)) {
+ return false;
+ }
+ return ReadDomainName(&new_buf, name);
+ }
+ return true;
+}
+
+void WriteDomainName(rtc::ByteBufferWriter* buf, const std::string& name) {
+ std::vector<std::string> labels;
+ rtc::tokenize(name, '.', &labels);
+ for (const auto& label : labels) {
+ buf->WriteUInt8(label.length());
+ buf->WriteString(label);
+ }
+ buf->WriteUInt8(0);
+}
+
+} // namespace
+
+void MDnsHeader::SetQueryOrResponse(bool is_query) {
+ if (is_query) {
+ flags &= ~kMDnsFlagMaskQueryOrResponse;
+ } else {
+ flags |= kMDnsFlagMaskQueryOrResponse;
+ }
+}
+
+void MDnsHeader::SetAuthoritative(bool is_authoritative) {
+ if (is_authoritative) {
+ flags |= kMDnsFlagMaskAuthoritative;
+ } else {
+ flags &= ~kMDnsFlagMaskAuthoritative;
+ }
+}
+
+bool MDnsHeader::IsAuthoritative() const {
+ return flags & kMDnsFlagMaskAuthoritative;
+}
+
+bool MDnsHeader::Read(MessageBufferReader* buf) {
+ if (!buf->ReadUInt16(&id) || !buf->ReadUInt16(&flags) ||
+ !buf->ReadUInt16(&qdcount) || !buf->ReadUInt16(&ancount) ||
+ !buf->ReadUInt16(&nscount) || !buf->ReadUInt16(&arcount)) {
+ RTC_LOG(LS_ERROR) << "Invalid mDNS header.";
+ return false;
+ }
+ return true;
+}
+
+void MDnsHeader::Write(rtc::ByteBufferWriter* buf) const {
+ buf->WriteUInt16(id);
+ buf->WriteUInt16(flags);
+ buf->WriteUInt16(qdcount);
+ buf->WriteUInt16(ancount);
+ buf->WriteUInt16(nscount);
+ buf->WriteUInt16(arcount);
+}
+
+bool MDnsHeader::IsQuery() const {
+ return !(flags & kMDnsFlagMaskQueryOrResponse);
+}
+
+MDnsSectionEntry::MDnsSectionEntry() = default;
+MDnsSectionEntry::~MDnsSectionEntry() = default;
+MDnsSectionEntry::MDnsSectionEntry(const MDnsSectionEntry& other) = default;
+
+void MDnsSectionEntry::SetType(SectionEntryType type) {
+ switch (type) {
+ case SectionEntryType::kA:
+ type_ = 1;
+ return;
+ case SectionEntryType::kAAAA:
+ type_ = 28;
+ return;
+ default:
+ RTC_NOTREACHED();
+ }
+}
+
+SectionEntryType MDnsSectionEntry::GetType() const {
+ switch (type_) {
+ case 1:
+ return SectionEntryType::kA;
+ case 28:
+ return SectionEntryType::kAAAA;
+ default:
+ return SectionEntryType::kUnsupported;
+ }
+}
+
+void MDnsSectionEntry::SetClass(SectionEntryClass cls) {
+ switch (cls) {
+ case SectionEntryClass::kIN:
+ class_ = 1;
+ return;
+ default:
+ RTC_NOTREACHED();
+ }
+}
+
+SectionEntryClass MDnsSectionEntry::GetClass() const {
+ switch (class_) {
+ case 1:
+ return SectionEntryClass::kIN;
+ default:
+ return SectionEntryClass::kUnsupported;
+ }
+}
+
+MDnsQuestion::MDnsQuestion() = default;
+MDnsQuestion::MDnsQuestion(const MDnsQuestion& other) = default;
+MDnsQuestion::~MDnsQuestion() = default;
+
+bool MDnsQuestion::Read(MessageBufferReader* buf) {
+ if (!ReadDomainName(buf, &name_)) {
+ RTC_LOG(LS_ERROR) << "Invalid name.";
+ return false;
+ }
+ if (!buf->ReadUInt16(&type_) || !buf->ReadUInt16(&class_)) {
+ RTC_LOG(LS_ERROR) << "Invalid type and class.";
+ return false;
+ }
+ return true;
+}
+
+bool MDnsQuestion::Write(rtc::ByteBufferWriter* buf) const {
+ WriteDomainName(buf, name_);
+ buf->WriteUInt16(type_);
+ buf->WriteUInt16(class_);
+ return true;
+}
+
+void MDnsQuestion::SetUnicastResponse(bool should_unicast) {
+ if (should_unicast) {
+ class_ |= kMDnsQClassMaskUnicastResponse;
+ } else {
+ class_ &= ~kMDnsQClassMaskUnicastResponse;
+ }
+}
+
+bool MDnsQuestion::ShouldUnicastResponse() const {
+ return class_ & kMDnsQClassMaskUnicastResponse;
+}
+
+MDnsResourceRecord::MDnsResourceRecord() = default;
+MDnsResourceRecord::MDnsResourceRecord(const MDnsResourceRecord& other) =
+ default;
+MDnsResourceRecord::~MDnsResourceRecord() = default;
+
+bool MDnsResourceRecord::Read(MessageBufferReader* buf) {
+ if (!ReadDomainName(buf, &name_)) {
+ return false;
+ }
+ if (!buf->ReadUInt16(&type_) || !buf->ReadUInt16(&class_) ||
+ !buf->ReadUInt32(&ttl_seconds_) || !buf->ReadUInt16(&rdlength_)) {
+ return false;
+ }
+
+ switch (GetType()) {
+ case SectionEntryType::kA:
+ return ReadARData(buf);
+ case SectionEntryType::kAAAA:
+ return ReadQuadARData(buf);
+ case SectionEntryType::kUnsupported:
+ return false;
+ default:
+ RTC_NOTREACHED();
+ }
+ return false;
+}
+bool MDnsResourceRecord::ReadARData(MessageBufferReader* buf) {
+ // A RDATA contains a 32-bit IPv4 address.
+ return buf->ReadString(&rdata_, 4);
+}
+
+bool MDnsResourceRecord::ReadQuadARData(MessageBufferReader* buf) {
+ // AAAA RDATA contains a 128-bit IPv6 address.
+ return buf->ReadString(&rdata_, 16);
+}
+
+bool MDnsResourceRecord::Write(rtc::ByteBufferWriter* buf) const {
+ WriteDomainName(buf, name_);
+ buf->WriteUInt16(type_);
+ buf->WriteUInt16(class_);
+ buf->WriteUInt32(ttl_seconds_);
+ buf->WriteUInt16(rdlength_);
+ switch (GetType()) {
+ case SectionEntryType::kA:
+ WriteARData(buf);
+ return true;
+ case SectionEntryType::kAAAA:
+ WriteQuadARData(buf);
+ return true;
+ case SectionEntryType::kUnsupported:
+ return false;
+ default:
+ RTC_NOTREACHED();
+ }
+ return true;
+}
+
+void MDnsResourceRecord::WriteARData(rtc::ByteBufferWriter* buf) const {
+ buf->WriteString(rdata_);
+}
+
+void MDnsResourceRecord::WriteQuadARData(rtc::ByteBufferWriter* buf) const {
+ buf->WriteString(rdata_);
+}
+
+bool MDnsResourceRecord::SetIPAddressInRecordData(
+ const rtc::IPAddress& address) {
+ int af = address.family();
+ if (af != AF_INET && af != AF_INET6) {
+ return false;
+ }
+ char out[16] = {0};
+ if (!rtc::inet_pton(af, address.ToString().c_str(), out)) {
+ return false;
+ }
+ rdlength_ = (af == AF_INET) ? 4 : 16;
+ rdata_ = std::string(out, rdlength_);
+ return true;
+}
+
+bool MDnsResourceRecord::GetIPAddressFromRecordData(
+ rtc::IPAddress* address) const {
+ if (GetType() != SectionEntryType::kA &&
+ GetType() != SectionEntryType::kAAAA) {
+ return false;
+ }
+ if (rdata_.size() != 4 && rdata_.size() != 16) {
+ return false;
+ }
+ char out[INET6_ADDRSTRLEN] = {0};
+ int af = (GetType() == SectionEntryType::kA) ? AF_INET : AF_INET6;
+ if (!rtc::inet_ntop(af, rdata_.data(), out, sizeof(out))) {
+ return false;
+ }
+ return rtc::IPFromString(std::string(out), address);
+}
+
+MDnsMessage::MDnsMessage() = default;
+MDnsMessage::~MDnsMessage() = default;
+
+bool MDnsMessage::Read(MessageBufferReader* buf) {
+ RTC_DCHECK_EQ(0u, buf->CurrentOffset());
+ if (!header_.Read(buf)) {
+ return false;
+ }
+
+ auto read_question = [&buf](std::vector<MDnsQuestion>* section,
+ uint16_t count) {
+ section->resize(count);
+ for (auto& question : (*section)) {
+ if (!question.Read(buf)) {
+ return false;
+ }
+ }
+ return true;
+ };
+ auto read_rr = [&buf](std::vector<MDnsResourceRecord>* section,
+ uint16_t count) {
+ section->resize(count);
+ for (auto& rr : (*section)) {
+ if (!rr.Read(buf)) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ if (!read_question(&question_section_, header_.qdcount) ||
+ !read_rr(&answer_section_, header_.ancount) ||
+ !read_rr(&authority_section_, header_.nscount) ||
+ !read_rr(&additional_section_, header_.arcount)) {
+ return false;
+ }
+ return true;
+}
+
+bool MDnsMessage::Write(rtc::ByteBufferWriter* buf) const {
+ header_.Write(buf);
+
+ auto write_rr = [&buf](const std::vector<MDnsResourceRecord>& section) {
+ for (auto rr : section) {
+ if (!rr.Write(buf)) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ for (auto question : question_section_) {
+ if (!question.Write(buf)) {
+ return false;
+ }
+ }
+ if (!write_rr(answer_section_) || !write_rr(authority_section_) ||
+ !write_rr(additional_section_)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool MDnsMessage::ShouldUnicastResponse() const {
+ bool should_unicast = false;
+ for (const auto& question : question_section_) {
+ should_unicast |= question.ShouldUnicastResponse();
+ }
+ return should_unicast;
+}
+
+void MDnsMessage::AddQuestion(const MDnsQuestion& question) {
+ question_section_.push_back(question);
+ header_.qdcount = question_section_.size();
+}
+
+void MDnsMessage::AddAnswerRecord(const MDnsResourceRecord& answer) {
+ answer_section_.push_back(answer);
+ header_.ancount = answer_section_.size();
+}
+
+} // namespace webrtc
diff --git a/p2p/base/mdns_message.h b/p2p/base/mdns_message.h
new file mode 100644
index 0000000..f6dec20
--- /dev/null
+++ b/p2p/base/mdns_message.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2018 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef P2P_BASE_MDNS_MESSAGE_H_
+#define P2P_BASE_MDNS_MESSAGE_H_
+
+// This file contains classes to read and write mDNSs message defined in RFC
+// 6762 and RFC 1025 (DNS messages). Note that it is recommended by RFC 6762 to
+// use the name compression scheme defined in RFC 1035 whenever possible. We
+// currently only implement the capability of reading compressed names in mDNS
+// messages in MDnsMessage::Read(); however, the MDnsMessage::Write() does not
+// support name compression yet.
+//
+// Fuzzer tests (test/fuzzers/mdns_parser_fuzzer.cc) MUST always be performed
+// after changes made to this file.
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "rtc_base/bytebuffer.h"
+#include "rtc_base/ipaddress.h"
+#include "rtc_base/message_buffer_reader.h"
+
+namespace webrtc {
+
+// We use "section entry" to denote either a question or a resource record.
+//
+// RFC 1035 Section 3.2.2.
+enum class SectionEntryType {
+ kA,
+ kAAAA,
+ // Only the above types are processed in the current implementation.
+ kUnsupported,
+};
+
+// RFC 1035 Section 3.2.4.
+enum class SectionEntryClass {
+ kIN,
+ kUnsupported,
+};
+
+// RFC 1035, Section 4.1.1.
+class MDnsHeader final {
+ public:
+ bool Read(MessageBufferReader* buf);
+ void Write(rtc::ByteBufferWriter* buf) const;
+
+ void SetQueryOrResponse(bool is_query);
+ bool IsQuery() const;
+ void SetAuthoritative(bool is_authoritative);
+ bool IsAuthoritative() const;
+
+ uint16_t id = 0;
+ uint16_t flags = 0;
+ // Number of entries in the question section.
+ uint16_t qdcount = 0;
+ // Number of resource records in the answer section.
+ uint16_t ancount = 0;
+ // Number of name server resource records in the authority records section.
+ uint16_t nscount = 0;
+ // Number of resource records in the additional records section.
+ uint16_t arcount = 0;
+};
+
+// Entries in each section after the header share a common structure. Note that
+// this is not a concept defined in RFC 1035.
+class MDnsSectionEntry {
+ public:
+ MDnsSectionEntry();
+ MDnsSectionEntry(const MDnsSectionEntry& other);
+ virtual ~MDnsSectionEntry();
+ virtual bool Read(MessageBufferReader* buf) = 0;
+ virtual bool Write(rtc::ByteBufferWriter* buf) const = 0;
+
+ void SetName(const std::string& name) { name_ = name; }
+ // Returns the fully qualified domain name in the section entry, i.e., QNAME
+ // in a question or NAME in a resource record.
+ std::string GetName() const { return name_; }
+
+ void SetType(SectionEntryType type);
+ SectionEntryType GetType() const;
+ void SetClass(SectionEntryClass cls);
+ SectionEntryClass GetClass() const;
+
+ protected:
+ std::string name_; // Fully qualified domain name.
+ uint16_t type_ = 0;
+ uint16_t class_ = 0;
+};
+
+// RFC 1035, Section 4.1.2.
+class MDnsQuestion final : public MDnsSectionEntry {
+ public:
+ MDnsQuestion();
+ MDnsQuestion(const MDnsQuestion& other);
+ ~MDnsQuestion() override;
+
+ bool Read(MessageBufferReader* buf) override;
+ bool Write(rtc::ByteBufferWriter* buf) const override;
+
+ void SetUnicastResponse(bool should_unicast);
+ bool ShouldUnicastResponse() const;
+};
+
+// RFC 1035, Section 4.1.3.
+class MDnsResourceRecord final : public MDnsSectionEntry {
+ public:
+ MDnsResourceRecord();
+ MDnsResourceRecord(const MDnsResourceRecord& other);
+ ~MDnsResourceRecord() override;
+
+ bool Read(MessageBufferReader* buf) override;
+ bool Write(rtc::ByteBufferWriter* buf) const override;
+
+ void SetTtlSeconds(uint32_t ttl_seconds) { ttl_seconds_ = ttl_seconds; }
+ uint32_t GetTtlSeconds() const { return ttl_seconds_; }
+ // Returns true if |address| is in the address family AF_INET or AF_INET6 and
+ // |address| has a valid IPv4 or IPv6 address; false otherwise.
+ bool SetIPAddressInRecordData(const rtc::IPAddress& address);
+ // Returns true if the record is of type A or AAAA and the record has a valid
+ // IPv4 or IPv6 address; false otherwise. Stores the valid IP in |address|.
+ bool GetIPAddressFromRecordData(rtc::IPAddress* address) const;
+
+ private:
+ // The list of methods reading and writing rdata can grow as we support more
+ // types of rdata.
+ bool ReadARData(MessageBufferReader* buf);
+ void WriteARData(rtc::ByteBufferWriter* buf) const;
+
+ bool ReadQuadARData(MessageBufferReader* buf);
+ void WriteQuadARData(rtc::ByteBufferWriter* buf) const;
+
+ uint32_t ttl_seconds_ = 0;
+ uint16_t rdlength_ = 0;
+ std::string rdata_;
+};
+
+class MDnsMessage final {
+ public:
+ // RFC 1035, Section 4.1.
+ enum class Section { kQuestion, kAnswer, kAuthority, kAdditional };
+
+ MDnsMessage();
+ ~MDnsMessage();
+ // Reads the mDNS message in |buf| and populates the corresponding fields in
+ // MDnsMessage.
+ bool Read(MessageBufferReader* buf);
+ // Write an mDNS message to |buf| based on the fields in MDnsMessage.
+ //
+ // TODO(qingsi): Implement name compression when writing mDNS messages.
+ bool Write(rtc::ByteBufferWriter* buf) const;
+
+ void SetId(uint16_t id) { header_.id = id; }
+ uint16_t GetId() const { return header_.id; }
+
+ void SetQueryOrResponse(bool is_query) {
+ header_.SetQueryOrResponse(is_query);
+ }
+ bool IsQuery() const { return header_.IsQuery(); }
+
+ void SetAuthoritative(bool is_authoritative) {
+ header_.SetAuthoritative(is_authoritative);
+ }
+ bool IsAuthoritative() const { return header_.IsAuthoritative(); }
+
+ // Returns true if the message is a query and the unicast response is
+ // preferred. False otherwise.
+ bool ShouldUnicastResponse() const;
+
+ void AddQuestion(const MDnsQuestion& question);
+ // TODO(qingsi): Implement AddXRecord for name server and additional records.
+ void AddAnswerRecord(const MDnsResourceRecord& answer);
+
+ const std::vector<MDnsQuestion>& question_section() const {
+ return question_section_;
+ }
+ const std::vector<MDnsResourceRecord>& answer_section() const {
+ return answer_section_;
+ }
+ const std::vector<MDnsResourceRecord>& authority_section() const {
+ return authority_section_;
+ }
+ const std::vector<MDnsResourceRecord>& additional_section() const {
+ return additional_section_;
+ }
+
+ private:
+ MDnsHeader header_;
+ std::vector<MDnsQuestion> question_section_;
+ std::vector<MDnsResourceRecord> answer_section_;
+ std::vector<MDnsResourceRecord> authority_section_;
+ std::vector<MDnsResourceRecord> additional_section_;
+};
+
+} // namespace webrtc
+
+#endif // P2P_BASE_MDNS_MESSAGE_H_
diff --git a/p2p/base/mdns_message_unittest.cc b/p2p/base/mdns_message_unittest.cc
new file mode 100644
index 0000000..fb4a3b1
--- /dev/null
+++ b/p2p/base/mdns_message_unittest.cc
@@ -0,0 +1,570 @@
+/*
+ * Copyright 2018 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "p2p/base/mdns_message.h"
+#include "rtc_base/bytebuffer.h"
+#include "rtc_base/gunit.h"
+#include "rtc_base/ipaddress.h"
+#include "rtc_base/socketaddress.h"
+#include "test/gmock.h"
+
+#define ReadMDnsMessage(X, Y) ReadMDnsMessageTestCase(X, Y, sizeof(Y))
+#define WriteMDnsMessageAndCompare(X, Y) \
+ WriteMDnsMessageAndCompareWithTestCast(X, Y, sizeof(Y))
+
+using ::testing::ElementsAre;
+using ::testing::Pair;
+using ::testing::UnorderedElementsAre;
+
+namespace webrtc {
+
+namespace {
+
+const uint8_t kSingleQuestionForIPv4AddrWithUnicastResponse[] = {
+ 0x12, 0x34, // ID
+ 0x00, 0x00, // flags
+ 0x00, 0x01, // number of questions
+ 0x00, 0x00, // number of answer rr
+ 0x00, 0x00, // number of name server rr
+ 0x00, 0x00, // number of additional rr
+ 0x06, 0x77, 0x65, 0x62, 0x72, 0x74, 0x63, // webrtc
+ 0x03, 0x6f, 0x72, 0x67, // org
+ 0x00, // null label
+ 0x00, 0x01, // type A Record
+ 0x80, 0x01, // class IN, unicast response
+};
+
+const uint8_t kTwoQuestionsForIPv4AndIPv6AddrWithMulticastResponse[] = {
+ 0x12, 0x34, // ID
+ 0x00, 0x00, // flags
+ 0x00, 0x02, // number of questions
+ 0x00, 0x00, // number of answer rr
+ 0x00, 0x00, // number of name server rr
+ 0x00, 0x00, // number of additional rr
+ 0x07, 0x77, 0x65, 0x62, 0x72, 0x74, 0x63, 0x34, // webrtc4
+ 0x03, 0x6f, 0x72, 0x67, // org
+ 0x00, // null label
+ 0x00, 0x01, // type A Record
+ 0x00, 0x01, // class IN, multicast response
+ 0x07, 0x77, 0x65, 0x62, 0x72, 0x74, 0x63, 0x36, // webrtc6
+ 0x03, 0x6f, 0x72, 0x67, // org
+ 0x00, // null label
+ 0x00, 0x1C, // type AAAA Record
+ 0x00, 0x01, // class IN, multicast response
+};
+
+const uint8_t
+ kTwoQuestionsForIPv4AndIPv6AddrWithMulticastResponseAndNameCompression[] = {
+ 0x12, 0x34, // ID
+ 0x00, 0x00, // flags
+ 0x00, 0x02, // number of questions
+ 0x00, 0x00, // number of answer rr
+ 0x00, 0x00, // number of name server rr
+ 0x00, 0x00, // number of additional rr
+ 0x03, 0x77, 0x77, 0x77, // www
+ 0x06, 0x77, 0x65, 0x62, 0x72, 0x74, 0x63, // webrtc
+ 0x03, 0x6f, 0x72, 0x67, // org
+ 0x00, // null label
+ 0x00, 0x01, // type A Record
+ 0x00, 0x01, // class IN, multicast response
+ 0x04, 0x6d, 0x64, 0x6e, 0x73, // mdns
+ 0xc0, 0x10, // offset 16, webrtc.org.
+ 0x00, 0x1C, // type AAAA Record
+ 0x00, 0x01, // class IN, multicast response
+};
+
+const uint8_t kThreeQuestionsWithTwoPointersToTheSameNameSuffix[] = {
+ 0x12, 0x34, // ID
+ 0x00, 0x00, // flags
+ 0x00, 0x03, // number of questions
+ 0x00, 0x00, // number of answer rr
+ 0x00, 0x00, // number of name server rr
+ 0x00, 0x00, // number of additional rr
+ 0x03, 0x77, 0x77, 0x77, // www
+ 0x06, 0x77, 0x65, 0x62, 0x72, 0x74, 0x63, // webrtc
+ 0x03, 0x6f, 0x72, 0x67, // org
+ 0x00, // null label
+ 0x00, 0x01, // type A Record
+ 0x00, 0x01, // class IN, multicast response
+ 0x04, 0x6d, 0x64, 0x6e, 0x73, // mdns
+ 0xc0, 0x10, // offset 16, webrtc.org.
+ 0x00, 0x1C, // type AAAA Record
+ 0x00, 0x01, // class IN, multicast response
+ 0xc0, 0x10, // offset 16, webrtc.org.
+ 0x00, 0x01, // type A Record
+ 0x00, 0x01, // class IN, multicast response
+};
+
+const uint8_t kThreeQuestionsWithPointerToNameSuffixContainingAnotherPointer[] =
+ {
+ 0x12, 0x34, // ID
+ 0x00, 0x00, // flags
+ 0x00, 0x03, // number of questions
+ 0x00, 0x00, // number of answer rr
+ 0x00, 0x00, // number of name server rr
+ 0x00, 0x00, // number of additional rr
+ 0x03, 0x77, 0x77, 0x77, // www
+ 0x06, 0x77, 0x65, 0x62, 0x72, 0x74, 0x63, // webrtc
+ 0x03, 0x6f, 0x72, 0x67, // org
+ 0x00, // null label
+ 0x00, 0x01, // type A Record
+ 0x00, 0x01, // class IN, multicast response
+ 0x04, 0x6d, 0x64, 0x6e, 0x73, // mdns
+ 0xc0, 0x10, // offset 16, webrtc.org.
+ 0x00, 0x1C, // type AAAA Record
+ 0x00, 0x01, // class IN, multicast response
+ 0x03, 0x77, 0x77, 0x77, // www
+ 0xc0, 0x20, // offset 32, mdns.webrtc.org.
+ 0x00, 0x01, // type A Record
+ 0x00, 0x01, // class IN, multicast response
+};
+
+const uint8_t kCorruptedQuestionWithNameCompression1[] = {
+ 0x12, 0x34, // ID
+ 0x84, 0x00, // flags
+ 0x00, 0x01, // number of questions
+ 0x00, 0x00, // number of answer rr
+ 0x00, 0x00, // number of name server rr
+ 0x00, 0x00, // number of additional rr
+ 0xc0, 0x0c, // offset 12,
+ 0x00, 0x01, // type A Record
+ 0x00, 0x01, // class IN
+};
+
+const uint8_t kCorruptedQuestionWithNameCompression2[] = {
+ 0x12, 0x34, // ID
+ 0x84, 0x00, // flags
+ 0x00, 0x01, // number of questions
+ 0x00, 0x00, // number of answer rr
+ 0x00, 0x00, // number of name server rr
+ 0x00, 0x00, // number of additional rr
+ 0x01, 0x77, // w
+ 0xc0, 0x0c, // offset 12,
+ 0x00, 0x01, // type A Record
+ 0x00, 0x01, // class IN
+};
+
+const uint8_t kSingleAuthoritativeAnswerWithIPv4Addr[] = {
+ 0x12, 0x34, // ID
+ 0x84, 0x00, // flags
+ 0x00, 0x00, // number of questions
+ 0x00, 0x01, // number of answer rr
+ 0x00, 0x00, // number of name server rr
+ 0x00, 0x00, // number of additional rr
+ 0x06, 0x77, 0x65, 0x62, 0x72, 0x74, 0x63, // webrtc
+ 0x03, 0x6f, 0x72, 0x67, // org
+ 0x00, // null label
+ 0x00, 0x01, // type A Record
+ 0x00, 0x01, // class IN
+ 0x00, 0x00, 0x00, 0x78, // TTL, 120 seconds
+ 0x00, 0x04, // rdlength, 32 bits
+ 0xC0, 0xA8, 0x00, 0x01, // 192.168.0.1
+};
+
+const uint8_t kTwoAuthoritativeAnswersWithIPv4AndIPv6Addr[] = {
+ 0x12, 0x34, // ID
+ 0x84, 0x00, // flags
+ 0x00, 0x00, // number of questions
+ 0x00, 0x02, // number of answer rr
+ 0x00, 0x00, // number of name server rr
+ 0x00, 0x00, // number of additional rr
+ 0x07, 0x77, 0x65, 0x62, 0x72, 0x74, 0x63, 0x34, // webrtc4
+ 0x03, 0x6f, 0x72, 0x67, // org
+ 0x00, // null label
+ 0x00, 0x01, // type A Record
+ 0x00, 0x01, // class IN
+ 0x00, 0x00, 0x00, 0x3c, // TTL, 60 seconds
+ 0x00, 0x04, // rdlength, 32 bits
+ 0xC0, 0xA8, 0x00, 0x01, // 192.168.0.1
+ 0x07, 0x77, 0x65, 0x62, 0x72, 0x74, 0x63, 0x36, // webrtc6
+ 0x03, 0x6f, 0x72, 0x67, // org
+ 0x00, // null label
+ 0x00, 0x1C, // type AAAA Record
+ 0x00, 0x01, // class IN
+ 0x00, 0x00, 0x00, 0x78, // TTL, 120 seconds
+ 0x00, 0x10, // rdlength, 128 bits
+ 0xfd, 0x12, 0x34, 0x56, 0x78, 0x9a, 0x00, 0x01, // fd12:3456:789a:1::1
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+};
+
+const uint8_t kTwoAuthoritativeAnswersWithIPv4AndIPv6AddrWithNameCompression[] =
+ {
+ 0x12, 0x34, // ID
+ 0x84, 0x00, // flags
+ 0x00, 0x00, // number of questions
+ 0x00, 0x02, // number of answer rr
+ 0x00, 0x00, // number of name server rr
+ 0x00, 0x00, // number of additional rr
+ 0x03, 0x77, 0x77, 0x77, // www
+ 0x06, 0x77, 0x65, 0x62, 0x72, 0x74, 0x63, // webrtc
+ 0x03, 0x6f, 0x72, 0x67, // org
+ 0x00, // null label
+ 0x00, 0x01, // type A Record
+ 0x00, 0x01, // class IN
+ 0x00, 0x00, 0x00, 0x3c, // TTL, 60 seconds
+ 0x00, 0x04, // rdlength, 32 bits
+ 0xc0, 0xA8, 0x00, 0x01, // 192.168.0.1
+ 0xc0, 0x10, // offset 16, webrtc.org.
+ 0x00, 0x1C, // type AAAA Record
+ 0x00, 0x01, // class IN
+ 0x00, 0x00, 0x00, 0x78, // TTL, 120 seconds
+ 0x00, 0x10, // rdlength, 128 bits
+ 0xfd, 0x12, 0x34, 0x56, 0x78, 0x9a, 0x00, 0x01, // fd12:3456:789a:1::1
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+};
+
+const uint8_t kCorruptedAnswerWithNameCompression1[] = {
+ 0x12, 0x34, // ID
+ 0x84, 0x00, // flags
+ 0x00, 0x00, // number of questions
+ 0x00, 0x01, // number of answer rr
+ 0x00, 0x00, // number of name server rr
+ 0x00, 0x00, // number of additional rr
+ 0xc0, 0x0c, // offset 12,
+ 0x00, 0x01, // type A Record
+ 0x00, 0x01, // class IN
+ 0x00, 0x00, 0x00, 0x3c, // TTL, 60 seconds
+ 0x00, 0x04, // rdlength, 32 bits
+ 0xc0, 0xA8, 0x00, 0x01, // 192.168.0.1
+};
+
+const uint8_t kCorruptedAnswerWithNameCompression2[] = {
+ 0x12, 0x34, // ID
+ 0x84, 0x00, // flags
+ 0x00, 0x00, // number of questions
+ 0x00, 0x01, // number of answer rr
+ 0x00, 0x00, // number of name server rr
+ 0x00, 0x00, // number of additional rr
+ 0x01, 0x77, // w
+ 0xc0, 0x0c, // offset 12,
+ 0x00, 0x01, // type A Record
+ 0x00, 0x01, // class IN
+ 0x00, 0x00, 0x00, 0x3c, // TTL, 60 seconds
+ 0x00, 0x04, // rdlength, 32 bits
+ 0xc0, 0xA8, 0x00, 0x01, // 192.168.0.1
+};
+
+bool ReadMDnsMessageTestCase(MDnsMessage* msg,
+ const uint8_t* testcase,
+ size_t size) {
+ MessageBufferReader buf(reinterpret_cast<const char*>(testcase), size);
+ return msg->Read(&buf);
+}
+
+void WriteMDnsMessageAndCompareWithTestCast(MDnsMessage* msg,
+ const uint8_t* testcase,
+ size_t size) {
+ rtc::ByteBufferWriter out;
+ EXPECT_TRUE(msg->Write(&out));
+ EXPECT_EQ(size, out.Length());
+ int len = static_cast<int>(out.Length());
+ rtc::ByteBufferReader read_buf(out);
+ std::string bytes;
+ read_buf.ReadString(&bytes, len);
+ std::string testcase_bytes(reinterpret_cast<const char*>(testcase), size);
+ EXPECT_EQ(testcase_bytes, bytes);
+}
+
+bool GetQueriedNames(MDnsMessage* msg, std::set<std::string>* names) {
+ if (!msg->IsQuery() || msg->question_section().empty()) {
+ return false;
+ }
+ for (const auto& question : msg->question_section()) {
+ names->insert(question.GetName());
+ }
+ return true;
+}
+
+bool GetResolution(MDnsMessage* msg,
+ std::map<std::string, rtc::IPAddress>* names) {
+ if (msg->IsQuery() || msg->answer_section().empty()) {
+ return false;
+ }
+ for (const auto& answer : msg->answer_section()) {
+ rtc::IPAddress resolved_addr;
+ if (!answer.GetIPAddressFromRecordData(&resolved_addr)) {
+ return false;
+ }
+ (*names)[answer.GetName()] = resolved_addr;
+ }
+ return true;
+}
+
+} // namespace
+
+TEST(MDnsMessageTest, ReadSingleQuestionForIPv4Address) {
+ MDnsMessage msg;
+ ASSERT_TRUE(
+ ReadMDnsMessage(&msg, kSingleQuestionForIPv4AddrWithUnicastResponse));
+ EXPECT_TRUE(msg.IsQuery());
+ EXPECT_EQ(0x1234, msg.GetId());
+ ASSERT_EQ(1u, msg.question_section().size());
+ EXPECT_EQ(0u, msg.answer_section().size());
+ EXPECT_EQ(0u, msg.authority_section().size());
+ EXPECT_EQ(0u, msg.additional_section().size());
+ EXPECT_TRUE(msg.ShouldUnicastResponse());
+
+ const auto& question = msg.question_section()[0];
+ EXPECT_EQ(SectionEntryType::kA, question.GetType());
+
+ std::set<std::string> queried_names;
+ EXPECT_TRUE(GetQueriedNames(&msg, &queried_names));
+ EXPECT_THAT(queried_names, ElementsAre("webrtc.org."));
+}
+
+TEST(MDnsMessageTest, ReadTwoQuestionsForIPv4AndIPv6Addr) {
+ MDnsMessage msg;
+ ASSERT_TRUE(ReadMDnsMessage(
+ &msg, kTwoQuestionsForIPv4AndIPv6AddrWithMulticastResponse));
+ EXPECT_TRUE(msg.IsQuery());
+ EXPECT_EQ(0x1234, msg.GetId());
+ ASSERT_EQ(2u, msg.question_section().size());
+ EXPECT_EQ(0u, msg.answer_section().size());
+ EXPECT_EQ(0u, msg.authority_section().size());
+ EXPECT_EQ(0u, msg.additional_section().size());
+
+ const auto& question1 = msg.question_section()[0];
+ const auto& question2 = msg.question_section()[1];
+ EXPECT_EQ(SectionEntryType::kA, question1.GetType());
+ EXPECT_EQ(SectionEntryType::kAAAA, question2.GetType());
+
+ std::set<std::string> queried_names;
+ EXPECT_TRUE(GetQueriedNames(&msg, &queried_names));
+ EXPECT_THAT(queried_names,
+ UnorderedElementsAre("webrtc4.org.", "webrtc6.org."));
+}
+
+TEST(MDnsMessageTest, ReadTwoQuestionsForIPv4AndIPv6AddrWithNameCompression) {
+ MDnsMessage msg;
+ ASSERT_TRUE(ReadMDnsMessage(
+ &msg,
+ kTwoQuestionsForIPv4AndIPv6AddrWithMulticastResponseAndNameCompression));
+
+ ASSERT_EQ(2u, msg.question_section().size());
+ const auto& question1 = msg.question_section()[0];
+ const auto& question2 = msg.question_section()[1];
+ EXPECT_EQ(SectionEntryType::kA, question1.GetType());
+ EXPECT_EQ(SectionEntryType::kAAAA, question2.GetType());
+
+ std::set<std::string> queried_names;
+ EXPECT_TRUE(GetQueriedNames(&msg, &queried_names));
+ EXPECT_THAT(queried_names,
+ UnorderedElementsAre("www.webrtc.org.", "mdns.webrtc.org."));
+}
+
+TEST(MDnsMessageTest, ReadThreeQuestionsWithTwoPointersToTheSameNameSuffix) {
+ MDnsMessage msg;
+ ASSERT_TRUE(
+ ReadMDnsMessage(&msg, kThreeQuestionsWithTwoPointersToTheSameNameSuffix));
+
+ ASSERT_EQ(3u, msg.question_section().size());
+ const auto& question1 = msg.question_section()[0];
+ const auto& question2 = msg.question_section()[1];
+ const auto& question3 = msg.question_section()[2];
+ EXPECT_EQ(SectionEntryType::kA, question1.GetType());
+ EXPECT_EQ(SectionEntryType::kAAAA, question2.GetType());
+ EXPECT_EQ(SectionEntryType::kA, question3.GetType());
+
+ std::set<std::string> queried_names;
+ EXPECT_TRUE(GetQueriedNames(&msg, &queried_names));
+ EXPECT_THAT(queried_names,
+ UnorderedElementsAre("www.webrtc.org.", "mdns.webrtc.org.",
+ "webrtc.org."));
+}
+
+TEST(MDnsMessageTest,
+ ReadThreeQuestionsWithPointerToNameSuffixContainingAnotherPointer) {
+ MDnsMessage msg;
+ ASSERT_TRUE(ReadMDnsMessage(
+ &msg, kThreeQuestionsWithPointerToNameSuffixContainingAnotherPointer));
+
+ ASSERT_EQ(3u, msg.question_section().size());
+ const auto& question1 = msg.question_section()[0];
+ const auto& question2 = msg.question_section()[1];
+ const auto& question3 = msg.question_section()[2];
+ EXPECT_EQ(SectionEntryType::kA, question1.GetType());
+ EXPECT_EQ(SectionEntryType::kAAAA, question2.GetType());
+ EXPECT_EQ(SectionEntryType::kA, question3.GetType());
+
+ std::set<std::string> queried_names;
+ EXPECT_TRUE(GetQueriedNames(&msg, &queried_names));
+ EXPECT_THAT(queried_names,
+ UnorderedElementsAre("www.webrtc.org.", "mdns.webrtc.org.",
+ "www.mdns.webrtc.org."));
+}
+
+TEST(MDnsMessageTest,
+ ReadQuestionWithCorruptedPointerInNameCompressionShouldFail) {
+ MDnsMessage msg;
+ EXPECT_FALSE(ReadMDnsMessage(&msg, kCorruptedQuestionWithNameCompression1));
+ EXPECT_FALSE(ReadMDnsMessage(&msg, kCorruptedQuestionWithNameCompression2));
+}
+
+TEST(MDnsMessageTest, ReadSingleAnswerForIPv4Addr) {
+ MDnsMessage msg;
+ ASSERT_TRUE(ReadMDnsMessage(&msg, kSingleAuthoritativeAnswerWithIPv4Addr));
+ EXPECT_FALSE(msg.IsQuery());
+ EXPECT_TRUE(msg.IsAuthoritative());
+ EXPECT_EQ(0x1234, msg.GetId());
+ EXPECT_EQ(0u, msg.question_section().size());
+ ASSERT_EQ(1u, msg.answer_section().size());
+ EXPECT_EQ(0u, msg.authority_section().size());
+ EXPECT_EQ(0u, msg.additional_section().size());
+
+ const auto& answer = msg.answer_section()[0];
+ EXPECT_EQ(SectionEntryType::kA, answer.GetType());
+ EXPECT_EQ(120u, answer.GetTtlSeconds());
+
+ std::map<std::string, rtc::IPAddress> resolution;
+ EXPECT_TRUE(GetResolution(&msg, &resolution));
+ rtc::IPAddress expected_addr(rtc::SocketAddress("192.168.0.1", 0).ipaddr());
+ EXPECT_THAT(resolution, ElementsAre(Pair("webrtc.org.", expected_addr)));
+}
+
+TEST(MDnsMessageTest, ReadTwoAnswersForIPv4AndIPv6Addr) {
+ MDnsMessage msg;
+ ASSERT_TRUE(
+ ReadMDnsMessage(&msg, kTwoAuthoritativeAnswersWithIPv4AndIPv6Addr));
+ EXPECT_FALSE(msg.IsQuery());
+ EXPECT_TRUE(msg.IsAuthoritative());
+ EXPECT_EQ(0x1234, msg.GetId());
+ EXPECT_EQ(0u, msg.question_section().size());
+ ASSERT_EQ(2u, msg.answer_section().size());
+ EXPECT_EQ(0u, msg.authority_section().size());
+ EXPECT_EQ(0u, msg.additional_section().size());
+
+ const auto& answer1 = msg.answer_section()[0];
+ const auto& answer2 = msg.answer_section()[1];
+ EXPECT_EQ(SectionEntryType::kA, answer1.GetType());
+ EXPECT_EQ(SectionEntryType::kAAAA, answer2.GetType());
+ EXPECT_EQ(60u, answer1.GetTtlSeconds());
+ EXPECT_EQ(120u, answer2.GetTtlSeconds());
+
+ std::map<std::string, rtc::IPAddress> resolution;
+ EXPECT_TRUE(GetResolution(&msg, &resolution));
+ rtc::IPAddress expected_addr_ipv4(
+ rtc::SocketAddress("192.168.0.1", 0).ipaddr());
+ rtc::IPAddress expected_addr_ipv6(
+ rtc::SocketAddress("fd12:3456:789a:1::1", 0).ipaddr());
+ EXPECT_THAT(resolution,
+ UnorderedElementsAre(Pair("webrtc4.org.", expected_addr_ipv4),
+ Pair("webrtc6.org.", expected_addr_ipv6)));
+}
+
+TEST(MDnsMessageTest, ReadTwoAnswersForIPv4AndIPv6AddrWithNameCompression) {
+ MDnsMessage msg;
+ ASSERT_TRUE(ReadMDnsMessage(
+ &msg, kTwoAuthoritativeAnswersWithIPv4AndIPv6AddrWithNameCompression));
+
+ std::map<std::string, rtc::IPAddress> resolution;
+ EXPECT_TRUE(GetResolution(&msg, &resolution));
+ rtc::IPAddress expected_addr_ipv4(
+ rtc::SocketAddress("192.168.0.1", 0).ipaddr());
+ rtc::IPAddress expected_addr_ipv6(
+ rtc::SocketAddress("fd12:3456:789a:1::1", 0).ipaddr());
+ EXPECT_THAT(resolution,
+ UnorderedElementsAre(Pair("www.webrtc.org.", expected_addr_ipv4),
+ Pair("webrtc.org.", expected_addr_ipv6)));
+}
+
+TEST(MDnsMessageTest,
+ ReadAnswerWithCorruptedPointerInNameCompressionShouldFail) {
+ MDnsMessage msg;
+ EXPECT_FALSE(ReadMDnsMessage(&msg, kCorruptedAnswerWithNameCompression1));
+ EXPECT_FALSE(ReadMDnsMessage(&msg, kCorruptedAnswerWithNameCompression2));
+}
+
+TEST(MDnsMessageTest, WriteSingleQuestionForIPv4Addr) {
+ MDnsMessage msg;
+ msg.SetId(0x1234);
+ msg.SetQueryOrResponse(true);
+
+ MDnsQuestion question;
+ question.SetName("webrtc.org.");
+ question.SetType(SectionEntryType::kA);
+ question.SetClass(SectionEntryClass::kIN);
+ question.SetUnicastResponse(true);
+ msg.AddQuestion(question);
+
+ WriteMDnsMessageAndCompare(&msg,
+ kSingleQuestionForIPv4AddrWithUnicastResponse);
+}
+
+TEST(MDnsMessageTest, WriteTwoQuestionsForIPv4AndIPv6Addr) {
+ MDnsMessage msg;
+ msg.SetId(0x1234);
+ msg.SetQueryOrResponse(true);
+
+ MDnsQuestion question1;
+ question1.SetName("webrtc4.org.");
+ question1.SetType(SectionEntryType::kA);
+ question1.SetClass(SectionEntryClass::kIN);
+ msg.AddQuestion(question1);
+
+ MDnsQuestion question2;
+ question2.SetName("webrtc6.org.");
+ question2.SetType(SectionEntryType::kAAAA);
+ question2.SetClass(SectionEntryClass::kIN);
+ msg.AddQuestion(question2);
+
+ WriteMDnsMessageAndCompare(
+ &msg, kTwoQuestionsForIPv4AndIPv6AddrWithMulticastResponse);
+}
+
+TEST(MDnsMessageTest, WriteSingleAnswerToIPv4Addr) {
+ MDnsMessage msg;
+ msg.SetId(0x1234);
+ msg.SetQueryOrResponse(false);
+ msg.SetAuthoritative(true);
+
+ MDnsResourceRecord answer;
+ answer.SetName("webrtc.org.");
+ answer.SetType(SectionEntryType::kA);
+ answer.SetClass(SectionEntryClass::kIN);
+ EXPECT_TRUE(answer.SetIPAddressInRecordData(
+ rtc::SocketAddress("192.168.0.1", 0).ipaddr()));
+ answer.SetTtlSeconds(120);
+ msg.AddAnswerRecord(answer);
+
+ WriteMDnsMessageAndCompare(&msg, kSingleAuthoritativeAnswerWithIPv4Addr);
+}
+
+TEST(MDnsMessageTest, WriteTwoAnswersToIPv4AndIPv6Addr) {
+ MDnsMessage msg;
+ msg.SetId(0x1234);
+ msg.SetQueryOrResponse(false);
+ msg.SetAuthoritative(true);
+
+ MDnsResourceRecord answer1;
+ answer1.SetName("webrtc4.org.");
+ answer1.SetType(SectionEntryType::kA);
+ answer1.SetClass(SectionEntryClass::kIN);
+ answer1.SetIPAddressInRecordData(
+ rtc::SocketAddress("192.168.0.1", 0).ipaddr());
+ answer1.SetTtlSeconds(60);
+ msg.AddAnswerRecord(answer1);
+
+ MDnsResourceRecord answer2;
+ answer2.SetName("webrtc6.org.");
+ answer2.SetType(SectionEntryType::kAAAA);
+ answer2.SetClass(SectionEntryClass::kIN);
+ answer2.SetIPAddressInRecordData(
+ rtc::SocketAddress("fd12:3456:789a:1::1", 0).ipaddr());
+ answer2.SetTtlSeconds(120);
+ msg.AddAnswerRecord(answer2);
+
+ WriteMDnsMessageAndCompare(&msg, kTwoAuthoritativeAnswersWithIPv4AndIPv6Addr);
+}
+
+} // namespace webrtc
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
index 7a03b3b..2ef79fa 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
@@ -413,6 +413,7 @@
"ignore_wundef.h",
"location.cc",
"location.h",
+ "message_buffer_reader.h",
"numerics/histogram_percentile_counter.cc",
"numerics/histogram_percentile_counter.h",
"numerics/mod_ops.h",
diff --git a/rtc_base/bytebuffer.h b/rtc_base/bytebuffer.h
index 9036bcd..9e08f02 100644
--- a/rtc_base/bytebuffer.h
+++ b/rtc_base/bytebuffer.h
@@ -185,7 +185,7 @@
// after this call.
bool Consume(size_t size);
- private:
+ protected:
void Construct(const char* bytes, size_t size);
const char* bytes_;
@@ -193,6 +193,7 @@
size_t start_;
size_t end_;
+ private:
RTC_DISALLOW_COPY_AND_ASSIGN(ByteBufferReader);
};
diff --git a/rtc_base/message_buffer_reader.h b/rtc_base/message_buffer_reader.h
new file mode 100644
index 0000000..baba89e
--- /dev/null
+++ b/rtc_base/message_buffer_reader.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2018 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef RTC_BASE_MESSAGE_BUFFER_READER_H_
+#define RTC_BASE_MESSAGE_BUFFER_READER_H_
+
+#include "rtc_base/bytebuffer.h"
+
+namespace webrtc {
+
+// A simple subclass of the ByteBufferReader that exposes the starting address
+// of the message and its length, so that we can recall previously parsed data.
+class MessageBufferReader : public rtc::ByteBufferReader {
+ public:
+ MessageBufferReader(const char* bytes, size_t len)
+ : rtc::ByteBufferReader(bytes, len) {}
+ ~MessageBufferReader() = default;
+
+ // Starting address of the message.
+ const char* MessageData() const { return bytes_; }
+ // Total length of the message. Note that this is different from Length(),
+ // which is the length of the remaining message from the current offset.
+ size_t MessageLength() const { return size_; }
+ // Current offset in the message.
+ size_t CurrentOffset() const { return start_; }
+};
+
+} // namespace webrtc
+
+#endif // RTC_BASE_MESSAGE_BUFFER_READER_H_
diff --git a/test/DEPS b/test/DEPS
index cd8b2e2..80f47b2 100644
--- a/test/DEPS
+++ b/test/DEPS
@@ -32,6 +32,9 @@
"+modules/pacing/packet_router.h",
"+modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h",
],
+ ".*mdns_parser_fuzzer\.cc": [
+ "+p2p/base/mdns_message.h",
+ ],
".*pseudotcp_parser_fuzzer\.cc": [
"+p2p/base/pseudotcp.h",
],
diff --git a/test/fuzzers/BUILD.gn b/test/fuzzers/BUILD.gn
index f361d59..e006c6a 100644
--- a/test/fuzzers/BUILD.gn
+++ b/test/fuzzers/BUILD.gn
@@ -438,6 +438,18 @@
dict = "corpora/stun.tokens"
}
+webrtc_fuzzer_test("mdns_parser_fuzzer") {
+ sources = [
+ "mdns_parser_fuzzer.cc",
+ ]
+ deps = [
+ "../../p2p:rtc_p2p",
+ "../../rtc_base:rtc_base_approved",
+ "//third_party/abseil-cpp/absl/memory",
+ ]
+ seed_corpus = "corpora/mdns-corpus"
+}
+
webrtc_fuzzer_test("pseudotcp_parser_fuzzer") {
sources = [
"pseudotcp_parser_fuzzer.cc",
diff --git a/test/fuzzers/corpora/mdns-corpus/1.mdns b/test/fuzzers/corpora/mdns-corpus/1.mdns
new file mode 100644
index 0000000..6e1789a
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/1.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/10.mdns b/test/fuzzers/corpora/mdns-corpus/10.mdns
new file mode 100644
index 0000000..f5fa6f8
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/10.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/11.mdns b/test/fuzzers/corpora/mdns-corpus/11.mdns
new file mode 100644
index 0000000..f72f28c
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/11.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/12.mdns b/test/fuzzers/corpora/mdns-corpus/12.mdns
new file mode 100644
index 0000000..9efa76d
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/12.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/13.mdns b/test/fuzzers/corpora/mdns-corpus/13.mdns
new file mode 100644
index 0000000..538a5a6
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/13.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/14.mdns b/test/fuzzers/corpora/mdns-corpus/14.mdns
new file mode 100644
index 0000000..d4ce4ef
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/14.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/15.mdns b/test/fuzzers/corpora/mdns-corpus/15.mdns
new file mode 100644
index 0000000..ff28105
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/15.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/16.mdns b/test/fuzzers/corpora/mdns-corpus/16.mdns
new file mode 100644
index 0000000..a1a02d7
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/16.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/17.mdns b/test/fuzzers/corpora/mdns-corpus/17.mdns
new file mode 100644
index 0000000..ba30f7b
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/17.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/18.mdns b/test/fuzzers/corpora/mdns-corpus/18.mdns
new file mode 100644
index 0000000..7cbdd3e
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/18.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/19.mdns b/test/fuzzers/corpora/mdns-corpus/19.mdns
new file mode 100644
index 0000000..f70eaa3
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/19.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/2.mdns b/test/fuzzers/corpora/mdns-corpus/2.mdns
new file mode 100644
index 0000000..7d259c2
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/2.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/20.mdns b/test/fuzzers/corpora/mdns-corpus/20.mdns
new file mode 100644
index 0000000..6681f94
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/20.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/3.mdns b/test/fuzzers/corpora/mdns-corpus/3.mdns
new file mode 100644
index 0000000..3ac4fd6
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/3.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/4.mdns b/test/fuzzers/corpora/mdns-corpus/4.mdns
new file mode 100644
index 0000000..3207842
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/4.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/5.mdns b/test/fuzzers/corpora/mdns-corpus/5.mdns
new file mode 100644
index 0000000..871ea41
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/5.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/6.mdns b/test/fuzzers/corpora/mdns-corpus/6.mdns
new file mode 100644
index 0000000..ee2f8ec
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/6.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/7.mdns b/test/fuzzers/corpora/mdns-corpus/7.mdns
new file mode 100644
index 0000000..d37b935
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/7.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/8.mdns b/test/fuzzers/corpora/mdns-corpus/8.mdns
new file mode 100644
index 0000000..dd2f976
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/8.mdns
Binary files differ
diff --git a/test/fuzzers/corpora/mdns-corpus/9.mdns b/test/fuzzers/corpora/mdns-corpus/9.mdns
new file mode 100644
index 0000000..a01e729
--- /dev/null
+++ b/test/fuzzers/corpora/mdns-corpus/9.mdns
Binary files differ
diff --git a/test/fuzzers/mdns_parser_fuzzer.cc b/test/fuzzers/mdns_parser_fuzzer.cc
new file mode 100644
index 0000000..c3f765e
--- /dev/null
+++ b/test/fuzzers/mdns_parser_fuzzer.cc
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2018 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "absl/memory/memory.h"
+#include "p2p/base/mdns_message.h"
+#include "rtc_base/message_buffer_reader.h"
+
+namespace webrtc {
+
+void FuzzOneInput(const uint8_t* data, size_t size) {
+ MessageBufferReader buf(reinterpret_cast<const char*>(data), size);
+ auto mdns_msg = absl::make_unique<MDnsMessage>();
+ mdns_msg->Read(&buf);
+}
+
+} // namespace webrtc