]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsparser.cc
Meson: Separate test files from common files
[thirdparty/pdns.git] / pdns / dnsparser.cc
CommitLineData
4192ca66 1/*
12471842
PL
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
ff6a1e7b 22#include "dnsparser.hh"
6c0670c3 23#include "dnswriter.hh"
4cf74e6f 24#include <boost/algorithm/string.hpp>
5dba9d25 25#include <boost/format.hpp>
ff6a1e7b 26
61b26744 27#include "namespaces.hh"
3cce32ac 28#include "noinitvector.hh"
ff6a1e7b 29
bd98d61b 30UnknownRecordContent::UnknownRecordContent(const string& zone)
ff6a1e7b 31{
b4db4fe4
CH
32 // parse the input
33 vector<string> parts;
34 stringtok(parts, zone);
35 // we need exactly 3 parts, except if the length field is set to 0 then we only need 2
dc593046 36 if (parts.size() != 3 && !(parts.size() == 2 && boost::equals(parts.at(1), "0"))) {
b4db4fe4 37 throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got " + std::to_string(parts.size()) + ": " + zone);
ff6a1e7b
BH
38 }
39
b4db4fe4
CH
40 if (parts.at(0) != "\\#") {
41 throw MOADNSException("Unknown record was stored incorrectly, first part should be '\\#', got '" + parts.at(0) + "'");
42 }
2d79e327 43
b4db4fe4 44 const string& relevant = (parts.size() > 2) ? parts.at(2) : "";
a0383aad 45 auto total = pdns::checked_stoi<unsigned int>(parts.at(1));
b4db4fe4
CH
46 if (relevant.size() % 2 || (relevant.size() / 2) != total) {
47 throw MOADNSException((boost::format("invalid unknown record length: size not equal to length field (%d != 2 * %d)") % relevant.size() % total).str());
48 }
f5234115 49
b4db4fe4
CH
50 string out;
51 out.reserve(total + 1);
f5234115 52
b4db4fe4
CH
53 for (unsigned int n = 0; n < total; ++n) {
54 int c;
55 if (sscanf(&relevant.at(2*n), "%02x", &c) != 1) {
56 throw MOADNSException("unable to read data at position " + std::to_string(2 * n) + " from unknown record of size " + std::to_string(relevant.size()));
95a61c6f 57 }
b4db4fe4 58 out.append(1, (char)c);
6c0670c3 59 }
cff3e04d 60
b4db4fe4
CH
61 d_record.insert(d_record.end(), out.begin(), out.end());
62}
cff3e04d 63
d73de874 64string UnknownRecordContent::getZoneRepresentation(bool /* noDot */) const
b4db4fe4
CH
65{
66 ostringstream str;
67 str<<"\\# "<<(unsigned int)d_record.size()<<" ";
68 char hex[4];
d7f67000
RP
69 for (unsigned char n : d_record) {
70 snprintf(hex, sizeof(hex), "%02x", n);
b4db4fe4 71 str << hex;
6c0670c3 72 }
b4db4fe4
CH
73 return str.str();
74}
5a1f298f 75
d06dcda4 76void UnknownRecordContent::toPacket(DNSPacketWriter& pw) const
b4db4fe4
CH
77{
78 pw.xfrBlob(string(d_record.begin(),d_record.end()));
79}
ff6a1e7b 80
ef2ea4bf 81shared_ptr<DNSRecordContent> DNSRecordContent::deserialize(const DNSName& qname, uint16_t qtype, const string& serialized)
ea634573
BH
82{
83 dnsheader dnsheader;
84 memset(&dnsheader, 0, sizeof(dnsheader));
85 dnsheader.qdcount=htons(1);
86 dnsheader.ancount=htons(1);
87
3cce32ac 88 PacketBuffer packet; // build pseudo packet
678ce973 89 /* will look like: dnsheader, 5 bytes, encoded qname, dns record header, serialized data */
5f2286f1 90 const auto& encoded = qname.getStorage();
678ce973
BH
91 packet.resize(sizeof(dnsheader) + 5 + encoded.size() + sizeof(struct dnsrecordheader) + serialized.size());
92
93 uint16_t pos=0;
678ce973
BH
94 memcpy(&packet[0], &dnsheader, sizeof(dnsheader)); pos+=sizeof(dnsheader);
95
533493b2
AV
96 constexpr std::array<uint8_t, 5> tmp= {'\x0', '\x0', '\x1', '\x0', '\x1' }; // root question for ns_t_a
97 memcpy(&packet[pos], tmp.data(), tmp.size()); pos += tmp.size();
678ce973 98
705f31ae 99 memcpy(&packet[pos], encoded.c_str(), encoded.size()); pos+=(uint16_t)encoded.size();
ea634573
BH
100
101 struct dnsrecordheader drh;
102 drh.d_type=htons(qtype);
78f56b38 103 drh.d_class=htons(QClass::IN);
ea634573
BH
104 drh.d_ttl=0;
105 drh.d_clen=htons(serialized.size());
106
678ce973 107 memcpy(&packet[pos], &drh, sizeof(drh)); pos+=sizeof(drh);
1d830152 108 if (!serialized.empty()) {
6d02e928
OM
109 memcpy(&packet[pos], serialized.c_str(), serialized.size());
110 pos += (uint16_t) serialized.size();
90ee15b3 111 (void) pos;
6d02e928 112 }
ea634573 113
3cce32ac
RG
114 DNSRecord dr;
115 dr.d_class = QClass::IN;
116 dr.d_type = qtype;
117 dr.d_name = qname;
118 dr.d_clen = serialized.size();
e2134021 119 PacketReader pr(std::string_view(reinterpret_cast<const char*>(packet.data()), packet.size()), packet.size() - serialized.size() - sizeof(dnsrecordheader));
3cce32ac
RG
120 /* needed to get the record boundaries right */
121 pr.getDnsrecordheader(drh);
d525b58b 122 auto content = DNSRecordContent::make(dr, pr, Opcode::Query);
3cce32ac 123 return content;
ea634573 124}
ff6a1e7b 125
d525b58b
KM
126std::shared_ptr<DNSRecordContent> DNSRecordContent::make(const DNSRecord& dr,
127 PacketReader& pr)
ff6a1e7b 128{
7f7b8d55
BH
129 uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT
130
faa05786 131 auto i = getTypemap().find(pair(searchclass, dr.d_type));
49a06471 132 if(i==getTypemap().end() || !i->second) {
6177a176 133 return std::make_shared<UnknownRecordContent>(dr, pr);
ff6a1e7b 134 }
945a9ad4 135
32122aab 136 return i->second(dr, pr);
ff6a1e7b
BH
137}
138
d525b58b
KM
139std::shared_ptr<DNSRecordContent> DNSRecordContent::make(uint16_t qtype, uint16_t qclass,
140 const string& content)
6c0670c3 141{
faa05786 142 auto i = getZmakermap().find(pair(qclass, qtype));
49a06471 143 if(i==getZmakermap().end()) {
6177a176 144 return std::make_shared<UnknownRecordContent>(content);
6c0670c3
BH
145 }
146
32122aab 147 return i->second(content);
6c0670c3
BH
148}
149
d525b58b
KM
150std::shared_ptr<DNSRecordContent> DNSRecordContent::make(const DNSRecord& dr, PacketReader& pr, uint16_t oc)
151{
7945d472
RA
152 // For opcode UPDATE and where the DNSRecord is an answer record, we don't care about content, because this is
153 // not used within the prerequisite section of RFC2136, so - we can simply use unknownrecordcontent.
89c46d18 154 // For section 3.2.3, we do need content so we need to get it properly. But only for the correct QClasses.
e693ff5a 155 if (oc == Opcode::Update && dr.d_place == DNSResourceRecord::ANSWER && dr.d_class != 1)
6177a176 156 return std::make_shared<UnknownRecordContent>(dr, pr);
914353ca 157
7945d472
RA
158 uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT
159
faa05786 160 auto i = getTypemap().find(pair(searchclass, dr.d_type));
7945d472 161 if(i==getTypemap().end() || !i->second) {
6177a176 162 return std::make_shared<UnknownRecordContent>(dr, pr);
7945d472
RA
163 }
164
32122aab 165 return i->second(dr, pr);
7945d472
RA
166}
167
4de01aaa 168string DNSRecordContent::upgradeContent(const DNSName& qname, const QType& qtype, const string& content) {
b4db4fe4
CH
169 // seamless upgrade for previously unsupported but now implemented types.
170 UnknownRecordContent unknown_content(content);
171 shared_ptr<DNSRecordContent> rc = DNSRecordContent::deserialize(qname, qtype.getCode(), unknown_content.serialize(qname));
172 return rc->getZoneRepresentation();
173}
7945d472 174
49a06471
BH
175DNSRecordContent::typemap_t& DNSRecordContent::getTypemap()
176{
177 static DNSRecordContent::typemap_t typemap;
178 return typemap;
179}
180
108c321e 181DNSRecordContent::n2typemap_t& DNSRecordContent::getN2Typemap()
49a06471 182{
93e4cb97
BH
183 static DNSRecordContent::n2typemap_t n2typemap;
184 return n2typemap;
49a06471
BH
185}
186
108c321e
BH
187DNSRecordContent::t2namemap_t& DNSRecordContent::getT2Namemap()
188{
93e4cb97
BH
189 static DNSRecordContent::t2namemap_t t2namemap;
190 return t2namemap;
108c321e
BH
191}
192
49a06471
BH
193DNSRecordContent::zmakermap_t& DNSRecordContent::getZmakermap()
194{
195 static DNSRecordContent::zmakermap_t zmakermap;
196 return zmakermap;
197}
198
2e63e431
RG
199bool DNSRecordContent::isRegisteredType(uint16_t rtype, uint16_t rclass)
200{
201 return getTypemap().count(pair(rclass, rtype)) != 0;
202}
203
5708a729 204DNSRecord::DNSRecord(const DNSResourceRecord& rr): d_name(rr.qname)
fbe23591 205{
fbe23591 206 d_type = rr.qtype.getCode();
207 d_ttl = rr.ttl;
208 d_class = rr.qclass;
4950b140 209 d_place = DNSResourceRecord::ANSWER;
e24c18b0 210 d_clen = 0;
d525b58b 211 d_content = DNSRecordContent::make(d_type, rr.qclass, rr.content);
fbe23591 212}
213
4950b140 214// If you call this and you are not parsing a packet coming from a socket, you are doing it wrong.
ae2dcdd3
FM
215DNSResourceRecord DNSResourceRecord::fromWire(const DNSRecord& wire)
216{
217 DNSResourceRecord resourceRecord;
218 resourceRecord.qname = wire.d_name;
219 resourceRecord.qtype = QType(wire.d_type);
220 resourceRecord.ttl = wire.d_ttl;
221 resourceRecord.content = wire.getContent()->getZoneRepresentation(true);
222 resourceRecord.auth = false;
223 resourceRecord.qclass = wire.d_class;
224 return resourceRecord;
4950b140
CH
225}
226
fa5a722b 227void MOADNSParser::init(bool query, const std::string_view& packet)
ff6a1e7b 228{
78f56b38 229 if (packet.size() < sizeof(dnsheader))
ff6a1e7b 230 throw MOADNSException("Packet shorter than minimal header");
bd98d61b 231
78f56b38 232 memcpy(&d_header, packet.data(), sizeof(dnsheader));
ff6a1e7b 233
7945d472 234 if(d_header.opcode != Opcode::Query && d_header.opcode != Opcode::Notify && d_header.opcode != Opcode::Update)
335da0ba 235 throw MOADNSException("Can't parse non-query packet with opcode="+ std::to_string(d_header.opcode));
f27e6356 236
ff6a1e7b
BH
237 d_header.qdcount=ntohs(d_header.qdcount);
238 d_header.ancount=ntohs(d_header.ancount);
239 d_header.nscount=ntohs(d_header.nscount);
240 d_header.arcount=ntohs(d_header.arcount);
27c0050c
RG
241
242 if (query && (d_header.qdcount > 1))
243 throw MOADNSException("Query with QD > 1 ("+std::to_string(d_header.qdcount)+")");
bd98d61b 244
629eaf49 245 unsigned int n=0;
ff6a1e7b 246
78f56b38 247 PacketReader pr(packet);
7b1469bb 248 bool validPacket=false;
512c8492 249 try {
00bf10d3
BH
250 d_qtype = d_qclass = 0; // sometimes replies come in with no question, don't present garbage then
251
40207e64 252 for(n=0;n < d_header.qdcount; ++n) {
8ad443ea 253 d_qname=pr.getName();
40207e64
BH
254 d_qtype=pr.get16BitInt();
255 d_qclass=pr.get16BitInt();
256 }
257
512c8492
BH
258 struct dnsrecordheader ah;
259 vector<unsigned char> record;
04151caa 260 bool seenTSIG = false;
7b1469bb 261 validPacket=true;
756e82cf 262 d_answers.reserve((unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount));
c4ac5865 263 for(n=0;n < (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount); ++n) {
512c8492 264 DNSRecord dr;
bd98d61b 265
512c8492 266 if(n < d_header.ancount)
e693ff5a 267 dr.d_place=DNSResourceRecord::ANSWER;
512c8492 268 else if(n < d_header.ancount + d_header.nscount)
e693ff5a 269 dr.d_place=DNSResourceRecord::AUTHORITY;
bd98d61b 270 else
e693ff5a 271 dr.d_place=DNSResourceRecord::ADDITIONAL;
04151caa 272
78f56b38 273 unsigned int recordStartPos=pr.getPosition();
57e5f5f7 274
f809c028 275 DNSName name=pr.getName();
04151caa 276
512c8492
BH
277 pr.getDnsrecordheader(ah);
278 dr.d_ttl=ah.d_ttl;
279 dr.d_type=ah.d_type;
280 dr.d_class=ah.d_class;
04151caa 281
c92e6020
RG
282 dr.d_name = std::move(name);
283 dr.d_clen = ah.d_clen;
7b1469bb 284
c2a1b24d
PL
285 if (query &&
286 !(d_qtype == QType::IXFR && dr.d_place == DNSResourceRecord::AUTHORITY && dr.d_type == QType::SOA) && // IXFR queries have a SOA in their AUTHORITY section
287 (dr.d_place == DNSResourceRecord::ANSWER || dr.d_place == DNSResourceRecord::AUTHORITY || (dr.d_type != QType::OPT && dr.d_type != QType::TSIG && dr.d_type != QType::SIG && dr.d_type != QType::TKEY) || ((dr.d_type == QType::TSIG || dr.d_type == QType::SIG || dr.d_type == QType::TKEY) && dr.d_class != QClass::ANY))) {
27c0050c 288// cerr<<"discarding RR, query is "<<query<<", place is "<<dr.d_place<<", type is "<<dr.d_type<<", class is "<<dr.d_class<<endl;
d06dcda4 289 dr.setContent(std::make_shared<UnknownRecordContent>(dr, pr));
27c0050c
RG
290 }
291 else {
292// cerr<<"parsing RR, query is "<<query<<", place is "<<dr.d_place<<", type is "<<dr.d_type<<", class is "<<dr.d_class<<endl;
d525b58b 293 dr.setContent(DNSRecordContent::make(dr, pr, d_header.opcode));
27c0050c
RG
294 }
295
18f707fa
RG
296 /* XXX: XPF records should be allowed after TSIG as soon as the actual XPF option code has been assigned:
297 if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG && dr.d_type != QType::XPF)
298 */
299 if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG) {
04151caa
RG
300 /* only XPF records are allowed after a TSIG */
301 throw MOADNSException("Packet ("+d_qname.toString()+"|#"+std::to_string(d_qtype)+") has an unexpected record ("+std::to_string(dr.d_type)+") after a TSIG one.");
302 }
303
60a1c204 304 if(dr.d_type == QType::TSIG && dr.d_class == QClass::ANY) {
18f707fa 305 if(seenTSIG || dr.d_place != DNSResourceRecord::ADDITIONAL) {
86f1af1c 306 throw MOADNSException("Packet ("+d_qname.toLogString()+"|#"+std::to_string(d_qtype)+") has a TSIG record in an invalid position.");
60a1c204 307 }
04151caa 308 seenTSIG = true;
78f56b38 309 d_tsigPos = recordStartPos;
60a1c204 310 }
c771b712 311
e32a8d46 312 d_answers.emplace_back(std::move(dr), pr.getPosition() - sizeof(dnsheader));
512c8492 313 }
e5986c84 314
04151caa 315#if 0
78f56b38
RG
316 if(pr.getPosition()!=packet.size()) {
317 throw MOADNSException("Packet ("+d_qname+"|#"+std::to_string(d_qtype)+") has trailing garbage ("+ std::to_string(pr.getPosition()) + " < " +
318 std::to_string(packet.size()) + ")");
ff6a1e7b 319 }
04151caa 320#endif
512c8492 321 }
78f56b38 322 catch(const std::out_of_range &re) {
629eaf49
BH
323 if(validPacket && d_header.tc) { // don't sweat it over truncated packets, but do adjust an, ns and arcount
324 if(n < d_header.ancount) {
4957a608 325 d_header.ancount=n; d_header.nscount = d_header.arcount = 0;
629eaf49
BH
326 }
327 else if(n < d_header.ancount + d_header.nscount) {
4957a608 328 d_header.nscount = n - d_header.ancount; d_header.arcount=0;
629eaf49
BH
329 }
330 else {
4957a608 331 d_header.arcount = n - d_header.ancount - d_header.nscount;
629eaf49
BH
332 }
333 }
334 else {
78f56b38 335 throw MOADNSException("Error parsing packet of "+std::to_string(packet.size())+" bytes (rd="+
335da0ba 336 std::to_string(d_header.rd)+
232f0877 337 "), out of bounds: "+string(re.what()));
629eaf49 338 }
ff6a1e7b 339 }
ff6a1e7b
BH
340}
341
28647bcf
RG
342bool MOADNSParser::hasEDNS() const
343{
344 if (d_header.arcount == 0 || d_answers.empty()) {
345 return false;
346 }
347
348 for (const auto& record : d_answers) {
349 if (record.first.d_place == DNSResourceRecord::ADDITIONAL && record.first.d_type == QType::OPT) {
350 return true;
351 }
352 }
353
354 return false;
355}
10321a98 356
ff6a1e7b
BH
357void PacketReader::getDnsrecordheader(struct dnsrecordheader &ah)
358{
59a6ce3b 359 unsigned char *p = reinterpret_cast<unsigned char*>(&ah);
bd98d61b 360
59a6ce3b
RG
361 for(unsigned int n = 0; n < sizeof(dnsrecordheader); ++n) {
362 p[n] = d_content.at(d_pos++);
363 }
bd98d61b 364
59a6ce3b
RG
365 ah.d_type = ntohs(ah.d_type);
366 ah.d_class = ntohs(ah.d_class);
367 ah.d_clen = ntohs(ah.d_clen);
368 ah.d_ttl = ntohl(ah.d_ttl);
8c1c9170 369
59a6ce3b
RG
370 d_startrecordpos = d_pos; // needed for getBlob later on
371 d_recordlen = ah.d_clen;
ff6a1e7b
BH
372}
373
374
092f210a 375void PacketReader::copyRecord(vector<unsigned char>& dest, uint16_t len)
ff6a1e7b 376{
59a6ce3b 377 if (len == 0) {
bff744a8 378 return;
59a6ce3b
RG
379 }
380 if ((d_pos + len) > d_content.size()) {
381 throw std::out_of_range("Attempt to copy outside of packet");
382 }
383
384 dest.resize(len);
bff744a8 385
59a6ce3b
RG
386 for (uint16_t n = 0; n < len; ++n) {
387 dest.at(n) = d_content.at(d_pos++);
ff6a1e7b
BH
388 }
389}
390
092f210a 391void PacketReader::copyRecord(unsigned char* dest, uint16_t len)
ff6a1e7b 392{
59a6ce3b 393 if (d_pos + len > d_content.size()) {
10f4eea8 394 throw std::out_of_range("Attempt to copy outside of packet");
59a6ce3b 395 }
ff6a1e7b 396
bff744a8 397 memcpy(dest, &d_content.at(d_pos), len);
59a6ce3b 398 d_pos += len;
ff6a1e7b
BH
399}
400
786ed0ff
PL
401void PacketReader::xfrNodeOrLocatorID(NodeOrLocatorID& ret)
402{
403 if (d_pos + sizeof(ret) > d_content.size()) {
404 throw std::out_of_range("Attempt to read 64 bit value outside of packet");
405 }
d97a781e 406 memcpy(&ret.content, &d_content.at(d_pos), sizeof(ret.content));
786ed0ff
PL
407 d_pos += sizeof(ret);
408}
409
341930bb
BH
410void PacketReader::xfr48BitInt(uint64_t& ret)
411{
412 ret=0;
78f56b38 413 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 414 ret<<=8;
78f56b38 415 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 416 ret<<=8;
78f56b38 417 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 418 ret<<=8;
78f56b38 419 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 420 ret<<=8;
78f56b38 421 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 422 ret<<=8;
78f56b38 423 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 424}
ff6a1e7b 425
092f210a 426uint32_t PacketReader::get32BitInt()
ff6a1e7b 427{
092f210a 428 uint32_t ret=0;
78f56b38 429 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b 430 ret<<=8;
78f56b38 431 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b 432 ret<<=8;
78f56b38 433 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b 434 ret<<=8;
78f56b38 435 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
bd98d61b 436
ff6a1e7b
BH
437 return ret;
438}
439
440
092f210a 441uint16_t PacketReader::get16BitInt()
ff6a1e7b 442{
092f210a 443 uint16_t ret=0;
78f56b38 444 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b 445 ret<<=8;
78f56b38 446 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
bd98d61b 447
ff6a1e7b
BH
448 return ret;
449}
450
49a06471 451uint8_t PacketReader::get8BitInt()
ff6a1e7b
BH
452{
453 return d_content.at(d_pos++);
454}
455
8171ab83 456DNSName PacketReader::getName()
ff6a1e7b 457{
d926c0da 458 unsigned int consumed;
c1229531 459 try {
4646277d 460 DNSName dn((const char*) d_content.data(), d_content.size(), d_pos, true /* uncompress */, nullptr /* qtype */, nullptr /* qclass */, &consumed, sizeof(dnsheader));
bd98d61b 461
c1229531 462 d_pos+=consumed;
8171ab83 463 return dn;
c1229531 464 }
6177a176
RG
465 catch(const std::range_error& re) {
466 throw std::out_of_range(string("dnsname issue: ")+re.what());
467 }
468 catch(...) {
469 throw std::out_of_range("dnsname issue");
470 }
fb38d90b 471 throw PDNSException("PacketReader::getName(): name is empty");
ff6a1e7b
BH
472}
473
ef6a78d5
BH
474static string txtEscape(const string &name)
475{
476 string ret;
5dba9d25 477 char ebuf[5];
ef6a78d5 478
d7f67000
RP
479 for(char i : name) {
480 if((unsigned char) i >= 127 || (unsigned char) i < 32) {
481 snprintf(ebuf, sizeof(ebuf), "\\%03u", (unsigned char)i);
5dba9d25
PD
482 ret += ebuf;
483 }
d7f67000 484 else if(i=='"' || i=='\\'){
ef6a78d5 485 ret += '\\';
d7f67000 486 ret += i;
ef6a78d5
BH
487 }
488 else
d7f67000 489 ret += i;
5dba9d25 490 }
ef6a78d5
BH
491 return ret;
492}
493
494// exceptions thrown here do not result in logging in the main pdns auth server - just so you know!
84e1142d 495string PacketReader::getText(bool multi, bool lenField)
9d9c52ef
BH
496{
497 string ret;
498 ret.reserve(40);
ef6a78d5
BH
499 while(d_pos < d_startrecordpos + d_recordlen ) {
500 if(!ret.empty()) {
501 ret.append(1,' ');
502 }
84e1142d
PL
503 uint16_t labellen;
504 if(lenField)
78f56b38 505 labellen=static_cast<uint8_t>(d_content.at(d_pos++));
84e1142d
PL
506 else
507 labellen=d_recordlen - (d_pos - d_startrecordpos);
bd98d61b 508
ef6a78d5 509 ret.append(1,'"');
950f78df
BH
510 if(labellen) { // no need to do anything for an empty string
511 string val(&d_content.at(d_pos), &d_content.at(d_pos+labellen-1)+1);
512 ret.append(txtEscape(val)); // the end is one beyond the packet
513 }
ef6a78d5
BH
514 ret.append(1,'"');
515 d_pos+=labellen;
516 if(!multi)
517 break;
518 }
9d9c52ef 519
d97b8188
PD
520 if (ret.empty() && !lenField) {
521 // all lenField == false cases (CAA and URI at the time of this writing) want that emptiness to be explicit
522 return "\"\"";
523 }
9d9c52ef
BH
524 return ret;
525}
ff6a1e7b 526
948a927f
PL
527string PacketReader::getUnquotedText(bool lenField)
528{
a94e5580 529 uint16_t stop_at;
948a927f 530 if(lenField)
78f56b38 531 stop_at = static_cast<uint8_t>(d_content.at(d_pos)) + d_pos + 1;
948a927f
PL
532 else
533 stop_at = d_recordlen;
534
d7bbbcf4
RG
535 /* think unsigned overflow */
536 if (stop_at < d_pos) {
537 throw std::out_of_range("getUnquotedText out of record range");
538 }
539
948a927f
PL
540 if(stop_at == d_pos)
541 return "";
542
543 d_pos++;
d97b8188 544 string ret(d_content.substr(d_pos, stop_at-d_pos));
948a927f
PL
545 d_pos = stop_at;
546 return ret;
547}
ef6a78d5 548
8c1c9170
BH
549void PacketReader::xfrBlob(string& blob)
550{
02d6face
RG
551 try {
552 if(d_recordlen && !(d_pos == (d_startrecordpos + d_recordlen))) {
553 if (d_pos > (d_startrecordpos + d_recordlen)) {
554 throw std::out_of_range("xfrBlob out of record range");
555 }
556 blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1);
b4d358e7 557 }
02d6face
RG
558 else {
559 blob.clear();
560 }
561
562 d_pos = d_startrecordpos + d_recordlen;
b4d358e7 563 }
02d6face
RG
564 catch(...)
565 {
566 throw std::out_of_range("xfrBlob out of range");
b4d358e7 567 }
2bc83940 568}
59a0f653 569
2fe9d6f7
AT
570void PacketReader::xfrBlobNoSpaces(string& blob, int length) {
571 xfrBlob(blob, length);
572}
573
06ffdc52
BH
574void PacketReader::xfrBlob(string& blob, int length)
575{
2617464d 576 if(length) {
b4d358e7
RG
577 if (length < 0) {
578 throw std::out_of_range("xfrBlob out of range (negative length)");
579 }
580
0407751c 581 blob.assign(&d_content.at(d_pos), &d_content.at(d_pos + length - 1 ) + 1 );
b4d358e7 582
2617464d
BH
583 d_pos += length;
584 }
b4d358e7 585 else {
2617464d 586 blob.clear();
b4d358e7 587 }
06ffdc52
BH
588}
589
373914dc
PL
590void PacketReader::xfrSvcParamKeyVals(set<SvcParam> &kvs) {
591 while (d_pos < (d_startrecordpos + d_recordlen)) {
592 if (d_pos + 2 > (d_startrecordpos + d_recordlen)) {
593 throw std::out_of_range("incomplete key");
594 }
595 uint16_t keyInt;
596 xfr16BitInt(keyInt);
597 auto key = static_cast<SvcParam::SvcParamKey>(keyInt);
598 uint16_t len;
599 xfr16BitInt(len);
bd98d61b 600
373914dc
PL
601 if (d_pos + len > (d_startrecordpos + d_recordlen)) {
602 throw std::out_of_range("record is shorter than SVCB lengthfield implies");
603 }
604
605 switch (key)
606 {
607 case SvcParam::mandatory: {
608 if (len % 2 != 0) {
609 throw std::out_of_range("mandatory SvcParam has invalid length");
610 }
611 if (len == 0) {
612 throw std::out_of_range("empty 'mandatory' values");
613 }
614 std::set<SvcParam::SvcParamKey> paramKeys;
615 size_t stop = d_pos + len;
616 while (d_pos < stop) {
617 uint16_t keyval;
618 xfr16BitInt(keyval);
619 paramKeys.insert(static_cast<SvcParam::SvcParamKey>(keyval));
620 }
96fdac2a 621 kvs.insert(SvcParam(key, std::move(paramKeys)));
373914dc
PL
622 break;
623 }
624 case SvcParam::alpn: {
625 size_t stop = d_pos + len;
626 std::vector<string> alpns;
627 while (d_pos < stop) {
628 string alpn;
629 uint8_t alpnLen = 0;
630 xfr8BitInt(alpnLen);
631 if (alpnLen == 0) {
632 throw std::out_of_range("alpn length of 0");
633 }
634 xfrBlob(alpn, alpnLen);
635 alpns.push_back(alpn);
636 }
96fdac2a 637 kvs.insert(SvcParam(key, std::move(alpns)));
373914dc
PL
638 break;
639 }
640 case SvcParam::no_default_alpn: {
641 if (len != 0) {
642 throw std::out_of_range("invalid length for no-default-alpn");
643 }
644 kvs.insert(SvcParam(key));
645 break;
646 }
647 case SvcParam::port: {
648 if (len != 2) {
649 throw std::out_of_range("invalid length for port");
650 }
651 uint16_t port;
652 xfr16BitInt(port);
653 kvs.insert(SvcParam(key, port));
654 break;
655 }
656 case SvcParam::ipv4hint: /* fall-through */
657 case SvcParam::ipv6hint: {
658 size_t addrLen = (key == SvcParam::ipv4hint ? 4 : 16);
659 if (len % addrLen != 0) {
660 throw std::out_of_range("invalid length for " + SvcParam::keyToString(key));
661 }
662 vector<ComboAddress> addresses;
663 auto stop = d_pos + len;
664 while (d_pos < stop)
665 {
666 ComboAddress addr;
667 xfrCAWithoutPort(key, addr);
668 addresses.push_back(addr);
669 }
96fdac2a 670 kvs.insert(SvcParam(key, std::move(addresses)));
373914dc
PL
671 break;
672 }
4f254e34 673 case SvcParam::ech: {
373914dc
PL
674 std::string blob;
675 blob.reserve(len);
676 xfrBlobNoSpaces(blob, len);
677 kvs.insert(SvcParam(key, blob));
678 break;
679 }
680 default: {
681 std::string blob;
682 blob.reserve(len);
683 xfrBlob(blob, len);
684 kvs.insert(SvcParam(key, blob));
685 break;
686 }
687 }
688 }
689}
690
06ffdc52 691
d73de874 692void PacketReader::xfrHexBlob(string& blob, bool /* keepReading */)
59a0f653
BH
693{
694 xfrBlob(blob);
695}
b8e0f341 696
3343ad1f 697//FIXME400 remove this method completely
27ff60a3 698string simpleCompress(const string& elabel, const string& root)
b8e0f341 699{
27ff60a3 700 string label=elabel;
3343ad1f 701 // FIXME400: this relies on the semi-canonical escaped output from getName
83b746fd 702 if(strchr(label.c_str(), '\\')) {
703 boost::replace_all(label, "\\.", ".");
704 boost::replace_all(label, "\\032", " ");
bd98d61b 705 boost::replace_all(label, "\\\\", "\\");
83b746fd 706 }
b8e0f341
BH
707 typedef vector<pair<unsigned int, unsigned int> > parts_t;
708 parts_t parts;
709 vstringtok(parts, label, ".");
710 string ret;
711 ret.reserve(label.size()+4);
d7f67000
RP
712 for(const auto & part : parts) {
713 if(!root.empty() && !strncasecmp(root.c_str(), label.c_str() + part.first, 1 + label.length() - part.first)) { // also match trailing 0, hence '1 +'
015db425
PD
714 const unsigned char rootptr[2]={0xc0,0x11};
715 ret.append((const char *) rootptr, 2);
7127879f
BH
716 return ret;
717 }
d7f67000
RP
718 ret.append(1, (char)(part.second - part.first));
719 ret.append(label.c_str() + part.first, part.second - part.first);
b8e0f341
BH
720 }
721 ret.append(1, (char)0);
722 return ret;
723}
724
153d5065 725// method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
4de01aaa 726void editDNSPacketTTL(char* packet, size_t length, const std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)>& visitor)
153d5065
RG
727{
728 if(length < sizeof(dnsheader))
729 return;
730 try
731 {
732 dnsheader dh;
733 memcpy((void*)&dh, (const dnsheader*)packet, sizeof(dh));
734 uint64_t numrecords = ntohs(dh.ancount) + ntohs(dh.nscount) + ntohs(dh.arcount);
735 DNSPacketMangler dpm(packet, length);
736
737 uint64_t n;
738 for(n=0; n < ntohs(dh.qdcount) ; ++n) {
c1780c41 739 dpm.skipDomainName();
153d5065
RG
740 /* type and class */
741 dpm.skipBytes(4);
742 }
743
744 for(n=0; n < numrecords; ++n) {
c1780c41 745 dpm.skipDomainName();
153d5065 746
d7ce446c 747 uint8_t section = n < ntohs(dh.ancount) ? 1 : (n < (ntohs(dh.ancount) + ntohs(dh.nscount)) ? 2 : 3);
153d5065
RG
748 uint16_t dnstype = dpm.get16BitInt();
749 uint16_t dnsclass = dpm.get16BitInt();
750
751 if(dnstype == QType::OPT) // not getting near that one with a stick
752 break;
753
754 uint32_t dnsttl = dpm.get32BitInt();
755 uint32_t newttl = visitor(section, dnsclass, dnstype, dnsttl);
756 if (newttl) {
757 dpm.rewindBytes(sizeof(newttl));
758 dpm.setAndSkip32BitInt(newttl);
759 }
760 dpm.skipRData();
761 }
762 }
763 catch(...)
764 {
765 return;
766 }
767}
768
953708cb 769static bool checkIfPacketContainsRecords(const PacketBuffer& packet, const std::unordered_set<QType>& qtypes)
6b9508c7
RG
770{
771 auto length = packet.size();
772 if (length < sizeof(dnsheader)) {
773 return false;
774 }
775
776 try {
90686725 777 const dnsheader_aligned dh(packet.data());
6b9508c7
RG
778 DNSPacketMangler dpm(const_cast<char*>(reinterpret_cast<const char*>(packet.data())), length);
779
780 const uint16_t qdcount = ntohs(dh->qdcount);
781 for (size_t n = 0; n < qdcount; ++n) {
782 dpm.skipDomainName();
783 /* type and class */
784 dpm.skipBytes(4);
785 }
786 const size_t recordsCount = static_cast<size_t>(ntohs(dh->ancount)) + ntohs(dh->nscount) + ntohs(dh->arcount);
787 for (size_t n = 0; n < recordsCount; ++n) {
788 dpm.skipDomainName();
789 uint16_t dnstype = dpm.get16BitInt();
790 uint16_t dnsclass = dpm.get16BitInt();
791 if (dnsclass == QClass::IN && qtypes.count(dnstype) > 0) {
792 return true;
793 }
794 /* ttl */
795 dpm.skipBytes(4);
796 dpm.skipRData();
797 }
798 }
799 catch (...) {
800 }
801
802 return false;
803}
804
953708cb 805static int rewritePacketWithoutRecordTypes(const PacketBuffer& initialPacket, PacketBuffer& newContent, const std::unordered_set<QType>& qtypes)
eb8d953f 806{
953708cb 807 static const std::unordered_set<QType>& safeTypes{QType::A, QType::AAAA, QType::DHCID, QType::TXT, QType::OPT, QType::HINFO, QType::DNSKEY, QType::CDNSKEY, QType::DS, QType::CDS, QType::DLV, QType::SSHFP, QType::KEY, QType::CERT, QType::TLSA, QType::SMIMEA, QType::OPENPGPKEY, QType::SVCB, QType::HTTPS, QType::NSEC3, QType::CSYNC, QType::NSEC3PARAM, QType::LOC, QType::NID, QType::L32, QType::L64, QType::EUI48, QType::EUI64, QType::URI, QType::CAA};
eb8d953f 808
bb09e03d
CHB
809 if (initialPacket.size() < sizeof(dnsheader)) {
810 return EINVAL;
bd98d61b 811 }
bb09e03d 812 try {
90686725 813 const dnsheader_aligned dh(initialPacket.data());
bb09e03d
CHB
814
815 if (ntohs(dh->qdcount) == 0)
816 return ENOENT;
fa5a722b 817 auto packetView = std::string_view(reinterpret_cast<const char*>(initialPacket.data()), initialPacket.size());
bb09e03d
CHB
818
819 PacketReader pr(packetView);
820
821 size_t idx = 0;
822 DNSName rrname;
823 uint16_t qdcount = ntohs(dh->qdcount);
824 uint16_t ancount = ntohs(dh->ancount);
825 uint16_t nscount = ntohs(dh->nscount);
826 uint16_t arcount = ntohs(dh->arcount);
827 uint16_t rrtype;
828 uint16_t rrclass;
829 string blob;
830 struct dnsrecordheader ah;
bd98d61b 831
bb09e03d
CHB
832 rrname = pr.getName();
833 rrtype = pr.get16BitInt();
834 rrclass = pr.get16BitInt();
835
836 GenericDNSPacketWriter<PacketBuffer> pw(newContent, rrname, rrtype, rrclass, dh->opcode);
837 pw.getHeader()->id=dh->id;
838 pw.getHeader()->qr=dh->qr;
839 pw.getHeader()->aa=dh->aa;
840 pw.getHeader()->tc=dh->tc;
841 pw.getHeader()->rd=dh->rd;
842 pw.getHeader()->ra=dh->ra;
843 pw.getHeader()->ad=dh->ad;
844 pw.getHeader()->cd=dh->cd;
845 pw.getHeader()->rcode=dh->rcode;
846
847 /* consume remaining qd if any */
848 if (qdcount > 1) {
849 for(idx = 1; idx < qdcount; idx++) {
850 rrname = pr.getName();
851 rrtype = pr.get16BitInt();
852 rrclass = pr.get16BitInt();
853 (void) rrtype;
854 (void) rrclass;
855 }
bd98d61b 856 }
bb09e03d
CHB
857
858 /* copy AN */
859 for (idx = 0; idx < ancount; idx++) {
860 rrname = pr.getName();
861 pr.getDnsrecordheader(ah);
862 pr.xfrBlob(blob);
863
864 if (qtypes.find(ah.d_type) == qtypes.end()) {
865 // if this is not a safe type
866 if (safeTypes.find(ah.d_type) == safeTypes.end()) {
867 // "unsafe" types might countain compressed data, so cancel rewrite
868 newContent.clear();
869 return EIO;
bd98d61b 870 }
bb09e03d
CHB
871 pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ANSWER, true);
872 pw.xfrBlob(blob);
bd98d61b 873 }
bb09e03d
CHB
874 }
875
876 /* copy NS */
877 for (idx = 0; idx < nscount; idx++) {
878 rrname = pr.getName();
879 pr.getDnsrecordheader(ah);
880 pr.xfrBlob(blob);
881
882 if (qtypes.find(ah.d_type) == qtypes.end()) {
883 if (safeTypes.find(ah.d_type) == safeTypes.end()) {
884 // "unsafe" types might countain compressed data, so cancel rewrite
885 newContent.clear();
886 return EIO;
887 }
888 pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::AUTHORITY, true);
889 pw.xfrBlob(blob);
890 }
891 }
892 /* copy AR */
893 for (idx = 0; idx < arcount; idx++) {
894 rrname = pr.getName();
895 pr.getDnsrecordheader(ah);
896 pr.xfrBlob(blob);
897
898 if (qtypes.find(ah.d_type) == qtypes.end()) {
899 if (safeTypes.find(ah.d_type) == safeTypes.end()) {
900 // "unsafe" types might countain compressed data, so cancel rewrite
901 newContent.clear();
902 return EIO;
903 }
904 pw.startRecord(rrname, ah.d_type, ah.d_ttl, ah.d_class, DNSResourceRecord::ADDITIONAL, true);
905 pw.xfrBlob(blob);
906 }
907 }
908 pw.commit();
909
bd98d61b 910 }
bb09e03d 911 catch (...)
bd98d61b 912 {
bb09e03d
CHB
913 newContent.clear();
914 return EIO;
915 }
916 return 0;
917}
918
953708cb 919void clearDNSPacketRecordTypes(vector<uint8_t>& packet, const std::unordered_set<QType>& qtypes)
bb09e03d
CHB
920{
921 return clearDNSPacketRecordTypes(reinterpret_cast<PacketBuffer&>(packet), qtypes);
922}
923
953708cb 924void clearDNSPacketRecordTypes(PacketBuffer& packet, const std::unordered_set<QType>& qtypes)
bb09e03d 925{
6b9508c7
RG
926 if (!checkIfPacketContainsRecords(packet, qtypes)) {
927 return;
928 }
929
bb09e03d
CHB
930 PacketBuffer newContent;
931
932 auto result = rewritePacketWithoutRecordTypes(packet, newContent, qtypes);
933 if (!result) {
934 packet = std::move(newContent);
bd98d61b
CHB
935 }
936}
937
2c73e580 938// method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
100ff9c7 939void ageDNSPacket(char* packet, size_t length, uint32_t seconds, const dnsheader_aligned& aligned_dh)
2c73e580 940{
100ff9c7 941 if (length < sizeof(dnsheader)) {
2c73e580 942 return;
100ff9c7
OM
943 }
944 try {
945 const dnsheader* dhp = aligned_dh.get();
946 const uint64_t dqcount = ntohs(dhp->qdcount);
947 const uint64_t numrecords = ntohs(dhp->ancount) + ntohs(dhp->nscount) + ntohs(dhp->arcount);
886e2cf2 948 DNSPacketMangler dpm(packet, length);
a683e8bd 949
100ff9c7 950 for (uint64_t rec = 0; rec < dqcount; ++rec) {
c1780c41 951 dpm.skipDomainName();
1819b930
RG
952 /* type and class */
953 dpm.skipBytes(4);
2c73e580 954 }
100ff9c7
OM
955
956 for(uint64_t rec = 0; rec < numrecords; ++rec) {
c1780c41 957 dpm.skipDomainName();
bd98d61b 958
2c73e580 959 uint16_t dnstype = dpm.get16BitInt();
1819b930
RG
960 /* class */
961 dpm.skipBytes(2);
bd98d61b 962
4204efb4
OM
963 if (dnstype != QType::OPT) { // not aging that one with a stick
964 dpm.decreaseAndSkip32BitInt(seconds);
965 } else {
966 dpm.skipBytes(4);
100ff9c7 967 }
2c73e580
BH
968 dpm.skipRData();
969 }
970 }
100ff9c7 971 catch(...) {
2c73e580
BH
972 }
973}
886e2cf2 974
100ff9c7 975void ageDNSPacket(std::string& packet, uint32_t seconds, const dnsheader_aligned& aligned_dh)
886e2cf2 976{
100ff9c7 977 ageDNSPacket(packet.data(), packet.length(), seconds, aligned_dh);
886e2cf2 978}
0766890a 979
47698274 980uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA)
0766890a
RG
981{
982 uint32_t result = std::numeric_limits<uint32_t>::max();
983 if(length < sizeof(dnsheader)) {
984 return result;
985 }
986 try
987 {
90686725 988 const dnsheader_aligned dh(packet);
0766890a
RG
989 DNSPacketMangler dpm(const_cast<char*>(packet), length);
990
991 const uint16_t qdcount = ntohs(dh->qdcount);
992 for(size_t n = 0; n < qdcount; ++n) {
c1780c41 993 dpm.skipDomainName();
1819b930
RG
994 /* type and class */
995 dpm.skipBytes(4);
0766890a
RG
996 }
997 const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
998 for(size_t n = 0; n < numrecords; ++n) {
c1780c41 999 dpm.skipDomainName();
0766890a 1000 const uint16_t dnstype = dpm.get16BitInt();
1819b930 1001 /* class */
47698274 1002 const uint16_t dnsclass = dpm.get16BitInt();
0766890a 1003
d7ce446c 1004 if(dnstype == QType::OPT) {
0766890a 1005 break;
d7ce446c
RG
1006 }
1007
1008 /* report it if we see a SOA record in the AUTHORITY section */
cfd669b4 1009 if(dnstype == QType::SOA && dnsclass == QClass::IN && seenAuthSOA != nullptr && n >= ntohs(dh->ancount) && n < (ntohs(dh->ancount) + ntohs(dh->nscount))) {
d7ce446c
RG
1010 *seenAuthSOA = true;
1011 }
0766890a
RG
1012
1013 const uint32_t ttl = dpm.get32BitInt();
47698274 1014 if (result > ttl) {
0766890a 1015 result = ttl;
47698274 1016 }
0766890a
RG
1017
1018 dpm.skipRData();
1019 }
1020 }
1021 catch(...)
1022 {
1023 }
1024 return result;
1025}
55baa1f2
RG
1026
1027uint32_t getDNSPacketLength(const char* packet, size_t length)
1028{
1029 uint32_t result = length;
1030 if(length < sizeof(dnsheader)) {
1031 return result;
1032 }
1033 try
1034 {
90686725 1035 const dnsheader_aligned dh(packet);
55baa1f2
RG
1036 DNSPacketMangler dpm(const_cast<char*>(packet), length);
1037
1038 const uint16_t qdcount = ntohs(dh->qdcount);
1039 for(size_t n = 0; n < qdcount; ++n) {
c1780c41 1040 dpm.skipDomainName();
1819b930
RG
1041 /* type and class */
1042 dpm.skipBytes(4);
55baa1f2
RG
1043 }
1044 const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
1045 for(size_t n = 0; n < numrecords; ++n) {
c1780c41 1046 dpm.skipDomainName();
1819b930
RG
1047 /* type (2), class (2) and ttl (4) */
1048 dpm.skipBytes(8);
55baa1f2
RG
1049 dpm.skipRData();
1050 }
1051 result = dpm.getOffset();
1052 }
1053 catch(...)
1054 {
1055 }
1056 return result;
1057}
1058
1059uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type)
1060{
1061 uint16_t result = 0;
1062 if(length < sizeof(dnsheader)) {
1063 return result;
1064 }
1065 try
1066 {
90686725 1067 const dnsheader_aligned dh(packet);
55baa1f2
RG
1068 DNSPacketMangler dpm(const_cast<char*>(packet), length);
1069
1070 const uint16_t qdcount = ntohs(dh->qdcount);
1071 for(size_t n = 0; n < qdcount; ++n) {
c1780c41 1072 dpm.skipDomainName();
55baa1f2
RG
1073 if (section == 0) {
1074 uint16_t dnstype = dpm.get16BitInt();
1075 if (dnstype == type) {
1076 result++;
1077 }
1819b930
RG
1078 /* class */
1079 dpm.skipBytes(2);
55baa1f2 1080 } else {
1819b930
RG
1081 /* type and class */
1082 dpm.skipBytes(4);
55baa1f2
RG
1083 }
1084 }
1085 const uint16_t ancount = ntohs(dh->ancount);
1086 for(size_t n = 0; n < ancount; ++n) {
c1780c41 1087 dpm.skipDomainName();
55baa1f2
RG
1088 if (section == 1) {
1089 uint16_t dnstype = dpm.get16BitInt();
1090 if (dnstype == type) {
1091 result++;
1092 }
1819b930
RG
1093 /* class */
1094 dpm.skipBytes(2);
55baa1f2 1095 } else {
1819b930
RG
1096 /* type and class */
1097 dpm.skipBytes(4);
55baa1f2 1098 }
1819b930
RG
1099 /* ttl */
1100 dpm.skipBytes(4);
55baa1f2
RG
1101 dpm.skipRData();
1102 }
1103 const uint16_t nscount = ntohs(dh->nscount);
1104 for(size_t n = 0; n < nscount; ++n) {
c1780c41 1105 dpm.skipDomainName();
55baa1f2
RG
1106 if (section == 2) {
1107 uint16_t dnstype = dpm.get16BitInt();
1108 if (dnstype == type) {
1109 result++;
1110 }
1819b930
RG
1111 /* class */
1112 dpm.skipBytes(2);
55baa1f2 1113 } else {
1819b930
RG
1114 /* type and class */
1115 dpm.skipBytes(4);
55baa1f2 1116 }
1819b930
RG
1117 /* ttl */
1118 dpm.skipBytes(4);
55baa1f2
RG
1119 dpm.skipRData();
1120 }
1121 const uint16_t arcount = ntohs(dh->arcount);
1122 for(size_t n = 0; n < arcount; ++n) {
c1780c41 1123 dpm.skipDomainName();
55baa1f2
RG
1124 if (section == 3) {
1125 uint16_t dnstype = dpm.get16BitInt();
1126 if (dnstype == type) {
1127 result++;
1128 }
1819b930
RG
1129 /* class */
1130 dpm.skipBytes(2);
55baa1f2 1131 } else {
1819b930
RG
1132 /* type and class */
1133 dpm.skipBytes(4);
55baa1f2 1134 }
1819b930
RG
1135 /* ttl */
1136 dpm.skipBytes(4);
55baa1f2
RG
1137 dpm.skipRData();
1138 }
1139 }
1140 catch(...)
1141 {
1142 }
1143 return result;
1144}
e7c732b8 1145
e0fd37ec 1146bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z)
e7c732b8
RG
1147{
1148 if (length < sizeof(dnsheader)) {
e0fd37ec 1149 return false;
e7c732b8
RG
1150 }
1151
e0fd37ec
RG
1152 *payloadSize = 0;
1153 *z = 0;
1154
e7c732b8
RG
1155 try
1156 {
90686725 1157 const dnsheader_aligned dh(packet);
e7c732b8
RG
1158 DNSPacketMangler dpm(const_cast<char*>(packet), length);
1159
1160 const uint16_t qdcount = ntohs(dh->qdcount);
1161 for(size_t n = 0; n < qdcount; ++n) {
c1780c41 1162 dpm.skipDomainName();
e7c732b8
RG
1163 /* type and class */
1164 dpm.skipBytes(4);
1165 }
1166 const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
1167 for(size_t n = 0; n < numrecords; ++n) {
c1780c41 1168 dpm.skipDomainName();
e7c732b8
RG
1169 const uint16_t dnstype = dpm.get16BitInt();
1170 const uint16_t dnsclass = dpm.get16BitInt();
1171
1172 if(dnstype == QType::OPT) {
e0fd37ec
RG
1173 /* skip extended rcode and version */
1174 dpm.skipBytes(2);
1175 *z = dpm.get16BitInt();
1176 *payloadSize = dnsclass;
1177 return true;
e7c732b8
RG
1178 }
1179
1180 /* TTL */
1181 dpm.skipBytes(4);
1182 dpm.skipRData();
1183 }
1184 }
1185 catch(...)
1186 {
1187 }
e0fd37ec
RG
1188
1189 return false;
e7c732b8 1190}
fec4382e
RG
1191
1192bool visitDNSPacket(const std::string_view& packet, const std::function<bool(uint8_t, uint16_t, uint16_t, uint32_t, uint16_t, const char*)>& visitor)
1193{
1194 if (packet.size() < sizeof(dnsheader)) {
1195 return false;
1196 }
1197
1198 try
1199 {
90686725
RG
1200 const dnsheader_aligned dh(packet.data());
1201 uint64_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
fec4382e
RG
1202 PacketReader reader(packet);
1203
1204 uint64_t n;
90686725 1205 for (n = 0; n < ntohs(dh->qdcount) ; ++n) {
fec4382e
RG
1206 (void) reader.getName();
1207 /* type and class */
1208 reader.skip(4);
1209 }
1210
1211 for (n = 0; n < numrecords; ++n) {
1212 (void) reader.getName();
1213
90686725 1214 uint8_t section = n < ntohs(dh->ancount) ? 1 : (n < (ntohs(dh->ancount) + ntohs(dh->nscount)) ? 2 : 3);
fec4382e
RG
1215 uint16_t dnstype = reader.get16BitInt();
1216 uint16_t dnsclass = reader.get16BitInt();
1217
1218 if (dnstype == QType::OPT) {
1219 // not getting near that one with a stick
1220 break;
1221 }
1222
1223 uint32_t dnsttl = reader.get32BitInt();
1224 uint16_t contentLength = reader.get16BitInt();
1225 uint16_t pos = reader.getPosition();
1226
1227 bool done = visitor(section, dnsclass, dnstype, dnsttl, contentLength, &packet.at(pos));
1228 if (done) {
1229 return true;
1230 }
1231
1232 reader.skip(contentLength);
1233 }
1234 }
1235 catch (...) {
1236 return false;
1237 }
1238
1239 return true;
1240}