// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef DISCOVERY_MDNS_MDNS_RECORDS_H_ #define DISCOVERY_MDNS_MDNS_RECORDS_H_ #include #include #include #include #include #include #include #include "absl/strings/ascii.h" #include "absl/strings/string_view.h" #include "absl/types/variant.h" #include "discovery/mdns/public/mdns_constants.h" #include "platform/base/error.h" #include "platform/base/interface_info.h" #include "platform/base/ip_address.h" #include "util/osp_logging.h" namespace openscreen { namespace discovery { bool IsValidDomainLabel(absl::string_view label); // Represents domain name as a collection of labels, ensures label length and // domain name length requirements are met. class DomainName { public: DomainName(); template static ErrorOr TryCreate(IteratorType first, IteratorType last) { std::vector labels; size_t max_wire_size = 1; labels.reserve(std::distance(first, last)); for (IteratorType entry = first; entry != last; ++entry) { if (!IsValidDomainLabel(*entry)) { return Error::Code::kParameterInvalid; } labels.emplace_back(*entry); // Include the length byte in the size calculation. max_wire_size += entry->size() + 1; } if (max_wire_size > kMaxDomainNameLength) { return Error::Code::kIndexOutOfBounds; } else { return DomainName(std::move(labels), max_wire_size); } } template DomainName(IteratorType first, IteratorType last) { ErrorOr domain = TryCreate(first, last); *this = std::move(domain.value()); } explicit DomainName(std::vector labels); explicit DomainName(const std::vector& labels); explicit DomainName(std::initializer_list labels); DomainName(const DomainName& other); DomainName(DomainName&& other) noexcept; DomainName& operator=(const DomainName& rhs); DomainName& operator=(DomainName&& rhs); bool operator<(const DomainName& rhs) const; bool operator<=(const DomainName& rhs) const; bool operator>(const DomainName& rhs) const; bool operator>=(const DomainName& rhs) const; bool operator==(const DomainName& rhs) const; bool operator!=(const DomainName& rhs) const; std::string ToString() const; // Returns the maximum space that the domain name could take up in its // on-the-wire format. This is an upper bound based on the length of the // labels that make up the domain name. It's possible that with domain name // compression the actual space taken in on-the-wire format is smaller. size_t MaxWireSize() const; bool empty() const { return labels_.empty(); } bool IsRoot() const { return labels_.empty(); } const std::vector& labels() const { return labels_; } template friend H AbslHashValue(H h, const DomainName& domain_name) { std::vector labels_clone = domain_name.labels_; for (auto& label : labels_clone) { absl::AsciiStrToLower(&label); } return H::combine(std::move(h), std::move(labels_clone)); } private: DomainName(std::vector labels, size_t max_wire_size); // max_wire_size_ starts at 1 for the terminating character length. size_t max_wire_size_ = 1; std::vector labels_; }; // Parsed representation of the extra data in a record. Does not include // standard DNS record data such as TTL, Name, Type, and Class. We use it to // distinguish a raw record type that we do not know the identity of. class RawRecordRdata { public: static ErrorOr TryCreate(std::vector rdata); RawRecordRdata(); explicit RawRecordRdata(std::vector rdata); RawRecordRdata(const uint8_t* begin, size_t size); RawRecordRdata(const RawRecordRdata& other); RawRecordRdata(RawRecordRdata&& other) noexcept; RawRecordRdata& operator=(const RawRecordRdata& rhs); RawRecordRdata& operator=(RawRecordRdata&& rhs); bool operator==(const RawRecordRdata& rhs) const; bool operator!=(const RawRecordRdata& rhs) const; size_t MaxWireSize() const; uint16_t size() const { return rdata_.size(); } const uint8_t* data() const { return rdata_.data(); } template friend H AbslHashValue(H h, const RawRecordRdata& rdata) { return H::combine(std::move(h), rdata.rdata_); } private: std::vector rdata_; }; // SRV record format (http://www.ietf.org/rfc/rfc2782.txt): // 2 bytes network-order unsigned priority // 2 bytes network-order unsigned weight // 2 bytes network-order unsigned port // target: domain name (on-the-wire representation) class SrvRecordRdata { public: SrvRecordRdata(); SrvRecordRdata(uint16_t priority, uint16_t weight, uint16_t port, DomainName target); SrvRecordRdata(const SrvRecordRdata& other); SrvRecordRdata(SrvRecordRdata&& other) noexcept; SrvRecordRdata& operator=(const SrvRecordRdata& rhs); SrvRecordRdata& operator=(SrvRecordRdata&& rhs); bool operator==(const SrvRecordRdata& rhs) const; bool operator!=(const SrvRecordRdata& rhs) const; size_t MaxWireSize() const; uint16_t priority() const { return priority_; } uint16_t weight() const { return weight_; } uint16_t port() const { return port_; } const DomainName& target() const { return target_; } template friend H AbslHashValue(H h, const SrvRecordRdata& rdata) { return H::combine(std::move(h), rdata.priority_, rdata.weight_, rdata.port_, rdata.target_); } private: uint16_t priority_ = 0; uint16_t weight_ = 0; uint16_t port_ = 0; DomainName target_; }; // A Record format (http://www.ietf.org/rfc/rfc1035.txt): // 4 bytes for IP address. class ARecordRdata { public: ARecordRdata(); explicit ARecordRdata(IPAddress ipv4_address, NetworkInterfaceIndex interface_index = 0); ARecordRdata(const ARecordRdata& other); ARecordRdata(ARecordRdata&& other) noexcept; ARecordRdata& operator=(const ARecordRdata& rhs); ARecordRdata& operator=(ARecordRdata&& rhs); bool operator==(const ARecordRdata& rhs) const; bool operator!=(const ARecordRdata& rhs) const; size_t MaxWireSize() const; const IPAddress& ipv4_address() const { return ipv4_address_; } NetworkInterfaceIndex interface_index() const { return interface_index_; } template friend H AbslHashValue(H h, const ARecordRdata& rdata) { const auto& bytes = rdata.ipv4_address_.bytes(); return H::combine_contiguous(std::move(h), bytes, 4); } private: IPAddress ipv4_address_{0, 0, 0, 0}; NetworkInterfaceIndex interface_index_; }; // AAAA Record format (http://www.ietf.org/rfc/rfc1035.txt): // 16 bytes for IP address. class AAAARecordRdata { public: AAAARecordRdata(); explicit AAAARecordRdata(IPAddress ipv6_address, NetworkInterfaceIndex interface_index = 0); AAAARecordRdata(const AAAARecordRdata& other); AAAARecordRdata(AAAARecordRdata&& other) noexcept; AAAARecordRdata& operator=(const AAAARecordRdata& rhs); AAAARecordRdata& operator=(AAAARecordRdata&& rhs); bool operator==(const AAAARecordRdata& rhs) const; bool operator!=(const AAAARecordRdata& rhs) const; size_t MaxWireSize() const; const IPAddress& ipv6_address() const { return ipv6_address_; } NetworkInterfaceIndex interface_index() const { return interface_index_; } template friend H AbslHashValue(H h, const AAAARecordRdata& rdata) { const auto& bytes = rdata.ipv6_address_.bytes(); return H::combine_contiguous(std::move(h), bytes, 16); } private: IPAddress ipv6_address_{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; NetworkInterfaceIndex interface_index_; }; // PTR record format (http://www.ietf.org/rfc/rfc1035.txt): // domain: On the wire representation of domain name. class PtrRecordRdata { public: PtrRecordRdata(); explicit PtrRecordRdata(DomainName ptr_domain); PtrRecordRdata(const PtrRecordRdata& other); PtrRecordRdata(PtrRecordRdata&& other) noexcept; PtrRecordRdata& operator=(const PtrRecordRdata& rhs); PtrRecordRdata& operator=(PtrRecordRdata&& rhs); bool operator==(const PtrRecordRdata& rhs) const; bool operator!=(const PtrRecordRdata& rhs) const; size_t MaxWireSize() const; const DomainName& ptr_domain() const { return ptr_domain_; } template friend H AbslHashValue(H h, const PtrRecordRdata& rdata) { return H::combine(std::move(h), rdata.ptr_domain_); } private: DomainName ptr_domain_; }; // TXT record format (http://www.ietf.org/rfc/rfc1035.txt). // texts: One or more . // An is a length octet followed by as many data octets. // // DNS-SD interprets as a list of boolean keys and key=value // attributes. See https://tools.ietf.org/html/rfc6763#section-6 for details. class TxtRecordRdata { public: using Entry = std::vector; static ErrorOr TryCreate(std::vector texts); TxtRecordRdata(); explicit TxtRecordRdata(std::vector texts); TxtRecordRdata(const TxtRecordRdata& other); TxtRecordRdata(TxtRecordRdata&& other) noexcept; TxtRecordRdata& operator=(const TxtRecordRdata& rhs); TxtRecordRdata& operator=(TxtRecordRdata&& rhs); bool operator==(const TxtRecordRdata& rhs) const; bool operator!=(const TxtRecordRdata& rhs) const; size_t MaxWireSize() const; // NOTE: TXT entries are not guaranteed to be character data. const std::vector& texts() const { return texts_; } template friend H AbslHashValue(H h, const TxtRecordRdata& rdata) { return H::combine(std::move(h), rdata.texts_); } private: TxtRecordRdata(std::vector texts, size_t max_wire_size); // max_wire_size_ is at least 3, uint16_t record length and at the // minimum a NULL byte character string is present. size_t max_wire_size_ = 3; // NOTE: For compatibility with DNS-SD usage, std::string is used for internal // storage. std::vector texts_; }; // NSEC record format (https://tools.ietf.org/html/rfc4034#section-4). // In mDNS, this record type is used for representing negative responses to // queries. // // next_domain_name: The next domain to process. In mDNS, this value is expected // to match the record-level domain name in a negative response. // // An example of how the |types_| vector is serialized is as follows: // When encoding the following DNS types: // - A (value 1) // - MX (value 15) // - RRSIG (value 46) // - NSEC (value 47) // - TYPE1234 (value 1234) // The result would be: // 0x00 0x06 0x40 0x01 0x00 0x00 0x00 0x03 // 0x04 0x1b 0x00 0x00 0x00 0x00 0x00 0x00 // 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 // 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 // 0x00 0x00 0x00 0x00 0x20 class NsecRecordRdata { public: NsecRecordRdata(); // Constructor that takes an arbitrary number of DnsType parameters. // NOTE: If `types...` provide a valid set of parameters for an // std::vector ctor call, this will compile. Do not use this ctor // except to provide multiple DnsType parameters. template NsecRecordRdata(DomainName next_domain_name, Types... types) : NsecRecordRdata(std::move(next_domain_name), std::vector{types...}) {} NsecRecordRdata(DomainName next_domain_name, std::vector types); NsecRecordRdata(const NsecRecordRdata& other); NsecRecordRdata(NsecRecordRdata&& other) noexcept; NsecRecordRdata& operator=(const NsecRecordRdata& rhs); NsecRecordRdata& operator=(NsecRecordRdata&& rhs); bool operator==(const NsecRecordRdata& rhs) const; bool operator!=(const NsecRecordRdata& rhs) const; size_t MaxWireSize() const; const DomainName& next_domain_name() const { return next_domain_name_; } const std::vector& types() const { return types_; } const std::vector& encoded_types() const { return encoded_types_; } template friend H AbslHashValue(H h, const NsecRecordRdata& rdata) { return H::combine(std::move(h), rdata.types_, rdata.next_domain_name_); } private: std::vector encoded_types_; std::vector types_; DomainName next_domain_name_; }; // The OPT pseudo-record / meta-record as defined by RFC6891. class OptRecordRdata { public: // A single option as defined in RFC6891 section 6.1.2. struct Option { size_t MaxWireSize() const; bool operator>(const Option& rhs) const; bool operator<(const Option& rhs) const; bool operator>=(const Option& rhs) const; bool operator<=(const Option& rhs) const; bool operator==(const Option& rhs) const; bool operator!=(const Option& rhs) const; template friend H AbslHashValue(H h, const Option& option) { return H::combine(std::move(h), option.code, option.length, option.data); } // Code assigned by the Expert Review process as defined by the DNSEXT // working group and the IESG, as specified in RFC6891 section 9.1. For // specific assignments, see: // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml uint16_t code; // Size (in octets) of |data|. uint16_t length; // Bit Field with meaning varying based on |code|. std::vector data; }; OptRecordRdata(); // Constructor that takes zero or more Option parameters. template explicit OptRecordRdata(Types... types) : OptRecordRdata(std::vector