add some logging
[ghsmtp.git] / DNS-message.cpp
blobabc4d6165b23731d3a2ea93e47d290c267f60032
1 #include "DNS-message.hpp"
3 #include "DNS-iostream.hpp"
4 #include "Domain.hpp"
6 #include <arpa/nameser.h>
8 namespace {
9 using octet = DNS::message::octet;
11 octet constexpr lo(uint16_t n) { return octet(n & 0xFF); }
12 octet constexpr hi(uint16_t n) { return octet((n >> 8) & 0xFF); }
14 constexpr uint16_t as_u16(octet hi, octet lo)
16 return (uint16_t(hi) << 8) + lo;
20 1 1 1 1 1 1
21 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
22 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
23 | ID |
24 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
25 |QR| Opcode |AA|TC|RD|RA| Z|AD|CD| RCODE |
26 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
27 | QDCOUNT |
28 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
29 | ANCOUNT |
30 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
31 | NSCOUNT |
32 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
33 | ARCOUNT |
34 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
38 class header {
39 octet id_hi_;
40 octet id_lo_;
42 octet flags_0_{1}; // recursion desired
43 octet flags_1_{0};
45 octet qdcount_hi_{0};
46 octet qdcount_lo_{1}; // 1 question
48 octet ancount_hi_{0};
49 octet ancount_lo_{0};
51 octet nscount_hi_{0};
52 octet nscount_lo_{0};
54 octet arcount_hi_{0};
55 octet arcount_lo_{1}; // 1 additional for the OPT
57 public:
58 explicit header(uint16_t id)
59 : id_hi_(hi(id))
60 , id_lo_(lo(id))
62 static_assert(sizeof(header) == 12);
65 uint16_t id() const { return as_u16(id_hi_, id_lo_); }
67 uint16_t qdcount() const { return as_u16(qdcount_hi_, qdcount_lo_); }
68 uint16_t ancount() const { return as_u16(ancount_hi_, ancount_lo_); }
69 uint16_t nscount() const { return as_u16(nscount_hi_, nscount_lo_); }
70 uint16_t arcount() const { return as_u16(arcount_hi_, arcount_lo_); }
72 // clang-format off
73 bool truncation() const { return (flags_0_ & 0x02) != 0; }
75 bool checking_disabled() const { return (flags_1_ & 0x10) != 0; }
76 bool authentic_data() const { return (flags_1_ & 0x20) != 0; }
77 bool recursion_available() const { return (flags_1_ & 0x80) != 0; }
78 // clang-format on
80 uint16_t rcode() const { return flags_1_ & 0xf; }
83 class question {
84 octet qtype_hi_;
85 octet qtype_lo_;
87 octet qclass_hi_;
88 octet qclass_lo_;
90 public:
91 explicit question(DNS::RR_type qtype, uint16_t qclass)
92 : qtype_hi_(hi(static_cast<uint16_t>(qtype)))
93 , qtype_lo_(lo(static_cast<uint16_t>(qtype)))
94 , qclass_hi_(hi(qclass))
95 , qclass_lo_(lo(qclass))
97 static_assert(sizeof(question) == 4);
100 DNS::RR_type qtype() const
102 return static_cast<DNS::RR_type>(as_u16(qtype_hi_, qtype_lo_));
104 uint16_t qclass() const { return as_u16(qclass_hi_, qclass_lo_); }
109 <https://tools.ietf.org/html/rfc6891#section-6.1.2>
111 +------------+--------------+------------------------------+
112 | Field Name | Field Type | Description |
113 +------------+--------------+------------------------------+
114 | NAME | domain name | MUST be 0 (root domain) |
115 | TYPE | u_int16_t | OPT (41) |
116 | CLASS | u_int16_t | requestor's UDP payload size |
117 | TTL | u_int32_t | extended RCODE and flags |
118 | RDLEN | u_int16_t | length of all RDATA |
119 | RDATA | octet stream | {attribute,value} pairs |
120 +------------+--------------+------------------------------+
122 <https://tools.ietf.org/html/rfc3225>
124 3. Protocol Changes, in place of TTL
126 +0 (MSB) +1 (LSB)
127 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
128 0: | EXTENDED-RCODE | VERSION |
129 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
130 2: |DO| Z |
131 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
135 class edns0_opt_meta_rr {
136 octet root_domain_name_{0}; // must be zero
138 octet type_hi_{0};
139 octet type_lo_{ns_t_opt};
141 octet class_hi_; // UDP payload size
142 octet class_lo_;
144 octet extended_rcode_{0};
145 octet version_{0};
147 octet z_hi_{0x80}; // "DNSSEC OK" (DO) bit
148 octet z_lo_{0};
150 octet rdlen_hi_{0};
151 octet rdlen_lo_{0};
153 public:
154 explicit edns0_opt_meta_rr(uint16_t max_udp_sz)
155 : class_hi_(hi(max_udp_sz))
156 , class_lo_(lo(max_udp_sz))
158 static_assert(sizeof(edns0_opt_meta_rr) == 11);
161 uint16_t extended_rcode() const { return extended_rcode_; }
166 <https://tools.ietf.org/html/rfc1035>
168 4.1.3. Resource record format
169 1 1 1 1 1 1
170 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
171 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
174 / NAME /
176 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
177 | TYPE |
178 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
179 | CLASS |
180 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
181 | TTL |
183 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
184 | RDLENGTH |
185 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
186 / RDATA /
188 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
192 class rr {
193 octet type_hi_;
194 octet type_lo_;
196 octet class_hi_;
197 octet class_lo_;
199 octet ttl_0_;
200 octet ttl_1_;
201 octet ttl_2_;
202 octet ttl_3_;
204 octet rdlength_hi_;
205 octet rdlength_lo_;
207 public:
208 rr() { static_assert(sizeof(rr) == 10); }
210 uint16_t rr_type() const { return as_u16(type_hi_, type_lo_); }
211 uint16_t rr_class() const { return as_u16(class_hi_, class_lo_); }
212 uint32_t rr_ttl() const
214 return (uint32_t(ttl_0_) << 24) + (uint32_t(ttl_1_) << 16) +
215 (uint32_t(ttl_2_) << 8) + (uint32_t(ttl_3_));
218 uint16_t rdlength() const { return as_u16(rdlength_hi_, rdlength_lo_); }
220 auto cdata() const
222 return reinterpret_cast<char const*>(this) + sizeof(rr);
224 auto rddata() const { return reinterpret_cast<octet const*>(cdata()); }
225 auto next_rr_name() const { return rddata() + rdlength(); }
228 // name processing code mostly adapted from c-ares
230 auto uztosl(size_t uznum)
232 CHECK_LE(uznum, size_t(std::numeric_limits<long>::max()));
233 return static_cast<long>(uznum);
236 // return the length of the expansion of an encoded domain name, or -1
237 // if the encoding is invalid
239 int name_length(octet const* encoded, DNS::message const& pkt)
241 auto const sp = static_cast<std::span<DNS::message::octet const>>(pkt);
242 auto const sp_end = sp.data() + sp.size();
244 // Allow the caller to pass us buf + len and have us check for it.
245 if (encoded >= sp_end)
246 return -1;
248 int length = 0;
249 int nindir = 0; // count indirections
251 while (*encoded) {
253 auto const top = (*encoded & NS_CMPRSFLGS);
255 if (top == NS_CMPRSFLGS) {
256 // Check the offset and go there.
257 if (encoded + 1 >= sp_end)
258 return -1;
260 unsigned const offset = (*encoded & ~NS_CMPRSFLGS) << 8 | *(encoded + 1);
261 if (offset >= sp.size())
262 return -1;
264 encoded = sp.data() + offset;
266 ++nindir;
268 auto constexpr max_indirs = 50; // maximum indirections allowed for a name
270 // If we've seen more indirects than the message length, or over
271 // some limit, then there's a loop.
272 if (nindir > std::streamsize(sp.size()) || nindir > max_indirs)
273 return -1;
275 else if (top == 0) {
276 auto offset = *encoded;
277 if (encoded + offset + 1 >= sp_end)
278 return -1;
280 ++encoded;
282 while (offset--) {
283 length += (*encoded == '.' || *encoded == '\\') ? 2 : 1;
284 encoded++;
287 ++length;
289 else {
290 // RFC 1035 4.1.4 says other options (01, 10) for top 2
291 // bits are reserved.
292 return -1;
296 // If there were any labels at all, then the number of dots is one
297 // less than the number of labels, so subtract one.
299 return length ? length - 1 : length;
302 bool expand_name(octet const* encoded,
303 DNS::message const& pkt,
304 std::string& name,
305 int& enc_len)
307 auto const sp = static_cast<std::span<DNS::message::octet const>>(pkt);
309 name.clear();
311 auto indir = false;
313 auto const nlen = name_length(encoded, pkt);
314 if (nlen < 0) {
315 LOG(WARNING) << "bad name";
316 return false;
319 name.reserve(nlen);
321 if (nlen == 0) {
322 // RFC 2181 says this should be ".": the root of the DNS tree.
323 // Since this function strips trailing dots though, it becomes ""s
325 // indirect root label (like 0xc0 0x0c) is 2 bytes long
326 if ((*encoded & NS_CMPRSFLGS) == NS_CMPRSFLGS)
327 enc_len = 2;
328 else
329 enc_len = 1; // the caller should move one byte to get past this
331 return true;
334 // error-checking done by name_length()
335 auto p = encoded;
336 while (*p) {
337 if ((*p & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
338 if (!indir) {
339 enc_len = uztosl(p + 2 - encoded);
340 indir = true;
342 p = sp.data() + ((*p & ~NS_CMPRSFLGS) << 8 | *(p + 1));
344 else {
345 int len = *p;
346 p++;
347 while (len--) {
348 if (*p == '.' || *p == '\\')
349 name += '\\';
350 name += static_cast<char>(*p);
351 p++;
353 name += '.';
357 if (!indir)
358 enc_len = uztosl(p + 1 - encoded);
360 if (name.length() && (name.back() == '.')) {
361 name.pop_back();
364 return true;
367 // return the length of the encoded name
369 int name_put(octet* buf, char const* name)
371 auto q = buf;
373 if ((name[0] == '.') && (name[1] == '\0'))
374 name++;
376 while (*name) {
377 if (*name == '.') {
378 LOG(WARNING) << "zero length label";
379 return -1;
382 uint8_t len = 0;
383 char const* p;
385 for (p = name; *p && *p != '.'; p++) {
386 if (*p == '\\' && *(p + 1) != 0)
387 p++;
388 len++;
390 if (len > 63) {
391 // RFC-1035 Section 2.3.4. Size limits
392 LOG(WARNING) << "label exceeds 63 octets";
393 return -1;
396 *q++ = len;
397 for (p = name; *p && *p != '.'; p++) {
398 if (*p == '\\' && *(p + 1) != 0)
399 p++;
400 *q++ = *p;
403 if (!*p)
404 break;
406 name = p + 1;
409 // Add the zero-length label at the end.
410 *q++ = 0;
412 auto const sz = q - buf;
413 if (sz > 255) {
414 // RFC-1035 Section 2.3.4. Size limits
415 LOG(WARNING) << "domain name exceeds 255 octets";
416 return -1;
419 return sz;
421 } // namespace
423 namespace DNS {
425 uint16_t message::id() const
427 auto const hdr_p = reinterpret_cast<header const*>(buf_.data());
428 return hdr_p->id();
431 size_t message::min_sz() { return sizeof(header); }
433 DNS::message
434 create_question(char const* name, DNS::RR_type type, uint16_t cls, uint16_t id)
436 // size to allocate may be larger than needed if backslash escapes
437 // are used in domain name
439 auto const sz_alloc = strlen(name) + 2 + // clang-format off
440 sizeof(header) +
441 sizeof(question) +
442 sizeof(edns0_opt_meta_rr); // clang-format on
444 DNS::message::container_t buf(sz_alloc);
446 auto q = buf.data();
448 new (q) header(id);
449 q += sizeof(header);
451 auto const len = name_put(q, name);
452 CHECK_GE(len, 1) << "malformed domain name " << name;
453 q += len;
455 new (q) question(type, cls);
456 q += sizeof(question);
458 new (q) edns0_opt_meta_rr(Config::max_udp_sz);
459 q += sizeof(edns0_opt_meta_rr);
461 // verify constructed size is less than or equal to allocated size
462 auto const sz = q - buf.data();
463 CHECK_LE(sz, sz_alloc);
465 buf.resize(sz);
466 buf.shrink_to_fit();
468 return DNS::message{std::move(buf)};
471 void check_answer(bool& nx_domain,
472 bool& bogus_or_indeterminate,
474 uint16_t& rcode,
475 uint16_t& extended_rcode,
477 bool& truncation,
478 bool& authentic_data,
479 bool& has_record,
481 DNS::message const& q,
482 DNS::message const& a,
484 DNS::RR_type type,
485 char const* name)
487 // We grab some stuff from the question we generated. This is not
488 // an un-trusted datum from afar.
490 auto const cls{[type = type, &q]() {
491 auto const q_sp = static_cast<std::span<DNS::message::octet const>>(q);
492 auto q_p = q_sp.data();
493 auto const q_hdr_p = reinterpret_cast<header const*>(q_p);
494 CHECK_EQ(q_hdr_p->qdcount(), uint16_t(1));
495 q_p += sizeof(header);
496 std::string qname;
497 int name_len;
498 CHECK(expand_name(q_p, q, qname, name_len));
499 q_p += name_len;
500 auto const question_p = reinterpret_cast<question const*>(q_p);
501 CHECK(question_p->qtype() == type)
502 << question_p->qtype() << " != " << type << '\n';
503 return question_p->qclass();
504 }()};
506 auto const a_sp = static_cast<std::span<DNS::message::octet const>>(a);
507 auto const a_sp_end = a_sp.data() + a_sp.size();
509 auto const hdr_p = reinterpret_cast<header const*>(a_sp.data());
511 rcode = hdr_p->rcode();
512 switch (rcode) {
513 case ns_r_noerror: break;
514 case ns_r_nxdomain: nx_domain = true; break;
515 case ns_r_servfail: bogus_or_indeterminate = true; break;
516 default:
517 bogus_or_indeterminate = true;
518 LOG(WARNING) << "name lookup error: " << DNS::rcode_c_str(rcode) << " for "
519 << name << '/' << type;
520 break;
523 truncation = hdr_p->truncation();
524 authentic_data = hdr_p->authentic_data();
525 has_record = hdr_p->ancount() != 0;
527 if (truncation) {
528 bogus_or_indeterminate = true;
529 LOG(WARNING) << "DNS answer truncated for " << name << '/' << type;
530 return;
533 // check the question part of the reply
535 if (hdr_p->qdcount() != 1) {
536 bogus_or_indeterminate = true;
537 LOG(WARNING) << "question not copied into answer for " << name << '/'
538 << type;
539 return;
542 // p is a pointer that pushes forward in the message as we process
543 // each section
544 auto p = a_sp.data() + sizeof(header);
546 { // make sure the question name matches
547 std::string qname;
548 auto enc_len = 0;
549 if (!expand_name(p, a, qname, enc_len)) {
550 bogus_or_indeterminate = true;
551 LOG(WARNING) << "bad message";
552 return;
554 p += enc_len;
555 if (p >= a_sp_end) {
556 bogus_or_indeterminate = true;
557 LOG(WARNING) << "bad message";
558 return;
560 if (!iequal(qname, name)) {
561 bogus_or_indeterminate = true;
562 LOG(WARNING) << "names don't match, " << qname << " != " << name;
563 return;
567 if ((p + sizeof(question)) >= a_sp_end) {
568 bogus_or_indeterminate = true;
569 LOG(WARNING) << "bad message";
570 return;
573 auto question_p = reinterpret_cast<question const*>(p);
574 p += sizeof(question);
576 if (question_p->qtype() != type) {
577 bogus_or_indeterminate = true;
578 LOG(WARNING) << "qtypes don't match, " << question_p->qtype()
579 << " != " << type;
580 return;
582 if (question_p->qclass() != cls) {
583 bogus_or_indeterminate = true;
584 LOG(WARNING) << "qclasses don't match, " << question_p->qclass()
585 << " != " << cls;
586 return;
589 // answers and nameservers
590 for (auto i = 0; i < (hdr_p->ancount() + hdr_p->nscount()); ++i) {
591 std::string x;
592 auto enc_len = 0;
593 if (!expand_name(p, a, x, enc_len) ||
594 ((p + enc_len + sizeof(rr)) > a_sp_end)) {
595 bogus_or_indeterminate = true;
596 LOG(WARNING) << "bad message in answer or nameserver section for " << name
597 << '/' << type;
598 return;
600 p += enc_len;
601 auto const rr_p = reinterpret_cast<rr const*>(p);
602 p = rr_p->next_rr_name();
605 // check additional section for OPT record
606 for (auto i = 0; i < hdr_p->arcount(); ++i) {
607 std::string x;
608 auto enc_len = 0;
609 if (!expand_name(p, a, x, enc_len) ||
610 ((p + enc_len + sizeof(rr)) > a_sp_end)) {
611 bogus_or_indeterminate = true;
612 LOG(WARNING) << "bad message in additional section for " << name << '/'
613 << type;
614 return;
616 p += enc_len;
617 auto const rr_p = reinterpret_cast<rr const*>(p);
619 switch (rr_p->rr_type()) {
620 case ns_t_opt: {
621 auto opt_p = reinterpret_cast<edns0_opt_meta_rr const*>(p);
622 extended_rcode = (opt_p->extended_rcode() << 4) + hdr_p->rcode();
623 break;
626 case ns_t_a:
627 case ns_t_aaaa:
628 case ns_t_ns:
629 case ns_t_rrsig:
630 // nameserver records often included with associated address info
631 break;
633 case ns_t_tlsa:
634 // tlsa records can now be in the additional section, as
635 // they are returned from dns.mullvad.net.
636 break;
638 default:
639 LOG(INFO) << "unknown additional record, name == " << name;
640 LOG(INFO) << "rr_p->type() == " << rr_p->rr_type() << " ("
641 << RR_type_c_str(rr_p->rr_type()) << ")";
642 LOG(INFO) << "rr_p->class() == " << rr_p->rr_class();
643 LOG(INFO) << "rr_p->ttl() == " << rr_p->rr_ttl();
644 break;
647 p = rr_p->next_rr_name();
650 unsigned long size_check = p - a_sp.data();
651 if (size_check != a_sp.size()) {
652 bogus_or_indeterminate = true;
653 LOG(WARNING) << "bad message size for " << name << '/' << type;
654 return;
658 std::optional<RR> get_A(rr const* rr_p, DNS::message const& pkt, bool& err)
660 if (rr_p->rdlength() != 4) {
661 LOG(WARNING) << "bogus A record";
662 err = true;
663 return {};
665 return RR_A{rr_p->rddata(), rr_p->rdlength()};
668 std::optional<RR> get_CNAME(rr const* rr_p, DNS::message const& pkt, bool& err)
670 std::string name;
671 int enc_len;
672 if (!expand_name(rr_p->rddata(), pkt, name, enc_len)) {
673 LOG(WARNING) << "bogus CNAME record";
674 err = true;
675 return {};
677 return RR_CNAME{name};
680 std::optional<RR> get_PTR(rr const* rr_p, DNS::message const& pkt, bool& err)
682 std::string name;
683 int enc_len;
684 if (!expand_name(rr_p->rddata(), pkt, name, enc_len)) {
685 LOG(WARNING) << "bogus PTR record";
686 err = true;
687 return {};
689 return RR_PTR{name};
692 std::optional<RR> get_MX(rr const* rr_p, DNS::message const& pkt, bool& err)
694 std::string name;
695 int enc_len;
696 if (rr_p->rdlength() < 3) {
697 LOG(WARNING) << "bogus MX record";
698 err = true;
699 return {};
701 auto p = rr_p->rddata();
702 auto const preference = as_u16(p[0], p[1]);
703 p += 2;
704 if (!expand_name(p, pkt, name, enc_len)) {
705 LOG(WARNING) << "bogus MX record";
706 err = true;
707 return {};
709 return RR_MX{name, preference};
712 std::optional<RR> get_TXT(rr const* rr_p, DNS::message const& pkt, bool& err)
714 if (rr_p->rdlength() < 1) {
715 LOG(WARNING) << "bogus TXT record";
716 err = true;
717 return {};
719 std::string str;
720 auto p = rr_p->rddata();
721 do {
722 if ((p + 1 + *p) > rr_p->next_rr_name()) {
723 LOG(WARNING) << "bogus string in TXT record";
724 err = true;
725 return {};
727 str.append(reinterpret_cast<char const*>(p) + 1, *p);
728 p += *p + 1;
729 } while (p < rr_p->next_rr_name());
730 return RR_TXT{str};
733 std::optional<RR> get_AAAA(rr const* rr_p, DNS::message const& pkt, bool& err)
735 if (rr_p->rdlength() != 16) {
736 LOG(WARNING) << "bogus AAAA record";
737 err = true;
738 return {};
740 return RR_AAAA{rr_p->rddata(), rr_p->rdlength()};
743 std::optional<RR> get_RRSIG(rr const* rr_p, DNS::message const& pkt, bool& err)
745 // LOG(WARNING) << "#### FIXME! RRSIG";
746 // return RR_RRSIG{rr_p->rddata(), rr_p->rdlength()});
747 return {};
750 std::optional<RR> get_TLSA(rr const* rr_p, DNS::message const& pkt, bool& err)
752 if (rr_p->rdlength() < 4) {
753 LOG(WARNING) << "bogus TLSA record";
754 err = true;
755 return {};
758 auto p = rr_p->rddata();
760 uint8_t cert_usage = *p++;
761 uint8_t selector = *p++;
762 uint8_t matching_type = *p++;
764 std::span<DNS::message::octet const> assoc_data{
765 p, static_cast<size_t>(rr_p->rdlength()) - 3};
767 return RR_TLSA{cert_usage, selector, matching_type, assoc_data};
770 std::optional<RR> get_rr(rr const* rr_p, DNS::message const& pkt, bool& err)
772 auto const typ = static_cast<DNS::RR_type>(rr_p->rr_type());
774 switch (typ) { // clang-format off
775 case DNS::RR_type::A: return get_A (rr_p, pkt, err);
776 case DNS::RR_type::CNAME: return get_CNAME(rr_p, pkt, err);
777 case DNS::RR_type::PTR: return get_PTR (rr_p, pkt, err);
778 case DNS::RR_type::MX: return get_MX (rr_p, pkt, err);
779 case DNS::RR_type::TXT: return get_TXT (rr_p, pkt, err);
780 case DNS::RR_type::AAAA: return get_AAAA (rr_p, pkt, err);
781 case DNS::RR_type::RRSIG: return get_RRSIG(rr_p, pkt, err);
782 case DNS::RR_type::TLSA: return get_TLSA (rr_p, pkt, err);
783 default: break;
784 } // clang-format on
786 LOG(WARNING) << "unsupported RR type " << typ;
787 return {};
790 RR_collection get_records(message const& pkt, bool& bogus_or_indeterminate)
792 auto const sp = static_cast<std::span<DNS::message::octet const>>(pkt);
793 auto const sp_end = sp.data() + sp.size();
795 RR_collection ret;
797 auto const hdr_p = reinterpret_cast<header const*>(sp.data());
799 auto p = sp.data() + sizeof(header);
801 // skip queries
802 for (auto i = 0; i < hdr_p->qdcount(); ++i) {
803 std::string qname;
804 auto enc_len = 0;
806 CHECK(expand_name(p, pkt, qname, enc_len));
807 p += enc_len;
808 // auto question_p = reinterpret_cast<question const*>(p);
809 p += sizeof(question);
812 // get answers
813 for (auto i = 0; i < hdr_p->ancount(); ++i) {
814 std::string name;
815 auto enc_len = 0;
816 CHECK(expand_name(p, pkt, name, enc_len));
817 p += enc_len;
818 if ((p + sizeof(rr)) > sp_end) {
819 bogus_or_indeterminate = true;
820 LOG(WARNING) << "bad message";
821 return RR_collection{};
823 auto rr_p = reinterpret_cast<rr const*>(p);
824 if ((p + rr_p->rdlength()) > sp_end) {
825 bogus_or_indeterminate = true;
826 LOG(WARNING) << "bad message";
827 return RR_collection{};
830 auto rr_ret = get_rr(rr_p, pkt, bogus_or_indeterminate);
832 if (bogus_or_indeterminate)
833 return RR_collection{};
835 if (rr_ret)
836 ret.emplace_back(*rr_ret);
838 p = rr_p->next_rr_name();
841 return ret;
844 } // namespace DNS