| /* |
| * 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/net_helpers.h" |
| #include "rtc_base/string_encode.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 (const auto& rr : section) { |
| if (!rr.Write(buf)) { |
| return false; |
| } |
| } |
| return true; |
| }; |
| |
| for (const 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 |