xref: /aosp_15_r20/external/cronet/net/dns/dns_response.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/dns/dns_response.h"
6 
7 #include <algorithm>
8 #include <cstdint>
9 #include <limits>
10 #include <numeric>
11 #include <optional>
12 #include <string_view>
13 #include <utility>
14 #include <vector>
15 
16 #include "base/big_endian.h"
17 #include "base/containers/span.h"
18 #include "base/containers/span_reader.h"
19 #include "base/containers/span_writer.h"
20 #include "base/logging.h"
21 #include "base/numerics/safe_conversions.h"
22 #include "base/strings/string_util.h"
23 #include "base/sys_byteorder.h"
24 #include "base/types/optional_util.h"
25 #include "net/base/io_buffer.h"
26 #include "net/base/net_errors.h"
27 #include "net/dns/dns_names_util.h"
28 #include "net/dns/dns_query.h"
29 #include "net/dns/dns_response_result_extractor.h"
30 #include "net/dns/dns_util.h"
31 #include "net/dns/public/dns_protocol.h"
32 #include "net/dns/record_rdata.h"
33 
34 namespace net {
35 
36 namespace {
37 
38 const size_t kHeaderSize = sizeof(dns_protocol::Header);
39 
40 const uint8_t kRcodeMask = 0xf;
41 
42 }  // namespace
43 
44 DnsResourceRecord::DnsResourceRecord() = default;
45 
DnsResourceRecord(const DnsResourceRecord & other)46 DnsResourceRecord::DnsResourceRecord(const DnsResourceRecord& other)
47     : name(other.name),
48       type(other.type),
49       klass(other.klass),
50       ttl(other.ttl),
51       owned_rdata(other.owned_rdata) {
52   if (!owned_rdata.empty())
53     rdata = owned_rdata;
54   else
55     rdata = other.rdata;
56 }
57 
DnsResourceRecord(DnsResourceRecord && other)58 DnsResourceRecord::DnsResourceRecord(DnsResourceRecord&& other)
59     : name(std::move(other.name)),
60       type(other.type),
61       klass(other.klass),
62       ttl(other.ttl),
63       owned_rdata(std::move(other.owned_rdata)) {
64   if (!owned_rdata.empty())
65     rdata = owned_rdata;
66   else
67     rdata = other.rdata;
68 }
69 
70 DnsResourceRecord::~DnsResourceRecord() = default;
71 
operator =(const DnsResourceRecord & other)72 DnsResourceRecord& DnsResourceRecord::operator=(
73     const DnsResourceRecord& other) {
74   name = other.name;
75   type = other.type;
76   klass = other.klass;
77   ttl = other.ttl;
78   owned_rdata = other.owned_rdata;
79 
80   if (!owned_rdata.empty())
81     rdata = owned_rdata;
82   else
83     rdata = other.rdata;
84 
85   return *this;
86 }
87 
operator =(DnsResourceRecord && other)88 DnsResourceRecord& DnsResourceRecord::operator=(DnsResourceRecord&& other) {
89   name = std::move(other.name);
90   type = other.type;
91   klass = other.klass;
92   ttl = other.ttl;
93   owned_rdata = std::move(other.owned_rdata);
94 
95   if (!owned_rdata.empty())
96     rdata = owned_rdata;
97   else
98     rdata = other.rdata;
99 
100   return *this;
101 }
102 
SetOwnedRdata(std::string value)103 void DnsResourceRecord::SetOwnedRdata(std::string value) {
104   DCHECK(!value.empty());
105   owned_rdata = std::move(value);
106   rdata = owned_rdata;
107   DCHECK_EQ(owned_rdata.data(), rdata.data());
108 }
109 
CalculateRecordSize() const110 size_t DnsResourceRecord::CalculateRecordSize() const {
111   bool has_final_dot = name.back() == '.';
112   // Depending on if |name| in the dotted format has the final dot for the root
113   // domain or not, the corresponding wire data in the DNS domain name format is
114   // 1 byte (with dot) or 2 bytes larger in size. See RFC 1035, Section 3.1 and
115   // DNSDomainFromDot.
116   return name.size() + (has_final_dot ? 1 : 2) +
117          net::dns_protocol::kResourceRecordSizeInBytesWithoutNameAndRData +
118          (owned_rdata.empty() ? rdata.size() : owned_rdata.size());
119 }
120 
121 DnsRecordParser::DnsRecordParser() = default;
122 
DnsRecordParser(base::span<const uint8_t> packet,size_t offset,size_t num_records)123 DnsRecordParser::DnsRecordParser(base::span<const uint8_t> packet,
124                                  size_t offset,
125                                  size_t num_records)
126     : packet_(packet), num_records_(num_records), cur_(offset) {
127   CHECK_LE(offset, packet_.size());
128 }
129 
DnsRecordParser(const void * packet,size_t length,size_t offset,size_t num_records)130 DnsRecordParser::DnsRecordParser(const void* packet,
131                                  size_t length,
132                                  size_t offset,
133                                  size_t num_records)
134     : DnsRecordParser(
135           // TODO(crbug.com/40284755): This span construction can not be sound
136           // here. This DnsRecordParser constructor should be removed.
137           UNSAFE_BUFFERS(
138               base::span(static_cast<const uint8_t*>(packet), length)),
139           offset,
140           num_records) {}
141 
ReadName(const void * const vpos,std::string * out) const142 unsigned DnsRecordParser::ReadName(const void* const vpos,
143                                    std::string* out) const {
144   static const char kAbortMsg[] = "Abort parsing of noncompliant DNS record.";
145 
146   CHECK_LE(packet_.data(), vpos);
147   CHECK_LE(vpos, packet_.last(0u).data());
148   const size_t initial_offset =
149       // SAFETY: `vpos` points into the span, as verified by the CHECKs above,
150       // so subtracting the data pointer is well-defined and gives an offset
151       // into the span.
152       //
153       // TODO(danakj): Since we need an offset anyway, no unsafe pointer usage
154       // would be required, and fewer CHECKs, if this function took an offset
155       // instead of a pointer.
156       UNSAFE_BUFFERS(static_cast<const uint8_t*>(vpos) - packet_.data());
157 
158   if (initial_offset == packet_.size()) {
159     return 0;
160   }
161 
162   size_t offset = initial_offset;
163   // Count number of seen bytes to detect loops.
164   unsigned seen = 0u;
165   // Remember how many bytes were consumed before first jump.
166   unsigned consumed = 0u;
167   // The length of the encoded name (sum of label octets and label lengths).
168   // For context, RFC 1034 states that the total number of octets representing a
169   // domain name (the sum of all label octets and label lengths) is limited to
170   // 255. RFC 1035 introduces message compression as a way to reduce packet size
171   // on the wire, not to increase the maximum domain name length.
172   unsigned encoded_name_len = 0u;
173 
174   if (out) {
175     out->clear();
176     out->reserve(dns_protocol::kMaxCharNameLength);
177   }
178 
179   for (;;) {
180     // The first two bits of the length give the type of the length. It's
181     // either a direct length or a pointer to the remainder of the name.
182     switch (packet_[offset] & dns_protocol::kLabelMask) {
183       case dns_protocol::kLabelPointer: {
184         if (packet_.size() < sizeof(uint16_t) ||
185             offset > packet_.size() - sizeof(uint16_t)) {
186           VLOG(1) << kAbortMsg << " Truncated or missing label pointer.";
187           return 0;
188         }
189         if (consumed == 0u) {
190           consumed = offset - initial_offset + sizeof(uint16_t);
191           if (!out) {
192             return consumed;  // If name is not stored, that's all we need.
193           }
194         }
195         seen += sizeof(uint16_t);
196         // If seen the whole packet, then we must be in a loop.
197         if (seen > packet_.size()) {
198           VLOG(1) << kAbortMsg << " Detected loop in label pointers.";
199           return 0;
200         }
201         uint16_t new_offset =
202             base::U16FromBigEndian(packet_.subspan(offset).first<2u>());
203         offset = new_offset & dns_protocol::kOffsetMask;
204         if (offset >= packet_.size()) {
205           VLOG(1) << kAbortMsg << " Label pointer points outside packet.";
206           return 0;
207         }
208         break;
209       }
210       case dns_protocol::kLabelDirect: {
211         uint8_t label_len = packet_[offset];
212         ++offset;
213         // Note: root domain (".") is NOT included.
214         if (label_len == 0) {
215           if (consumed == 0) {
216             consumed = offset - initial_offset;
217           }  // else we set |consumed| before first jump
218           return consumed;
219         }
220         // Add one octet for the length and |label_len| for the number of
221         // following octets.
222         encoded_name_len += 1 + label_len;
223         if (encoded_name_len > dns_protocol::kMaxNameLength) {
224           VLOG(1) << kAbortMsg << " Name is too long.";
225           return 0;
226         }
227         if (label_len >= packet_.size() - offset) {
228           VLOG(1) << kAbortMsg << " Truncated or missing label.";
229           return 0;  // Truncated or missing label.
230         }
231         if (out) {
232           if (!out->empty())
233             out->append(".");
234           // TODO(danakj): Use append_range() in C++23.
235           auto range = packet_.subspan(offset, label_len);
236           out->append(range.begin(), range.end());
237           CHECK_LE(out->size(), dns_protocol::kMaxCharNameLength);
238         }
239         offset += label_len;
240         seen += 1 + label_len;
241         break;
242       }
243       default:
244         // unhandled label type
245         VLOG(1) << kAbortMsg << " Unhandled label type.";
246         return 0;
247     }
248   }
249 }
250 
ReadRecord(DnsResourceRecord * out)251 bool DnsRecordParser::ReadRecord(DnsResourceRecord* out) {
252   CHECK(!packet_.empty());
253 
254   // Disallow parsing any more than the claimed number of records.
255   if (num_records_parsed_ >= num_records_)
256     return false;
257 
258   size_t consumed = ReadName(packet_.subspan(cur_).data(), &out->name);
259   if (!consumed) {
260     return false;
261   }
262   auto reader = base::SpanReader(packet_.subspan(cur_ + consumed));
263   uint16_t rdlen;
264   if (reader.ReadU16BigEndian(out->type) &&
265       reader.ReadU16BigEndian(out->klass) &&
266       reader.ReadU32BigEndian(out->ttl) &&  //
267       reader.ReadU16BigEndian(rdlen) &&
268       base::OptionalUnwrapTo(reader.Read(rdlen), out->rdata, [](auto span) {
269         return base::as_string_view(span);
270       })) {
271     cur_ += consumed + 2u + 2u + 4u + 2u + rdlen;
272     ++num_records_parsed_;
273     return true;
274   }
275   return false;
276 }
277 
ReadQuestion(std::string & out_dotted_qname,uint16_t & out_qtype)278 bool DnsRecordParser::ReadQuestion(std::string& out_dotted_qname,
279                                    uint16_t& out_qtype) {
280   size_t consumed = ReadName(packet_.subspan(cur_).data(), &out_dotted_qname);
281   if (!consumed)
282     return false;
283 
284   if (consumed + 2 * sizeof(uint16_t) > packet_.size() - cur_) {
285     return false;
286   }
287 
288   out_qtype = base::U16FromBigEndian(
289       packet_.subspan(cur_ + consumed).first<sizeof(uint16_t)>());
290 
291   cur_ += consumed + 2 * sizeof(uint16_t);  // QTYPE + QCLASS
292 
293   return true;
294 }
295 
DnsResponse(uint16_t id,bool is_authoritative,const std::vector<DnsResourceRecord> & answers,const std::vector<DnsResourceRecord> & authority_records,const std::vector<DnsResourceRecord> & additional_records,const std::optional<DnsQuery> & query,uint8_t rcode,bool validate_records,bool validate_names_as_internet_hostnames)296 DnsResponse::DnsResponse(
297     uint16_t id,
298     bool is_authoritative,
299     const std::vector<DnsResourceRecord>& answers,
300     const std::vector<DnsResourceRecord>& authority_records,
301     const std::vector<DnsResourceRecord>& additional_records,
302     const std::optional<DnsQuery>& query,
303     uint8_t rcode,
304     bool validate_records,
305     bool validate_names_as_internet_hostnames) {
306   bool has_query = query.has_value();
307   dns_protocol::Header header;
308   header.id = id;
309   bool success = true;
310   if (has_query) {
311     success &= (id == query.value().id());
312     DCHECK(success);
313     // DnsQuery only supports a single question.
314     header.qdcount = 1;
315   }
316   header.flags |= dns_protocol::kFlagResponse;
317   if (is_authoritative)
318     header.flags |= dns_protocol::kFlagAA;
319   DCHECK_EQ(0, rcode & ~kRcodeMask);
320   header.flags |= rcode;
321 
322   header.ancount = answers.size();
323   header.nscount = authority_records.size();
324   header.arcount = additional_records.size();
325 
326   // Response starts with the header and the question section (if any).
327   size_t response_size = has_query
328                              ? sizeof(header) + query.value().question_size()
329                              : sizeof(header);
330   // Add the size of all answers and additional records.
331   auto do_accumulation = [](size_t cur_size, const DnsResourceRecord& record) {
332     return cur_size + record.CalculateRecordSize();
333   };
334   response_size = std::accumulate(answers.begin(), answers.end(), response_size,
335                                   do_accumulation);
336   response_size =
337       std::accumulate(authority_records.begin(), authority_records.end(),
338                       response_size, do_accumulation);
339   response_size =
340       std::accumulate(additional_records.begin(), additional_records.end(),
341                       response_size, do_accumulation);
342 
343   auto io_buffer = base::MakeRefCounted<IOBufferWithSize>(response_size);
344   auto writer = base::SpanWriter(base::as_writable_bytes(io_buffer->span()));
345   success &= WriteHeader(&writer, header);
346   DCHECK(success);
347   if (has_query) {
348     success &= WriteQuestion(&writer, query.value());
349     DCHECK(success);
350   }
351   // Start the Answer section.
352   for (const auto& answer : answers) {
353     success &= WriteAnswer(&writer, answer, query, validate_records,
354                            validate_names_as_internet_hostnames);
355     DCHECK(success);
356   }
357   // Start the Authority section.
358   for (const auto& record : authority_records) {
359     success &= WriteRecord(&writer, record, validate_records,
360                            validate_names_as_internet_hostnames);
361     DCHECK(success);
362   }
363   // Start the Additional section.
364   for (const auto& record : additional_records) {
365     success &= WriteRecord(&writer, record, validate_records,
366                            validate_names_as_internet_hostnames);
367     DCHECK(success);
368   }
369   if (!success) {
370     return;
371   }
372   io_buffer_ = io_buffer;
373   io_buffer_size_ = response_size;
374   // Ensure we don't have any remaining uninitialized bytes in the buffer.
375   DCHECK_EQ(writer.remaining(), 0u);
376   std::ranges::fill(writer.remaining_span(), uint8_t{0});
377   if (has_query)
378     InitParse(io_buffer_size_, query.value());
379   else
380     InitParseWithoutQuery(io_buffer_size_);
381 }
382 
DnsResponse()383 DnsResponse::DnsResponse()
384     : io_buffer_(base::MakeRefCounted<IOBufferWithSize>(
385           dns_protocol::kMaxUDPSize + 1)),
386       io_buffer_size_(dns_protocol::kMaxUDPSize + 1) {}
387 
DnsResponse(scoped_refptr<IOBuffer> buffer,size_t size)388 DnsResponse::DnsResponse(scoped_refptr<IOBuffer> buffer, size_t size)
389     : io_buffer_(std::move(buffer)), io_buffer_size_(size) {}
390 
DnsResponse(size_t length)391 DnsResponse::DnsResponse(size_t length)
392     : io_buffer_(base::MakeRefCounted<IOBufferWithSize>(length)),
393       io_buffer_size_(length) {}
394 
DnsResponse(const void * data,size_t length,size_t answer_offset)395 DnsResponse::DnsResponse(const void* data, size_t length, size_t answer_offset)
396     : io_buffer_(base::MakeRefCounted<IOBufferWithSize>(length)),
397       io_buffer_size_(length),
398       parser_(io_buffer_->data(),
399               length,
400               answer_offset,
401               std::numeric_limits<size_t>::max()) {
402   DCHECK(data);
403   std::copy(static_cast<const char*>(data),
404             static_cast<const char*>(data) + length, io_buffer_->data());
405 }
406 
407 // static
CreateEmptyNoDataResponse(uint16_t id,bool is_authoritative,base::span<const uint8_t> qname,uint16_t qtype)408 DnsResponse DnsResponse::CreateEmptyNoDataResponse(
409     uint16_t id,
410     bool is_authoritative,
411     base::span<const uint8_t> qname,
412     uint16_t qtype) {
413   return DnsResponse(id, is_authoritative,
414                      /*answers=*/{},
415                      /*authority_records=*/{},
416                      /*additional_records=*/{}, DnsQuery(id, qname, qtype));
417 }
418 
419 DnsResponse::DnsResponse(DnsResponse&& other) = default;
420 DnsResponse& DnsResponse::operator=(DnsResponse&& other) = default;
421 
422 DnsResponse::~DnsResponse() = default;
423 
InitParse(size_t nbytes,const DnsQuery & query)424 bool DnsResponse::InitParse(size_t nbytes, const DnsQuery& query) {
425   const std::string_view question = query.question();
426 
427   // Response includes question, it should be at least that size.
428   if (nbytes < kHeaderSize + question.size() || nbytes > io_buffer_size_) {
429     return false;
430   }
431 
432   // At this point, it has been validated that the response is at least large
433   // enough to read the ID field.
434   id_available_ = true;
435 
436   // Match the query id.
437   DCHECK(id());
438   if (id().value() != query.id())
439     return false;
440 
441   // Not a response?
442   if ((base::NetToHost16(header()->flags) & dns_protocol::kFlagResponse) == 0)
443     return false;
444 
445   // Match question count.
446   if (base::NetToHost16(header()->qdcount) != 1)
447     return false;
448 
449   // Match the question section.
450   if (question !=
451       std::string_view(io_buffer_->data() + kHeaderSize, question.size())) {
452     return false;
453   }
454 
455   std::optional<std::string> dotted_qname =
456       dns_names_util::NetworkToDottedName(query.qname());
457   if (!dotted_qname.has_value())
458     return false;
459   dotted_qnames_.push_back(std::move(dotted_qname).value());
460   qtypes_.push_back(query.qtype());
461 
462   size_t num_records = base::NetToHost16(header()->ancount) +
463                        base::NetToHost16(header()->nscount) +
464                        base::NetToHost16(header()->arcount);
465 
466   // Construct the parser. Only allow parsing up to `num_records` records. If
467   // more records are present in the buffer, it's just garbage extra data after
468   // the formal end of the response and should be ignored.
469   parser_ = DnsRecordParser(io_buffer_->data(), nbytes,
470                             kHeaderSize + question.size(), num_records);
471   return true;
472 }
473 
InitParseWithoutQuery(size_t nbytes)474 bool DnsResponse::InitParseWithoutQuery(size_t nbytes) {
475   if (nbytes < kHeaderSize || nbytes > io_buffer_size_) {
476     return false;
477   }
478   id_available_ = true;
479 
480   // Not a response?
481   if ((base::NetToHost16(header()->flags) & dns_protocol::kFlagResponse) == 0)
482     return false;
483 
484   size_t num_records = base::NetToHost16(header()->ancount) +
485                        base::NetToHost16(header()->nscount) +
486                        base::NetToHost16(header()->arcount);
487   // Only allow parsing up to `num_records` records. If more records are present
488   // in the buffer, it's just garbage extra data after the formal end of the
489   // response and should be ignored.
490   parser_ =
491       DnsRecordParser(io_buffer_->data(), nbytes, kHeaderSize, num_records);
492 
493   unsigned qdcount = base::NetToHost16(header()->qdcount);
494   for (unsigned i = 0; i < qdcount; ++i) {
495     std::string dotted_qname;
496     uint16_t qtype;
497     if (!parser_.ReadQuestion(dotted_qname, qtype)) {
498       parser_ = DnsRecordParser();  // Make parser invalid again.
499       return false;
500     }
501     dotted_qnames_.push_back(std::move(dotted_qname));
502     qtypes_.push_back(qtype);
503   }
504 
505   return true;
506 }
507 
id() const508 std::optional<uint16_t> DnsResponse::id() const {
509   if (!id_available_)
510     return std::nullopt;
511 
512   return base::NetToHost16(header()->id);
513 }
514 
IsValid() const515 bool DnsResponse::IsValid() const {
516   return parser_.IsValid();
517 }
518 
flags() const519 uint16_t DnsResponse::flags() const {
520   DCHECK(parser_.IsValid());
521   return base::NetToHost16(header()->flags) & ~(kRcodeMask);
522 }
523 
rcode() const524 uint8_t DnsResponse::rcode() const {
525   DCHECK(parser_.IsValid());
526   return base::NetToHost16(header()->flags) & kRcodeMask;
527 }
528 
question_count() const529 unsigned DnsResponse::question_count() const {
530   DCHECK(parser_.IsValid());
531   return base::NetToHost16(header()->qdcount);
532 }
533 
answer_count() const534 unsigned DnsResponse::answer_count() const {
535   DCHECK(parser_.IsValid());
536   return base::NetToHost16(header()->ancount);
537 }
538 
authority_count() const539 unsigned DnsResponse::authority_count() const {
540   DCHECK(parser_.IsValid());
541   return base::NetToHost16(header()->nscount);
542 }
543 
additional_answer_count() const544 unsigned DnsResponse::additional_answer_count() const {
545   DCHECK(parser_.IsValid());
546   return base::NetToHost16(header()->arcount);
547 }
548 
GetSingleQType() const549 uint16_t DnsResponse::GetSingleQType() const {
550   DCHECK_EQ(qtypes().size(), 1u);
551   return qtypes().front();
552 }
553 
GetSingleDottedName() const554 std::string_view DnsResponse::GetSingleDottedName() const {
555   DCHECK_EQ(dotted_qnames().size(), 1u);
556   return dotted_qnames().front();
557 }
558 
Parser() const559 DnsRecordParser DnsResponse::Parser() const {
560   DCHECK(parser_.IsValid());
561   // Return a copy of the parser.
562   return parser_;
563 }
564 
header() const565 const dns_protocol::Header* DnsResponse::header() const {
566   return reinterpret_cast<const dns_protocol::Header*>(io_buffer_->data());
567 }
568 
WriteHeader(base::SpanWriter<uint8_t> * writer,const dns_protocol::Header & header)569 bool DnsResponse::WriteHeader(base::SpanWriter<uint8_t>* writer,
570                               const dns_protocol::Header& header) {
571   return writer->WriteU16BigEndian(header.id) &&
572          writer->WriteU16BigEndian(header.flags) &&
573          writer->WriteU16BigEndian(header.qdcount) &&
574          writer->WriteU16BigEndian(header.ancount) &&
575          writer->WriteU16BigEndian(header.nscount) &&
576          writer->WriteU16BigEndian(header.arcount);
577 }
578 
WriteQuestion(base::SpanWriter<uint8_t> * writer,const DnsQuery & query)579 bool DnsResponse::WriteQuestion(base::SpanWriter<uint8_t>* writer,
580                                 const DnsQuery& query) {
581   return writer->Write(base::as_byte_span(query.question()));
582 }
583 
WriteRecord(base::SpanWriter<uint8_t> * writer,const DnsResourceRecord & record,bool validate_record,bool validate_name_as_internet_hostname)584 bool DnsResponse::WriteRecord(base::SpanWriter<uint8_t>* writer,
585                               const DnsResourceRecord& record,
586                               bool validate_record,
587                               bool validate_name_as_internet_hostname) {
588   if (record.rdata != std::string_view(record.owned_rdata)) {
589     VLOG(1) << "record.rdata should point to record.owned_rdata.";
590     return false;
591   }
592 
593   if (validate_record &&
594       !RecordRdata::HasValidSize(record.owned_rdata, record.type)) {
595     VLOG(1) << "Invalid RDATA size for a record.";
596     return false;
597   }
598 
599   std::optional<std::vector<uint8_t>> domain_name =
600       dns_names_util::DottedNameToNetwork(record.name,
601                                           validate_name_as_internet_hostname);
602   if (!domain_name.has_value()) {
603     VLOG(1) << "Invalid dotted name (as "
604             << (validate_name_as_internet_hostname ? "Internet hostname)."
605                                                    : "DNS name).");
606     return false;
607   }
608 
609   return writer->Write(domain_name.value()) &&
610          writer->WriteU16BigEndian(record.type) &&
611          writer->WriteU16BigEndian(record.klass) &&
612          writer->WriteU32BigEndian(record.ttl) &&
613          writer->WriteU16BigEndian(record.owned_rdata.size()) &&
614          // Use the owned RDATA in the record to construct the response.
615          writer->Write(base::as_byte_span(record.owned_rdata));
616 }
617 
WriteAnswer(base::SpanWriter<uint8_t> * writer,const DnsResourceRecord & answer,const std::optional<DnsQuery> & query,bool validate_record,bool validate_name_as_internet_hostname)618 bool DnsResponse::WriteAnswer(base::SpanWriter<uint8_t>* writer,
619                               const DnsResourceRecord& answer,
620                               const std::optional<DnsQuery>& query,
621                               bool validate_record,
622                               bool validate_name_as_internet_hostname) {
623   // Generally assumed to be a mistake if we write answers that don't match the
624   // query type, except CNAME answers which can always be added.
625   if (validate_record && query.has_value() &&
626       answer.type != query.value().qtype() &&
627       answer.type != dns_protocol::kTypeCNAME) {
628     VLOG(1) << "Mismatched answer resource record type and qtype.";
629     return false;
630   }
631   return WriteRecord(writer, answer, validate_record,
632                      validate_name_as_internet_hostname);
633 }
634 
635 }  // namespace net
636