blob: f5d4568e5d0a81c7043fb0d18e9187ad4e52aaa2 [file] [log] [blame]
// 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 CAST_COMMON_MDNS_MDNS_RECORDS_H_
#define CAST_COMMON_MDNS_MDNS_RECORDS_H_
#include <initializer_list>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/types/variant.h"
#include "cast/common/mdns/mdns_constants.h"
#include "platform/api/logging.h"
#include "platform/base/ip_address.h"
namespace cast {
namespace mdns {
using IPAddress = openscreen::IPAddress;
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() = default;
template <typename IteratorType>
DomainName(IteratorType first, IteratorType last) {
labels_.reserve(std::distance(first, last));
for (IteratorType entry = first; entry != last; ++entry) {
OSP_DCHECK(IsValidDomainLabel(*entry));
labels_.emplace_back(*entry);
// Include the length byte in the size calculation.
max_wire_size_ += entry->size() + 1;
}
OSP_DCHECK(max_wire_size_ <= kMaxDomainNameLength);
}
explicit DomainName(const std::vector<absl::string_view>& labels);
explicit DomainName(std::initializer_list<absl::string_view> labels);
DomainName(const DomainName& other) = default;
DomainName(DomainName&& other) noexcept = default;
~DomainName() = default;
DomainName& operator=(const DomainName& other) = default;
DomainName& operator=(DomainName&& other) noexcept = default;
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(); }
const std::vector<std::string>& labels() const { return labels_; }
template <typename H>
friend H AbslHashValue(H h, const DomainName& domain_name) {
return H::combine(std::move(h), domain_name.labels_);
}
private:
// max_wire_size_ starts at 1 for the terminating character length.
size_t max_wire_size_ = 1;
std::vector<std::string> labels_;
};
std::ostream& operator<<(std::ostream& stream, const DomainName& domain_name);
// Parsed represenation 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:
RawRecordRdata() = default;
explicit RawRecordRdata(std::vector<uint8_t> rdata);
RawRecordRdata(const uint8_t* begin, size_t size);
RawRecordRdata(const RawRecordRdata& other) = default;
RawRecordRdata(RawRecordRdata&& other) noexcept = default;
~RawRecordRdata() = default;
RawRecordRdata& operator=(const RawRecordRdata& other) = default;
RawRecordRdata& operator=(RawRecordRdata&& other) noexcept = default;
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 <typename H>
friend H AbslHashValue(H h, const RawRecordRdata& rdata) {
return H::combine(std::move(h), rdata.rdata_);
}
private:
std::vector<uint8_t> 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() = default;
SrvRecordRdata(uint16_t priority,
uint16_t weight,
uint16_t port,
DomainName target);
SrvRecordRdata(const SrvRecordRdata& other) = default;
SrvRecordRdata(SrvRecordRdata&& other) noexcept = default;
~SrvRecordRdata() = default;
SrvRecordRdata& operator=(const SrvRecordRdata& other) = default;
SrvRecordRdata& operator=(SrvRecordRdata&& other) noexcept = default;
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 <typename H>
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() = default;
explicit ARecordRdata(IPAddress ipv4_address);
ARecordRdata(const ARecordRdata& other) = default;
ARecordRdata(ARecordRdata&& other) noexcept = default;
~ARecordRdata() = default;
ARecordRdata& operator=(const ARecordRdata& other) = default;
ARecordRdata& operator=(ARecordRdata&& other) noexcept = default;
bool operator==(const ARecordRdata& rhs) const;
bool operator!=(const ARecordRdata& rhs) const;
size_t MaxWireSize() const;
const IPAddress& ipv4_address() const { return ipv4_address_; }
template <typename H>
friend H AbslHashValue(H h, const ARecordRdata& rdata) {
return H::combine(std::move(h), rdata.ipv4_address_.bytes());
}
private:
IPAddress ipv4_address_{0, 0, 0, 0};
};
// AAAA Record format (http://www.ietf.org/rfc/rfc1035.txt):
// 16 bytes for IP address.
class AAAARecordRdata {
public:
AAAARecordRdata() = default;
explicit AAAARecordRdata(IPAddress ipv6_address);
AAAARecordRdata(const AAAARecordRdata& other) = default;
AAAARecordRdata(AAAARecordRdata&& other) noexcept = default;
~AAAARecordRdata() = default;
AAAARecordRdata& operator=(const AAAARecordRdata& other) = default;
AAAARecordRdata& operator=(AAAARecordRdata&& other) noexcept = default;
bool operator==(const AAAARecordRdata& rhs) const;
bool operator!=(const AAAARecordRdata& rhs) const;
size_t MaxWireSize() const;
const IPAddress& ipv6_address() const { return ipv6_address_; }
template <typename H>
friend H AbslHashValue(H h, const AAAARecordRdata& rdata) {
return H::combine(std::move(h), rdata.ipv6_address_.bytes());
}
private:
IPAddress ipv6_address_{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
};
// PTR record format (http://www.ietf.org/rfc/rfc1035.txt):
// domain: On the wire representation of domain name.
class PtrRecordRdata {
public:
PtrRecordRdata() = default;
explicit PtrRecordRdata(DomainName ptr_domain);
PtrRecordRdata(const PtrRecordRdata& other) = default;
PtrRecordRdata(PtrRecordRdata&& other) noexcept = default;
~PtrRecordRdata() = default;
PtrRecordRdata& operator=(const PtrRecordRdata& other) = default;
PtrRecordRdata& operator=(PtrRecordRdata&& other) noexcept = default;
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 <typename H>
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 <character-string>s.
// a <character-string> is a length octet followed by as many characters.
class TxtRecordRdata {
public:
TxtRecordRdata() = default;
template <typename IteratorType>
TxtRecordRdata(IteratorType first, IteratorType last) {
const size_t count = std::distance(first, last);
if (count > 0) {
texts_.reserve(count);
// max_wire_size includes uint16_t record length field.
max_wire_size_ = sizeof(uint16_t);
for (IteratorType entry = first; entry != last; ++entry) {
OSP_DCHECK(!entry->empty());
texts_.emplace_back(*entry);
// Include the length byte in the size calculation.
max_wire_size_ += entry->size() + 1;
}
}
}
explicit TxtRecordRdata(const std::vector<absl::string_view>& texts);
explicit TxtRecordRdata(std::initializer_list<absl::string_view> texts);
TxtRecordRdata(const TxtRecordRdata& other) = default;
TxtRecordRdata(TxtRecordRdata&& other) noexcept = default;
~TxtRecordRdata() = default;
TxtRecordRdata& operator=(const TxtRecordRdata& other) = default;
TxtRecordRdata& operator=(TxtRecordRdata&& other) noexcept = default;
bool operator==(const TxtRecordRdata& rhs) const;
bool operator!=(const TxtRecordRdata& rhs) const;
size_t MaxWireSize() const;
const std::vector<std::string>& texts() const { return texts_; }
template <typename H>
friend H AbslHashValue(H h, const TxtRecordRdata& rdata) {
return H::combine(std::move(h), rdata.texts_);
}
private:
// 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;
std::vector<std::string> texts_;
};
using Rdata = absl::variant<RawRecordRdata,
SrvRecordRdata,
ARecordRdata,
AAAARecordRdata,
PtrRecordRdata,
TxtRecordRdata>;
// Resource record top level format (http://www.ietf.org/rfc/rfc1035.txt):
// name: the name of the node to which this resource record pertains.
// type: 2 bytes network-order RR TYPE code.
// class: 2 bytes network-order RR CLASS code.
// ttl: 4 bytes network-order cache time interval.
// rdata: RDATA describing the resource. The format of this information varies
// according to the TYPE and CLASS of the resource record.
class MdnsRecord {
public:
MdnsRecord() = default;
MdnsRecord(DomainName name,
DnsType dns_type,
DnsClass record_class,
RecordType record_type,
uint32_t ttl,
Rdata rdata);
MdnsRecord(const MdnsRecord& other) = default;
MdnsRecord(MdnsRecord&& other) noexcept = default;
~MdnsRecord() = default;
MdnsRecord& operator=(const MdnsRecord& other) = default;
MdnsRecord& operator=(MdnsRecord&& other) noexcept = default;
bool operator==(const MdnsRecord& other) const;
bool operator!=(const MdnsRecord& other) const;
size_t MaxWireSize() const;
const DomainName& name() const { return name_; }
DnsType dns_type() const { return dns_type_; }
DnsClass record_class() const { return record_class_; }
RecordType record_type() const { return record_type_; }
uint32_t ttl() const { return ttl_; }
const Rdata& rdata() const { return rdata_; }
template <typename H>
friend H AbslHashValue(H h, const MdnsRecord& record) {
return H::combine(std::move(h), record.name_, record.dns_type_,
record.record_class_, record.record_type_, record.ttl_,
record.rdata_);
}
private:
DomainName name_;
DnsType dns_type_ = static_cast<DnsType>(0);
DnsClass record_class_ = static_cast<DnsClass>(0);
RecordType record_type_ = RecordType::kShared;
uint32_t ttl_ = kDefaultRecordTTL;
// Default-constructed Rdata contains default-constructed RawRecordRdata
// as it is the first alternative type and it is default-constructible.
Rdata rdata_;
};
// Question top level format (http://www.ietf.org/rfc/rfc1035.txt):
// name: a domain name which identifies the target resource set.
// type: 2 bytes network-order RR TYPE code.
// class: 2 bytes network-order RR CLASS code.
class MdnsQuestion {
public:
MdnsQuestion() = default;
MdnsQuestion(DomainName name,
DnsType dns_type,
DnsClass record_class,
ResponseType response_type);
MdnsQuestion(const MdnsQuestion& other) = default;
MdnsQuestion(MdnsQuestion&& other) noexcept = default;
~MdnsQuestion() = default;
MdnsQuestion& operator=(const MdnsQuestion& other) = default;
MdnsQuestion& operator=(MdnsQuestion&& other) noexcept = default;
bool operator==(const MdnsQuestion& other) const;
bool operator!=(const MdnsQuestion& other) const;
size_t MaxWireSize() const;
const DomainName& name() const { return name_; }
DnsType dns_type() const { return dns_type_; }
DnsClass record_class() const { return record_class_; }
ResponseType response_type() const { return response_type_; }
template <typename H>
friend H AbslHashValue(H h, const MdnsQuestion& record) {
return H::combine(std::move(h), record.name_, record.dns_type_,
record.record_class_, record.response_type_);
}
private:
void CopyFrom(const MdnsQuestion& other);
DomainName name_;
DnsType dns_type_ = static_cast<DnsType>(0);
DnsClass record_class_ = static_cast<DnsClass>(0);
ResponseType response_type_ = ResponseType::kMulticast;
};
// Message top level format (http://www.ietf.org/rfc/rfc1035.txt):
// id: 2 bytes network-order identifier assigned by the program that generates
// any kind of query. This identifier is copied to the corresponding reply and
// can be used by the requester to match up replies to outstanding queries.
// flags: 2 bytes network-order flags bitfield
// questions: questions in the message
// answers: resource records that answer the questions
// authority_records: resource records that point toward authoritative name
// servers additional_records: additional resource records that relate to the
// query
class MdnsMessage {
public:
MdnsMessage() = default;
// Constructs a message with ID, flags and empty question, answer, authority
// and additional record collections.
MdnsMessage(uint16_t id, MessageType type);
MdnsMessage(uint16_t id,
MessageType type,
std::vector<MdnsQuestion> questions,
std::vector<MdnsRecord> answers,
std::vector<MdnsRecord> authority_records,
std::vector<MdnsRecord> additional_records);
MdnsMessage(const MdnsMessage& other) = default;
MdnsMessage(MdnsMessage&& other) noexcept = default;
~MdnsMessage() = default;
MdnsMessage& operator=(const MdnsMessage& other) = default;
MdnsMessage& operator=(MdnsMessage&& other) noexcept = default;
bool operator==(const MdnsMessage& other) const;
bool operator!=(const MdnsMessage& other) const;
void AddQuestion(MdnsQuestion question);
void AddAnswer(MdnsRecord record);
void AddAuthorityRecord(MdnsRecord record);
void AddAdditionalRecord(MdnsRecord record);
size_t MaxWireSize() const;
uint16_t id() const { return id_; }
MessageType type() const { return type_; }
const std::vector<MdnsQuestion>& questions() const { return questions_; }
const std::vector<MdnsRecord>& answers() const { return answers_; }
const std::vector<MdnsRecord>& authority_records() const {
return authority_records_;
}
const std::vector<MdnsRecord>& additional_records() const {
return additional_records_;
}
template <typename H>
friend H AbslHashValue(H h, const MdnsMessage& message) {
return H::combine(std::move(h), message.id_, message.type_,
message.questions_, message.answers_,
message.authority_records_, message.additional_records_);
}
private:
// The mDNS header is 12 bytes long
size_t max_wire_size_ = sizeof(Header);
uint16_t id_ = 0;
MessageType type_ = MessageType::Query;
std::vector<MdnsQuestion> questions_;
std::vector<MdnsRecord> answers_;
std::vector<MdnsRecord> authority_records_;
std::vector<MdnsRecord> additional_records_;
};
} // namespace mdns
} // namespace cast
#endif // CAST_COMMON_MDNS_MDNS_RECORDS_H_