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/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_