diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn index f8f70d9889..74a124c62c 100644 --- a/p2p/BUILD.gn +++ b/p2p/BUILD.gn @@ -31,6 +31,8 @@ rtc_static_library("rtc_p2p") { "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 @@ if (rtc_include_tests) { "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 0000000000..61af6f39f2 --- /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 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* 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* 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& 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 0000000000..f6dec20a95 --- /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 +#include +#include +#include +#include + +#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& question_section() const { + return question_section_; + } + const std::vector& answer_section() const { + return answer_section_; + } + const std::vector& authority_section() const { + return authority_section_; + } + const std::vector& additional_section() const { + return additional_section_; + } + + private: + MDnsHeader header_; + std::vector question_section_; + std::vector answer_section_; + std::vector authority_section_; + std::vector 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 0000000000..fb4a3b1e7a --- /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 +#include +#include + +#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(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(out.Length()); + rtc::ByteBufferReader read_buf(out); + std::string bytes; + read_buf.ReadString(&bytes, len); + std::string testcase_bytes(reinterpret_cast(testcase), size); + EXPECT_EQ(testcase_bytes, bytes); +} + +bool GetQueriedNames(MDnsMessage* msg, std::set* 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* 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 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 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 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 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 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 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 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 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 7a03b3bfb5..2ef79fa6c4 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -413,6 +413,7 @@ rtc_source_set("rtc_base_approved_generic") { "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 9036bcde6f..9e08f025a8 100644 --- a/rtc_base/bytebuffer.h +++ b/rtc_base/bytebuffer.h @@ -185,7 +185,7 @@ class ByteBufferReader : public ByteBuffer { // 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 @@ class ByteBufferReader : public ByteBuffer { 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 0000000000..baba89e014 --- /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 cd8b2e2bb5..80f47b2f28 100644 --- a/test/DEPS +++ b/test/DEPS @@ -32,6 +32,9 @@ specific_include_rules = { "+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 f361d59261..e006c6a671 100644 --- a/test/fuzzers/BUILD.gn +++ b/test/fuzzers/BUILD.gn @@ -438,6 +438,18 @@ webrtc_fuzzer_test("stun_validator_fuzzer") { 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 0000000000..6e1789a83b Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/1.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/10.mdns b/test/fuzzers/corpora/mdns-corpus/10.mdns new file mode 100644 index 0000000000..f5fa6f8d17 Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/10.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/11.mdns b/test/fuzzers/corpora/mdns-corpus/11.mdns new file mode 100644 index 0000000000..f72f28c13c Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/11.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/12.mdns b/test/fuzzers/corpora/mdns-corpus/12.mdns new file mode 100644 index 0000000000..9efa76d64a Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/12.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/13.mdns b/test/fuzzers/corpora/mdns-corpus/13.mdns new file mode 100644 index 0000000000..538a5a62dc Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/13.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/14.mdns b/test/fuzzers/corpora/mdns-corpus/14.mdns new file mode 100644 index 0000000000..d4ce4efea8 Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/14.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/15.mdns b/test/fuzzers/corpora/mdns-corpus/15.mdns new file mode 100644 index 0000000000..ff2810565b Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/15.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/16.mdns b/test/fuzzers/corpora/mdns-corpus/16.mdns new file mode 100644 index 0000000000..a1a02d7f8e Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/16.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/17.mdns b/test/fuzzers/corpora/mdns-corpus/17.mdns new file mode 100644 index 0000000000..ba30f7bc2f Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/17.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/18.mdns b/test/fuzzers/corpora/mdns-corpus/18.mdns new file mode 100644 index 0000000000..7cbdd3e7a0 Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/18.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/19.mdns b/test/fuzzers/corpora/mdns-corpus/19.mdns new file mode 100644 index 0000000000..f70eaa3ab2 Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/19.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/2.mdns b/test/fuzzers/corpora/mdns-corpus/2.mdns new file mode 100644 index 0000000000..7d259c2ea9 Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/2.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/20.mdns b/test/fuzzers/corpora/mdns-corpus/20.mdns new file mode 100644 index 0000000000..6681f943e4 Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/20.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/3.mdns b/test/fuzzers/corpora/mdns-corpus/3.mdns new file mode 100644 index 0000000000..3ac4fd6c98 Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/3.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/4.mdns b/test/fuzzers/corpora/mdns-corpus/4.mdns new file mode 100644 index 0000000000..3207842f48 Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/4.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/5.mdns b/test/fuzzers/corpora/mdns-corpus/5.mdns new file mode 100644 index 0000000000..871ea41405 Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/5.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/6.mdns b/test/fuzzers/corpora/mdns-corpus/6.mdns new file mode 100644 index 0000000000..ee2f8eca1c Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/6.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/7.mdns b/test/fuzzers/corpora/mdns-corpus/7.mdns new file mode 100644 index 0000000000..d37b935654 Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/7.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/8.mdns b/test/fuzzers/corpora/mdns-corpus/8.mdns new file mode 100644 index 0000000000..dd2f976afd Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/8.mdns differ diff --git a/test/fuzzers/corpora/mdns-corpus/9.mdns b/test/fuzzers/corpora/mdns-corpus/9.mdns new file mode 100644 index 0000000000..a01e729342 Binary files /dev/null and b/test/fuzzers/corpora/mdns-corpus/9.mdns differ diff --git a/test/fuzzers/mdns_parser_fuzzer.cc b/test/fuzzers/mdns_parser_fuzzer.cc new file mode 100644 index 0000000000..c3f765ef78 --- /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 +#include + +#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(data), size); + auto mdns_msg = absl::make_unique(); + mdns_msg->Read(&buf); +} + +} // namespace webrtc