1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef DISCOVERY_MDNS_MDNS_RECORDS_H_
6 #define DISCOVERY_MDNS_MDNS_RECORDS_H_
7 
8 #include <algorithm>
9 #include <chrono>
10 #include <functional>
11 #include <initializer_list>
12 #include <string>
13 #include <utility>
14 #include <vector>
15 
16 #include "absl/strings/ascii.h"
17 #include "absl/strings/string_view.h"
18 #include "absl/types/variant.h"
19 #include "discovery/mdns/public/mdns_constants.h"
20 #include "platform/base/error.h"
21 #include "platform/base/interface_info.h"
22 #include "platform/base/ip_address.h"
23 #include "util/osp_logging.h"
24 
25 namespace openscreen {
26 namespace discovery {
27 
28 bool IsValidDomainLabel(absl::string_view label);
29 
30 // Represents domain name as a collection of labels, ensures label length and
31 // domain name length requirements are met.
32 class DomainName {
33  public:
34   DomainName();
35 
36   template <typename IteratorType>
TryCreate(IteratorType first,IteratorType last)37   static ErrorOr<DomainName> TryCreate(IteratorType first, IteratorType last) {
38     std::vector<std::string> labels;
39     size_t max_wire_size = 1;
40     labels.reserve(std::distance(first, last));
41     for (IteratorType entry = first; entry != last; ++entry) {
42       if (!IsValidDomainLabel(*entry)) {
43         return Error::Code::kParameterInvalid;
44       }
45       labels.emplace_back(*entry);
46       // Include the length byte in the size calculation.
47       max_wire_size += entry->size() + 1;
48     }
49 
50     if (max_wire_size > kMaxDomainNameLength) {
51       return Error::Code::kIndexOutOfBounds;
52     } else {
53       return DomainName(std::move(labels), max_wire_size);
54     }
55   }
56 
57   template <typename IteratorType>
DomainName(IteratorType first,IteratorType last)58   DomainName(IteratorType first, IteratorType last) {
59     ErrorOr<DomainName> domain = TryCreate(first, last);
60     *this = std::move(domain.value());
61   }
62   explicit DomainName(std::vector<std::string> labels);
63   explicit DomainName(const std::vector<absl::string_view>& labels);
64   explicit DomainName(std::initializer_list<absl::string_view> labels);
65   DomainName(const DomainName& other);
66   DomainName(DomainName&& other) noexcept;
67 
68   DomainName& operator=(const DomainName& rhs);
69   DomainName& operator=(DomainName&& rhs);
70   bool operator<(const DomainName& rhs) const;
71   bool operator<=(const DomainName& rhs) const;
72   bool operator>(const DomainName& rhs) const;
73   bool operator>=(const DomainName& rhs) const;
74   bool operator==(const DomainName& rhs) const;
75   bool operator!=(const DomainName& rhs) const;
76 
77   std::string ToString() const;
78 
79   // Returns the maximum space that the domain name could take up in its
80   // on-the-wire format. This is an upper bound based on the length of the
81   // labels that make up the domain name. It's possible that with domain name
82   // compression the actual space taken in on-the-wire format is smaller.
83   size_t MaxWireSize() const;
empty()84   bool empty() const { return labels_.empty(); }
IsRoot()85   bool IsRoot() const { return labels_.empty(); }
labels()86   const std::vector<std::string>& labels() const { return labels_; }
87 
88   template <typename H>
AbslHashValue(H h,const DomainName & domain_name)89   friend H AbslHashValue(H h, const DomainName& domain_name) {
90     std::vector<std::string> labels_clone = domain_name.labels_;
91     for (auto& label : labels_clone) {
92       absl::AsciiStrToLower(&label);
93     }
94     return H::combine(std::move(h), std::move(labels_clone));
95   }
96 
97  private:
98   DomainName(std::vector<std::string> labels, size_t max_wire_size);
99 
100   // max_wire_size_ starts at 1 for the terminating character length.
101   size_t max_wire_size_ = 1;
102   std::vector<std::string> labels_;
103 };
104 
105 // Parsed representation of the extra data in a record. Does not include
106 // standard DNS record data such as TTL, Name, Type, and Class. We use it to
107 // distinguish a raw record type that we do not know the identity of.
108 class RawRecordRdata {
109  public:
110   static ErrorOr<RawRecordRdata> TryCreate(std::vector<uint8_t> rdata);
111 
112   RawRecordRdata();
113   explicit RawRecordRdata(std::vector<uint8_t> rdata);
114   RawRecordRdata(const uint8_t* begin, size_t size);
115   RawRecordRdata(const RawRecordRdata& other);
116   RawRecordRdata(RawRecordRdata&& other) noexcept;
117 
118   RawRecordRdata& operator=(const RawRecordRdata& rhs);
119   RawRecordRdata& operator=(RawRecordRdata&& rhs);
120   bool operator==(const RawRecordRdata& rhs) const;
121   bool operator!=(const RawRecordRdata& rhs) const;
122 
123   size_t MaxWireSize() const;
size()124   uint16_t size() const { return rdata_.size(); }
data()125   const uint8_t* data() const { return rdata_.data(); }
126 
127   template <typename H>
AbslHashValue(H h,const RawRecordRdata & rdata)128   friend H AbslHashValue(H h, const RawRecordRdata& rdata) {
129     return H::combine(std::move(h), rdata.rdata_);
130   }
131 
132  private:
133   std::vector<uint8_t> rdata_;
134 };
135 
136 // SRV record format (http://www.ietf.org/rfc/rfc2782.txt):
137 //   2 bytes network-order unsigned priority
138 //   2 bytes network-order unsigned weight
139 //   2 bytes network-order unsigned port
140 // target: domain name (on-the-wire representation)
141 class SrvRecordRdata {
142  public:
143   SrvRecordRdata();
144   SrvRecordRdata(uint16_t priority,
145                  uint16_t weight,
146                  uint16_t port,
147                  DomainName target);
148   SrvRecordRdata(const SrvRecordRdata& other);
149   SrvRecordRdata(SrvRecordRdata&& other) noexcept;
150 
151   SrvRecordRdata& operator=(const SrvRecordRdata& rhs);
152   SrvRecordRdata& operator=(SrvRecordRdata&& rhs);
153   bool operator==(const SrvRecordRdata& rhs) const;
154   bool operator!=(const SrvRecordRdata& rhs) const;
155 
156   size_t MaxWireSize() const;
priority()157   uint16_t priority() const { return priority_; }
weight()158   uint16_t weight() const { return weight_; }
port()159   uint16_t port() const { return port_; }
target()160   const DomainName& target() const { return target_; }
161 
162   template <typename H>
AbslHashValue(H h,const SrvRecordRdata & rdata)163   friend H AbslHashValue(H h, const SrvRecordRdata& rdata) {
164     return H::combine(std::move(h), rdata.priority_, rdata.weight_, rdata.port_,
165                       rdata.target_);
166   }
167 
168  private:
169   uint16_t priority_ = 0;
170   uint16_t weight_ = 0;
171   uint16_t port_ = 0;
172   DomainName target_;
173 };
174 
175 // A Record format (http://www.ietf.org/rfc/rfc1035.txt):
176 // 4 bytes for IP address.
177 class ARecordRdata {
178  public:
179   ARecordRdata();
180   explicit ARecordRdata(IPAddress ipv4_address,
181                         NetworkInterfaceIndex interface_index = 0);
182   ARecordRdata(const ARecordRdata& other);
183   ARecordRdata(ARecordRdata&& other) noexcept;
184 
185   ARecordRdata& operator=(const ARecordRdata& rhs);
186   ARecordRdata& operator=(ARecordRdata&& rhs);
187   bool operator==(const ARecordRdata& rhs) const;
188   bool operator!=(const ARecordRdata& rhs) const;
189 
190   size_t MaxWireSize() const;
ipv4_address()191   const IPAddress& ipv4_address() const { return ipv4_address_; }
interface_index()192   NetworkInterfaceIndex interface_index() const { return interface_index_; }
193 
194   template <typename H>
AbslHashValue(H h,const ARecordRdata & rdata)195   friend H AbslHashValue(H h, const ARecordRdata& rdata) {
196     const auto& bytes = rdata.ipv4_address_.bytes();
197     return H::combine_contiguous(std::move(h), bytes, 4);
198   }
199 
200  private:
201   IPAddress ipv4_address_{0, 0, 0, 0};
202   NetworkInterfaceIndex interface_index_;
203 };
204 
205 // AAAA Record format (http://www.ietf.org/rfc/rfc1035.txt):
206 // 16 bytes for IP address.
207 class AAAARecordRdata {
208  public:
209   AAAARecordRdata();
210   explicit AAAARecordRdata(IPAddress ipv6_address,
211                            NetworkInterfaceIndex interface_index = 0);
212   AAAARecordRdata(const AAAARecordRdata& other);
213   AAAARecordRdata(AAAARecordRdata&& other) noexcept;
214 
215   AAAARecordRdata& operator=(const AAAARecordRdata& rhs);
216   AAAARecordRdata& operator=(AAAARecordRdata&& rhs);
217   bool operator==(const AAAARecordRdata& rhs) const;
218   bool operator!=(const AAAARecordRdata& rhs) const;
219 
220   size_t MaxWireSize() const;
ipv6_address()221   const IPAddress& ipv6_address() const { return ipv6_address_; }
interface_index()222   NetworkInterfaceIndex interface_index() const { return interface_index_; }
223 
224   template <typename H>
AbslHashValue(H h,const AAAARecordRdata & rdata)225   friend H AbslHashValue(H h, const AAAARecordRdata& rdata) {
226     const auto& bytes = rdata.ipv6_address_.bytes();
227     return H::combine_contiguous(std::move(h), bytes, 16);
228   }
229 
230  private:
231   IPAddress ipv6_address_{0x0000, 0x0000, 0x0000, 0x0000,
232                           0x0000, 0x0000, 0x0000, 0x0000};
233   NetworkInterfaceIndex interface_index_;
234 };
235 
236 // PTR record format (http://www.ietf.org/rfc/rfc1035.txt):
237 // domain: On the wire representation of domain name.
238 class PtrRecordRdata {
239  public:
240   PtrRecordRdata();
241   explicit PtrRecordRdata(DomainName ptr_domain);
242   PtrRecordRdata(const PtrRecordRdata& other);
243   PtrRecordRdata(PtrRecordRdata&& other) noexcept;
244 
245   PtrRecordRdata& operator=(const PtrRecordRdata& rhs);
246   PtrRecordRdata& operator=(PtrRecordRdata&& rhs);
247   bool operator==(const PtrRecordRdata& rhs) const;
248   bool operator!=(const PtrRecordRdata& rhs) const;
249 
250   size_t MaxWireSize() const;
ptr_domain()251   const DomainName& ptr_domain() const { return ptr_domain_; }
252 
253   template <typename H>
AbslHashValue(H h,const PtrRecordRdata & rdata)254   friend H AbslHashValue(H h, const PtrRecordRdata& rdata) {
255     return H::combine(std::move(h), rdata.ptr_domain_);
256   }
257 
258  private:
259   DomainName ptr_domain_;
260 };
261 
262 // TXT record format (http://www.ietf.org/rfc/rfc1035.txt).
263 // texts: One or more <entries>.
264 // An <entry> is a length octet followed by as many data octets.
265 //
266 // DNS-SD interprets <entries> as a list of boolean keys and key=value
267 // attributes.  See https://tools.ietf.org/html/rfc6763#section-6 for details.
268 class TxtRecordRdata {
269  public:
270   using Entry = std::vector<uint8_t>;
271 
272   static ErrorOr<TxtRecordRdata> TryCreate(std::vector<Entry> texts);
273 
274   TxtRecordRdata();
275   explicit TxtRecordRdata(std::vector<Entry> texts);
276   TxtRecordRdata(const TxtRecordRdata& other);
277   TxtRecordRdata(TxtRecordRdata&& other) noexcept;
278 
279   TxtRecordRdata& operator=(const TxtRecordRdata& rhs);
280   TxtRecordRdata& operator=(TxtRecordRdata&& rhs);
281   bool operator==(const TxtRecordRdata& rhs) const;
282   bool operator!=(const TxtRecordRdata& rhs) const;
283 
284   size_t MaxWireSize() const;
285   // NOTE: TXT entries are not guaranteed to be character data.
texts()286   const std::vector<std::string>& texts() const { return texts_; }
287 
288   template <typename H>
AbslHashValue(H h,const TxtRecordRdata & rdata)289   friend H AbslHashValue(H h, const TxtRecordRdata& rdata) {
290     return H::combine(std::move(h), rdata.texts_);
291   }
292 
293  private:
294   TxtRecordRdata(std::vector<std::string> texts, size_t max_wire_size);
295 
296   // max_wire_size_ is at least 3, uint16_t record length and at the
297   // minimum a NULL byte character string is present.
298   size_t max_wire_size_ = 3;
299   // NOTE: For compatibility with DNS-SD usage, std::string is used for internal
300   // storage.
301   std::vector<std::string> texts_;
302 };
303 
304 // NSEC record format (https://tools.ietf.org/html/rfc4034#section-4).
305 // In mDNS, this record type is used for representing negative responses to
306 // queries.
307 //
308 // next_domain_name: The next domain to process. In mDNS, this value is expected
309 // to match the record-level domain name in a negative response.
310 //
311 // An example of how the |types_| vector is serialized is as follows:
312 // When encoding the following DNS types:
313 // - A (value 1)
314 // - MX (value 15)
315 // - RRSIG (value 46)
316 // - NSEC (value 47)
317 // - TYPE1234 (value 1234)
318 // The result would be:
319 //          0x00 0x06 0x40 0x01 0x00 0x00 0x00 0x03
320 //          0x04 0x1b 0x00 0x00 0x00 0x00 0x00 0x00
321 //          0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
322 //          0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
323 //          0x00 0x00 0x00 0x00 0x20
324 class NsecRecordRdata {
325  public:
326   NsecRecordRdata();
327 
328   // Constructor that takes an arbitrary number of DnsType parameters.
329   // NOTE: If `types...` provide a valid set of parameters for an
330   // std::vector<DnsType> ctor call, this will compile. Do not use this ctor
331   // except to provide multiple DnsType parameters.
332   template <typename... Types>
NsecRecordRdata(DomainName next_domain_name,Types...types)333   NsecRecordRdata(DomainName next_domain_name, Types... types)
334       : NsecRecordRdata(std::move(next_domain_name),
335                         std::vector<DnsType>{types...}) {}
336   NsecRecordRdata(DomainName next_domain_name, std::vector<DnsType> types);
337   NsecRecordRdata(const NsecRecordRdata& other);
338   NsecRecordRdata(NsecRecordRdata&& other) noexcept;
339 
340   NsecRecordRdata& operator=(const NsecRecordRdata& rhs);
341   NsecRecordRdata& operator=(NsecRecordRdata&& rhs);
342   bool operator==(const NsecRecordRdata& rhs) const;
343   bool operator!=(const NsecRecordRdata& rhs) const;
344 
345   size_t MaxWireSize() const;
346 
next_domain_name()347   const DomainName& next_domain_name() const { return next_domain_name_; }
types()348   const std::vector<DnsType>& types() const { return types_; }
encoded_types()349   const std::vector<uint8_t>& encoded_types() const { return encoded_types_; }
350 
351   template <typename H>
AbslHashValue(H h,const NsecRecordRdata & rdata)352   friend H AbslHashValue(H h, const NsecRecordRdata& rdata) {
353     return H::combine(std::move(h), rdata.types_, rdata.next_domain_name_);
354   }
355 
356  private:
357   std::vector<uint8_t> encoded_types_;
358   std::vector<DnsType> types_;
359   DomainName next_domain_name_;
360 };
361 
362 // The OPT pseudo-record / meta-record as defined by RFC6891.
363 class OptRecordRdata {
364  public:
365   // A single option as defined in RFC6891 section 6.1.2.
366   struct Option {
367     size_t MaxWireSize() const;
368 
369     bool operator>(const Option& rhs) const;
370     bool operator<(const Option& rhs) const;
371     bool operator>=(const Option& rhs) const;
372     bool operator<=(const Option& rhs) const;
373     bool operator==(const Option& rhs) const;
374     bool operator!=(const Option& rhs) const;
375 
376     template <typename H>
AbslHashValueOption377     friend H AbslHashValue(H h, const Option& option) {
378       return H::combine(std::move(h), option.code, option.length, option.data);
379     }
380 
381     // Code assigned by the Expert Review process as defined by the DNSEXT
382     // working group and the IESG, as specified in RFC6891 section 9.1. For
383     // specific assignments, see:
384     // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
385     uint16_t code;
386 
387     // Size (in octets) of |data|.
388     uint16_t length;
389 
390     // Bit Field with meaning varying based on |code|.
391     std::vector<uint8_t> data;
392   };
393 
394   OptRecordRdata();
395 
396   // Constructor that takes zero or more Option parameters.
397   template <typename... Types>
OptRecordRdata(Types...types)398   explicit OptRecordRdata(Types... types)
399       : OptRecordRdata(std::vector<Option>{std::move(types)...}) {}
400   explicit OptRecordRdata(std::vector<Option> options);
401   OptRecordRdata(const OptRecordRdata& other);
402   OptRecordRdata(OptRecordRdata&& other) noexcept;
403 
404   OptRecordRdata& operator=(const OptRecordRdata& rhs);
405   OptRecordRdata& operator=(OptRecordRdata&& rhs);
406 
407   // NOTE: Only the options field is technically considered part of the rdata,
408   // so only this field is considered for equality comparison. The other fields
409   // are included here solely because their meaning differs for OPT pseudo-
410   // records and normal record types.
411   bool operator==(const OptRecordRdata& rhs) const;
412   bool operator!=(const OptRecordRdata& rhs) const;
413 
MaxWireSize()414   size_t MaxWireSize() const { return max_wire_size_; }
415 
416   // Set of options stored in this OPT record.
options()417   const std::vector<Option>& options() { return options_; }
418 
419   template <typename H>
AbslHashValue(H h,const OptRecordRdata & rdata)420   friend H AbslHashValue(H h, const OptRecordRdata& rdata) {
421     return H::combine(std::move(h), rdata.options_);
422   }
423 
424  private:
425   // NOTE: The elements of |options_| are stored is sorted order to simplify the
426   // comparison operators of OptRecordRdata.
427   std::vector<Option> options_;
428 
429   size_t max_wire_size_ = 0;
430 };
431 
432 using Rdata = absl::variant<RawRecordRdata,
433                             SrvRecordRdata,
434                             ARecordRdata,
435                             AAAARecordRdata,
436                             PtrRecordRdata,
437                             TxtRecordRdata,
438                             NsecRecordRdata,
439                             OptRecordRdata>;
440 
441 // Resource record top level format (http://www.ietf.org/rfc/rfc1035.txt):
442 // name: the name of the node to which this resource record pertains.
443 // type: 2 bytes network-order RR TYPE code.
444 // class: 2 bytes network-order RR CLASS code.
445 // ttl: 4 bytes network-order cache time interval.
446 // rdata:  RDATA describing the resource.  The format of this information varies
447 // according to the TYPE and CLASS of the resource record.
448 class MdnsRecord {
449  public:
450   using ConstRef = std::reference_wrapper<const MdnsRecord>;
451 
452   static ErrorOr<MdnsRecord> TryCreate(DomainName name,
453                                        DnsType dns_type,
454                                        DnsClass dns_class,
455                                        RecordType record_type,
456                                        std::chrono::seconds ttl,
457                                        Rdata rdata);
458 
459   MdnsRecord();
460   MdnsRecord(DomainName name,
461              DnsType dns_type,
462              DnsClass dns_class,
463              RecordType record_type,
464              std::chrono::seconds ttl,
465              Rdata rdata);
466   MdnsRecord(const MdnsRecord& other);
467   MdnsRecord(MdnsRecord&& other) noexcept;
468 
469   MdnsRecord& operator=(const MdnsRecord& rhs);
470   MdnsRecord& operator=(MdnsRecord&& rhs);
471   bool operator==(const MdnsRecord& other) const;
472   bool operator!=(const MdnsRecord& other) const;
473   bool operator<(const MdnsRecord& other) const;
474   bool operator>(const MdnsRecord& other) const;
475   bool operator<=(const MdnsRecord& other) const;
476   bool operator>=(const MdnsRecord& other) const;
477 
478   // While not being "equivalent", a record could be said to be an update of
479   // a different record if it is the same, excepting TTL.
480   bool IsReannouncementOf(const MdnsRecord& other) const;
481   size_t MaxWireSize() const;
name()482   const DomainName& name() const { return name_; }
dns_type()483   DnsType dns_type() const { return dns_type_; }
dns_class()484   DnsClass dns_class() const { return dns_class_; }
record_type()485   RecordType record_type() const { return record_type_; }
ttl()486   std::chrono::seconds ttl() const { return ttl_; }
rdata()487   const Rdata& rdata() const { return rdata_; }
488 
489   template <typename H>
AbslHashValue(H h,const MdnsRecord & record)490   friend H AbslHashValue(H h, const MdnsRecord& record) {
491     return H::combine(std::move(h), record.name_, record.dns_type_,
492                       record.dns_class_, record.record_type_,
493                       record.ttl_.count(), record.rdata_);
494   }
495 
496   std::string ToString() const;
497 
498  private:
499   static bool IsValidConfig(const DomainName& name,
500                             DnsType dns_type,
501                             std::chrono::seconds ttl,
502                             const Rdata& rdata);
503 
504   DomainName name_;
505   DnsType dns_type_ = static_cast<DnsType>(0);
506   DnsClass dns_class_ = static_cast<DnsClass>(0);
507   RecordType record_type_ = RecordType::kShared;
508   std::chrono::seconds ttl_{kDefaultRecordTTLSeconds};
509   // Default-constructed Rdata contains default-constructed RawRecordRdata
510   // as it is the first alternative type and it is default-constructible.
511   Rdata rdata_;
512 };
513 
514 // Creates an A or AAAA record as appropriate for the provided parameters.
515 MdnsRecord CreateAddressRecord(DomainName name, const IPAddress& address);
516 
517 // Question top level format (http://www.ietf.org/rfc/rfc1035.txt):
518 // name: a domain name which identifies the target resource set.
519 // type: 2 bytes network-order RR TYPE code.
520 // class: 2 bytes network-order RR CLASS code.
521 class MdnsQuestion {
522  public:
523   static ErrorOr<MdnsQuestion> TryCreate(DomainName name,
524                                          DnsType dns_type,
525                                          DnsClass dns_class,
526                                          ResponseType response_type);
527 
528   MdnsQuestion() = default;
529   MdnsQuestion(DomainName name,
530                DnsType dns_type,
531                DnsClass dns_class,
532                ResponseType response_type);
533 
534   bool operator==(const MdnsQuestion& other) const;
535   bool operator!=(const MdnsQuestion& other) const;
536 
537   size_t MaxWireSize() const;
name()538   const DomainName& name() const { return name_; }
dns_type()539   DnsType dns_type() const { return dns_type_; }
dns_class()540   DnsClass dns_class() const { return dns_class_; }
response_type()541   ResponseType response_type() const { return response_type_; }
542 
543   template <typename H>
AbslHashValue(H h,const MdnsQuestion & record)544   friend H AbslHashValue(H h, const MdnsQuestion& record) {
545     return H::combine(std::move(h), record.name_, record.dns_type_,
546                       record.dns_class_, record.response_type_);
547   }
548 
549  private:
550   void CopyFrom(const MdnsQuestion& other);
551 
552   DomainName name_;
553   DnsType dns_type_ = static_cast<DnsType>(0);
554   DnsClass dns_class_ = static_cast<DnsClass>(0);
555   ResponseType response_type_ = ResponseType::kMulticast;
556 };
557 
558 // Message top level format (http://www.ietf.org/rfc/rfc1035.txt):
559 // id: 2 bytes network-order identifier assigned by the program that generates
560 // any kind of query. This identifier is copied to the corresponding reply and
561 // can be used by the requester to match up replies to outstanding queries.
562 // flags: 2 bytes network-order flags bitfield.
563 // questions: questions in the message.
564 // answers: resource records that answer the questions.
565 // authority_records: resource records that point toward authoritative name.
566 // servers additional_records: additional resource records that relate to the
567 // query.
568 class MdnsMessage {
569  public:
570   static ErrorOr<MdnsMessage> TryCreate(
571       uint16_t id,
572       MessageType type,
573       std::vector<MdnsQuestion> questions,
574       std::vector<MdnsRecord> answers,
575       std::vector<MdnsRecord> authority_records,
576       std::vector<MdnsRecord> additional_records);
577 
578   MdnsMessage() = default;
579   // Constructs a message with ID, flags and empty question, answer, authority
580   // and additional record collections.
581   MdnsMessage(uint16_t id, MessageType type);
582   MdnsMessage(uint16_t id,
583               MessageType type,
584               std::vector<MdnsQuestion> questions,
585               std::vector<MdnsRecord> answers,
586               std::vector<MdnsRecord> authority_records,
587               std::vector<MdnsRecord> additional_records);
588 
589   bool operator==(const MdnsMessage& other) const;
590   bool operator!=(const MdnsMessage& other) const;
591 
592   void AddQuestion(MdnsQuestion question);
593   void AddAnswer(MdnsRecord record);
594   void AddAuthorityRecord(MdnsRecord record);
595   void AddAdditionalRecord(MdnsRecord record);
596 
597   // Returns false if adding a new record would push the size of this message
598   // beyond kMaxMulticastMessageSize, and true otherwise.
599   bool CanAddRecord(const MdnsRecord& record);
600 
601   // Sets the truncated bit (TC), as specified in RFC 1035 Section 4.1.1.
set_truncated()602   void set_truncated() { is_truncated_ = true; }
603 
604   // Returns true if the provided message is an mDNS probe query as described in
605   // RFC 6762 section 8.1. Specifically, it examines whether any question in
606   // the 'questions' section is a query for which answers are present in the
607   // 'authority records' section of the same message.
608   bool IsProbeQuery() const;
609 
610   size_t MaxWireSize() const;
id()611   uint16_t id() const { return id_; }
type()612   MessageType type() const { return type_; }
is_truncated()613   bool is_truncated() const { return is_truncated_; }
questions()614   const std::vector<MdnsQuestion>& questions() const { return questions_; }
answers()615   const std::vector<MdnsRecord>& answers() const { return answers_; }
authority_records()616   const std::vector<MdnsRecord>& authority_records() const {
617     return authority_records_;
618   }
additional_records()619   const std::vector<MdnsRecord>& additional_records() const {
620     return additional_records_;
621   }
622 
623   template <typename H>
AbslHashValue(H h,const MdnsMessage & message)624   friend H AbslHashValue(H h, const MdnsMessage& message) {
625     return H::combine(std::move(h), message.id_, message.type_,
626                       message.questions_, message.answers_,
627                       message.authority_records_, message.additional_records_);
628   }
629 
630  private:
631   // The mDNS header is 12 bytes long
632   size_t max_wire_size_ = sizeof(Header);
633   uint16_t id_ = 0;
634   bool is_truncated_ = false;
635   MessageType type_ = MessageType::Query;
636   std::vector<MdnsQuestion> questions_;
637   std::vector<MdnsRecord> answers_;
638   std::vector<MdnsRecord> authority_records_;
639   std::vector<MdnsRecord> additional_records_;
640 };
641 
642 uint16_t CreateMessageId();
643 
644 // Determines whether a record of the given type can be published.
645 bool CanBePublished(DnsType type);
646 
647 // Determines whether a record of the given type can be queried for.
648 bool CanBeQueried(DnsType type);
649 
650 // Determines whether a record of the given type received over the network
651 // should be processed.
652 bool CanBeProcessed(DnsType type);
653 
654 }  // namespace discovery
655 }  // namespace openscreen
656 
657 #endif  // DISCOVERY_MDNS_MDNS_RECORDS_H_
658