to Mdns.*. MdnsResponderInterface now explicitly requires the reference counting of created names to allow the coexistence of multiple users of the same responder where one user would not remove identical names created by others. MDns.* is also renamed to Mdns.* per the style guide. TBR=aleloi@webrtc.org Bug: webrtc:9605 Change-Id: I047fc41f34de8d4e97c980409a7f373769c4c252 Reviewed-on: https://webrtc-review.googlesource.com/c/101921 Commit-Queue: Qingsi Wang <qingsi@webrtc.org> Reviewed-by: Qingsi Wang <qingsi@webrtc.org> Reviewed-by: Steve Anton <steveanton@webrtc.org> Cr-Commit-Position: refs/heads/master@{#25458}
396 lines
10 KiB
C++
396 lines
10 KiB
C++
/*
|
|
* 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
|