/* * * Copyright (c) 2020 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include namespace mdns { namespace Minimal { /** * Wrapper around a MDNS bit-packed flags in a DNS header as defined in * RFC 1035 and RFC 6762 * * | 0| 1 2 3 4| 5| 6| 7| 8| 9| 0| 1| 2 3 4 5 | * |QR| OPCODE |AA|TC|RD|RA| Z|AD|CD| RCODE | * * RFC 6762 states: * - OPCODE must be 0 on transmission and queries received that are not 0 MUST be ignored * - RCODE must be 0 on transmission and messages received with non-zero must be silently ignored * * - AA (Authoritative Answer) MUST be 0 on transmission, ignored on reception * - RD (Recursion desired) MUST be 0 on transmission, ignored on reception * - RA (Recursion available) MUST be 0 on transmission, ignored on reception * - AD (Authentic data) MUST be 0 on transmission, ignored on reception * - CD (Checking Disabled) MUST be 0 on transmission, ignored on reception * * Accessors are only provided on useful values */ class BitPackedFlags { public: explicit BitPackedFlags(uint16_t value) : mValue(value) {} uint16_t RawValue() const { return mValue & kMdnsNonIgnoredMask; } bool IsQuery() const { return (mValue & kIsResponseMask) == 0; } BitPackedFlags & SetQuery() { return ClearMask(kIsResponseMask); } bool IsResponse() const { return (mValue & kIsResponseMask) == kIsResponseMask; } BitPackedFlags & SetResponse() { return SetMask(kIsResponseMask); } bool IsAuthoritative() const { return (mValue & kAuthoritativeMask) == kAuthoritativeMask; } BitPackedFlags & SetAuthoritative() { return SetMask(kAuthoritativeMask); } bool IsTruncated() const { return (mValue & kTruncationMask) != 0; } BitPackedFlags & SetTruncated(bool value) { return value ? SetMask(kTruncationMask) : ClearMask(kTruncationMask); } /// Validates that the message does not need to be ignored according to /// RFC 6762 bool IsValidMdns() const { return (mValue & (kOpcodeMask | kReturnCodeMask)) == 0; } private: uint16_t mValue = 0; inline BitPackedFlags & ClearMask(uint16_t mask) { mValue &= static_cast(~mask); return *this; } inline BitPackedFlags & SetMask(uint16_t mask) { mValue |= mask; return *this; } // Mask to limit values to what RFC 6762 consideres useful // 1111 1110 0000 0000 = FE0F // TODO(cecille): need to better document this value. Why is the comment different than the value? static constexpr uint16_t kMdnsNonIgnoredMask = 0x8E08; static constexpr uint16_t kAuthoritativeMask = 0x0400; static constexpr uint16_t kIsResponseMask = 0x8000; static constexpr uint16_t kOpcodeMask = 0x7000; static constexpr uint16_t kTruncationMask = 0x0200; static constexpr uint16_t kReturnCodeMask = 0x000F; }; /** * Allows operations on a DNS header. A DNS Header is defined in RFC 1035 * and looks like this: * * | 0| 1 2 3 4| 5| 6| 7| 8| 9| 0| 1| 2 3 4 5 | * | Message ID | * |QR| OPCODE |AA|TC|RD|RA| Z|AD|CD| RCODE | * | Items in QUESTION Section | * | Items in ANSWER Section | * | Items in AUTHORITY Section | * | Items in ADDITIONAL Section | */ class ConstHeaderRef { public: static constexpr size_t kSizeBytes = 12; /// size of a DNS header structure ConstHeaderRef(const uint8_t * buffer) : mBuffer(buffer) {} uint16_t GetMessageId() const { return Get16At(kMessageIdOffset); } BitPackedFlags GetFlags() const { return BitPackedFlags(Get16At(kFlagsOffset)); } uint16_t GetQueryCount() const { return Get16At(kQueryCountOffset); } uint16_t GetAnswerCount() const { return Get16At(kAnswerCountOffset); } uint16_t GetAuthorityCount() const { return Get16At(kAuthorityCountOffset); } uint16_t GetAdditionalCount() const { return Get16At(kAdditionalCountOffset); } protected: const uint8_t * mBuffer; inline uint16_t Get16At(size_t offset) const { return chip::Encoding::BigEndian::Get16(mBuffer + offset); } uint16_t GetRawFlags() const { return Get16At(kFlagsOffset); } static constexpr size_t kMessageIdOffset = 0; static constexpr size_t kFlagsOffset = 2; static constexpr size_t kQueryCountOffset = 4; static constexpr size_t kAnswerCountOffset = 6; static constexpr size_t kAuthorityCountOffset = 8; static constexpr size_t kAdditionalCountOffset = 10; }; class HeaderRef : public ConstHeaderRef { public: HeaderRef(uint8_t * buffer) : ConstHeaderRef(buffer) {} HeaderRef(const HeaderRef & other) = default; HeaderRef & operator=(const HeaderRef & other) = default; HeaderRef & Clear() { memset(GetWritable(), 0, kSizeBytes); return *this; } HeaderRef & SetMessageId(uint16_t value) { return Set16At(kMessageIdOffset, value); } HeaderRef & SetFlags(BitPackedFlags flags) { return Set16At(kFlagsOffset, flags.RawValue()); } HeaderRef & SetQueryCount(uint16_t value) { return Set16At(kQueryCountOffset, value); } HeaderRef & SetAnswerCount(uint16_t value) { return Set16At(kAnswerCountOffset, value); } HeaderRef & SetAuthorityCount(uint16_t value) { return Set16At(kAuthorityCountOffset, value); } HeaderRef & SetAdditionalCount(uint16_t value) { return Set16At(kAdditionalCountOffset, value); } private: /// Returns the internal buffer as writable. Const-cast is correct because /// the construct took a non-const buffer as well. uint8_t * GetWritable() { return const_cast(mBuffer); } inline HeaderRef & Set16At(size_t offset, uint16_t value) { chip::Encoding::BigEndian::Put16(GetWritable() + offset, value); return *this; } HeaderRef & SetRawFlags(uint16_t value) { return Set16At(kFlagsOffset, value); } }; } // namespace Minimal } // namespace mdns