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