]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsparser.cc
auth: switch circleci mssql image
[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"
ff6a1e7b
BH
28
29class UnknownRecordContent : public DNSRecordContent
30{
31public:
7fc69fd0 32 UnknownRecordContent(const DNSRecord& dr, PacketReader& pr)
5a1f298f 33 : d_dr(dr)
ff6a1e7b 34 {
7fc69fd0 35 pr.copyRecord(d_record, dr.d_clen);
ff6a1e7b
BH
36 }
37
5a1f298f 38 UnknownRecordContent(const string& zone)
6c0670c3 39 {
95a61c6f
AT
40 // parse the input
41 vector<string> parts;
42 stringtok(parts, zone);
43 if(parts.size()!=3 && !(parts.size()==2 && equals(parts[1],"0")) )
335da0ba 44 throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got "+std::to_string(parts.size())+": "+zone );
95a61c6f 45 const string& relevant=(parts.size() > 2) ? parts[2] : "";
335da0ba 46 unsigned int total=pdns_stou(parts[1]);
e007fb02
RG
47 if(relevant.size() % 2 || relevant.size() / 2 != total)
48 throw MOADNSException((boost::format("invalid unknown record length: size not equal to length field (%d != 2 * %d)") % relevant.size() % total).str());
95a61c6f
AT
49 string out;
50 out.reserve(total+1);
51 for(unsigned int n=0; n < total; ++n) {
52 int c;
53 sscanf(relevant.c_str()+2*n, "%02x", &c);
54 out.append(1, (char)c);
55 }
56
57 d_record.insert(d_record.end(), out.begin(), out.end());
6c0670c3 58 }
cff3e04d 59
f21fc0aa 60 string getZoneRepresentation(bool noDot) const override
ff6a1e7b
BH
61 {
62 ostringstream str;
705f31ae 63 str<<"\\# "<<(unsigned int)d_record.size()<<" ";
ff6a1e7b
BH
64 char hex[4];
65 for(size_t n=0; n<d_record.size(); ++n) {
9b2244e1 66 snprintf(hex, sizeof(hex), "%02x", d_record.at(n));
ff6a1e7b
BH
67 str << hex;
68 }
ff6a1e7b
BH
69 return str.str();
70 }
cff3e04d
CH
71
72 void toPacket(DNSPacketWriter& pw) override
6c0670c3 73 {
95a61c6f 74 pw.xfrBlob(string(d_record.begin(),d_record.end()));
6c0670c3 75 }
5a1f298f 76
cff3e04d 77 uint16_t getType() const override
5a1f298f 78 {
79 return d_dr.d_type;
80 }
ff6a1e7b 81private:
6c0670c3
BH
82 DNSRecord d_dr;
83 vector<uint8_t> d_record;
ff6a1e7b
BH
84};
85
561434a6 86shared_ptr<DNSRecordContent> DNSRecordContent::unserialize(const DNSName& qname, uint16_t qtype, const string& serialized)
ea634573
BH
87{
88 dnsheader dnsheader;
89 memset(&dnsheader, 0, sizeof(dnsheader));
90 dnsheader.qdcount=htons(1);
91 dnsheader.ancount=htons(1);
92
93 vector<uint8_t> packet; // build pseudo packet
678ce973
BH
94
95 /* will look like: dnsheader, 5 bytes, encoded qname, dns record header, serialized data */
ea634573 96
98584698 97 string encoded=qname.toDNSString();
678ce973
BH
98
99 packet.resize(sizeof(dnsheader) + 5 + encoded.size() + sizeof(struct dnsrecordheader) + serialized.size());
100
101 uint16_t pos=0;
102
103 memcpy(&packet[0], &dnsheader, sizeof(dnsheader)); pos+=sizeof(dnsheader);
104
105 char tmp[6]="\x0" "\x0\x1" "\x0\x1"; // root question for ns_t_a
106 memcpy(&packet[pos], &tmp, 5); pos+=5;
107
705f31ae 108 memcpy(&packet[pos], encoded.c_str(), encoded.size()); pos+=(uint16_t)encoded.size();
ea634573
BH
109
110 struct dnsrecordheader drh;
111 drh.d_type=htons(qtype);
78f56b38 112 drh.d_class=htons(QClass::IN);
ea634573
BH
113 drh.d_ttl=0;
114 drh.d_clen=htons(serialized.size());
115
678ce973 116 memcpy(&packet[pos], &drh, sizeof(drh)); pos+=sizeof(drh);
705f31ae 117 memcpy(&packet[pos], serialized.c_str(), serialized.size()); pos+=(uint16_t)serialized.size();
ea634573 118
27c0050c 119 MOADNSParser mdp(false, (char*)&*packet.begin(), (unsigned int)packet.size());
ea634573 120 shared_ptr<DNSRecordContent> ret= mdp.d_answers.begin()->first.d_content;
ea634573
BH
121 return ret;
122}
ff6a1e7b 123
6177a176 124std::shared_ptr<DNSRecordContent> DNSRecordContent::mastermake(const DNSRecord &dr,
232f0877 125 PacketReader& pr)
ff6a1e7b 126{
7f7b8d55
BH
127 uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT
128
129 typemap_t::const_iterator i=getTypemap().find(make_pair(searchclass, dr.d_type));
49a06471 130 if(i==getTypemap().end() || !i->second) {
6177a176 131 return std::make_shared<UnknownRecordContent>(dr, pr);
ff6a1e7b 132 }
945a9ad4 133
32122aab 134 return i->second(dr, pr);
ff6a1e7b
BH
135}
136
6177a176 137std::shared_ptr<DNSRecordContent> DNSRecordContent::mastermake(uint16_t qtype, uint16_t qclass,
232f0877 138 const string& content)
6c0670c3 139{
49a06471
BH
140 zmakermap_t::const_iterator i=getZmakermap().find(make_pair(qclass, qtype));
141 if(i==getZmakermap().end()) {
6177a176 142 return std::make_shared<UnknownRecordContent>(content);
6c0670c3
BH
143 }
144
32122aab 145 return i->second(content);
6c0670c3
BH
146}
147
6177a176 148std::shared_ptr<DNSRecordContent> DNSRecordContent::mastermake(const DNSRecord &dr, PacketReader& pr, uint16_t oc) {
7945d472
RA
149 // For opcode UPDATE and where the DNSRecord is an answer record, we don't care about content, because this is
150 // not used within the prerequisite section of RFC2136, so - we can simply use unknownrecordcontent.
89c46d18 151 // For section 3.2.3, we do need content so we need to get it properly. But only for the correct QClasses.
e693ff5a 152 if (oc == Opcode::Update && dr.d_place == DNSResourceRecord::ANSWER && dr.d_class != 1)
6177a176 153 return std::make_shared<UnknownRecordContent>(dr, pr);
914353ca 154
7945d472
RA
155 uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT
156
157 typemap_t::const_iterator i=getTypemap().find(make_pair(searchclass, dr.d_type));
158 if(i==getTypemap().end() || !i->second) {
6177a176 159 return std::make_shared<UnknownRecordContent>(dr, pr);
7945d472
RA
160 }
161
32122aab 162 return i->second(dr, pr);
7945d472
RA
163}
164
165
49a06471
BH
166DNSRecordContent::typemap_t& DNSRecordContent::getTypemap()
167{
168 static DNSRecordContent::typemap_t typemap;
169 return typemap;
170}
171
108c321e 172DNSRecordContent::n2typemap_t& DNSRecordContent::getN2Typemap()
49a06471 173{
93e4cb97
BH
174 static DNSRecordContent::n2typemap_t n2typemap;
175 return n2typemap;
49a06471
BH
176}
177
108c321e
BH
178DNSRecordContent::t2namemap_t& DNSRecordContent::getT2Namemap()
179{
93e4cb97
BH
180 static DNSRecordContent::t2namemap_t t2namemap;
181 return t2namemap;
108c321e
BH
182}
183
49a06471
BH
184DNSRecordContent::zmakermap_t& DNSRecordContent::getZmakermap()
185{
186 static DNSRecordContent::zmakermap_t zmakermap;
187 return zmakermap;
188}
189
5708a729 190DNSRecord::DNSRecord(const DNSResourceRecord& rr): d_name(rr.qname)
fbe23591 191{
fbe23591 192 d_type = rr.qtype.getCode();
193 d_ttl = rr.ttl;
194 d_class = rr.qclass;
4950b140 195 d_place = DNSResourceRecord::ANSWER;
e24c18b0 196 d_clen = 0;
6177a176 197 d_content = DNSRecordContent::mastermake(d_type, rr.qclass, rr.content);
fbe23591 198}
199
4950b140
CH
200// If you call this and you are not parsing a packet coming from a socket, you are doing it wrong.
201DNSResourceRecord DNSResourceRecord::fromWire(const DNSRecord& d) {
202 DNSResourceRecord rr;
203 rr.qname = d.d_name;
204 rr.qtype = QType(d.d_type);
205 rr.ttl = d.d_ttl;
bb7123d3 206 rr.content = d.d_content->getZoneRepresentation(true);
4950b140
CH
207 rr.auth = false;
208 rr.qclass = d.d_class;
209 return rr;
210}
211
78f56b38 212void MOADNSParser::init(bool query, const std::string& packet)
ff6a1e7b 213{
78f56b38 214 if (packet.size() < sizeof(dnsheader))
ff6a1e7b
BH
215 throw MOADNSException("Packet shorter than minimal header");
216
78f56b38 217 memcpy(&d_header, packet.data(), sizeof(dnsheader));
ff6a1e7b 218
7945d472 219 if(d_header.opcode != Opcode::Query && d_header.opcode != Opcode::Notify && d_header.opcode != Opcode::Update)
335da0ba 220 throw MOADNSException("Can't parse non-query packet with opcode="+ std::to_string(d_header.opcode));
f27e6356 221
ff6a1e7b
BH
222 d_header.qdcount=ntohs(d_header.qdcount);
223 d_header.ancount=ntohs(d_header.ancount);
224 d_header.nscount=ntohs(d_header.nscount);
225 d_header.arcount=ntohs(d_header.arcount);
27c0050c
RG
226
227 if (query && (d_header.qdcount > 1))
228 throw MOADNSException("Query with QD > 1 ("+std::to_string(d_header.qdcount)+")");
ff6a1e7b 229
629eaf49 230 unsigned int n=0;
ff6a1e7b 231
78f56b38 232 PacketReader pr(packet);
7b1469bb 233 bool validPacket=false;
512c8492 234 try {
00bf10d3
BH
235 d_qtype = d_qclass = 0; // sometimes replies come in with no question, don't present garbage then
236
40207e64 237 for(n=0;n < d_header.qdcount; ++n) {
8ad443ea 238 d_qname=pr.getName();
40207e64
BH
239 d_qtype=pr.get16BitInt();
240 d_qclass=pr.get16BitInt();
241 }
242
512c8492
BH
243 struct dnsrecordheader ah;
244 vector<unsigned char> record;
04151caa 245 bool seenTSIG = false;
7b1469bb 246 validPacket=true;
756e82cf 247 d_answers.reserve((unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount));
c4ac5865 248 for(n=0;n < (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount); ++n) {
512c8492
BH
249 DNSRecord dr;
250
251 if(n < d_header.ancount)
e693ff5a 252 dr.d_place=DNSResourceRecord::ANSWER;
512c8492 253 else if(n < d_header.ancount + d_header.nscount)
e693ff5a 254 dr.d_place=DNSResourceRecord::AUTHORITY;
512c8492 255 else
e693ff5a 256 dr.d_place=DNSResourceRecord::ADDITIONAL;
04151caa 257
78f56b38 258 unsigned int recordStartPos=pr.getPosition();
57e5f5f7 259
f809c028 260 DNSName name=pr.getName();
04151caa 261
512c8492
BH
262 pr.getDnsrecordheader(ah);
263 dr.d_ttl=ah.d_ttl;
264 dr.d_type=ah.d_type;
265 dr.d_class=ah.d_class;
04151caa 266
f809c028 267 dr.d_name=name;
512c8492 268 dr.d_clen=ah.d_clen;
7b1469bb 269
c2a1b24d
PL
270 if (query &&
271 !(d_qtype == QType::IXFR && dr.d_place == DNSResourceRecord::AUTHORITY && dr.d_type == QType::SOA) && // IXFR queries have a SOA in their AUTHORITY section
272 (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 273// cerr<<"discarding RR, query is "<<query<<", place is "<<dr.d_place<<", type is "<<dr.d_type<<", class is "<<dr.d_class<<endl;
63fc7705 274 dr.d_content=std::make_shared<UnknownRecordContent>(dr, pr);
27c0050c
RG
275 }
276 else {
277// cerr<<"parsing RR, query is "<<query<<", place is "<<dr.d_place<<", type is "<<dr.d_type<<", class is "<<dr.d_class<<endl;
6177a176 278 dr.d_content=DNSRecordContent::mastermake(dr, pr, d_header.opcode);
27c0050c
RG
279 }
280
78f56b38 281 d_answers.push_back(make_pair(dr, pr.getPosition() - sizeof(dnsheader)));
57e5f5f7 282
18f707fa
RG
283 /* XXX: XPF records should be allowed after TSIG as soon as the actual XPF option code has been assigned:
284 if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG && dr.d_type != QType::XPF)
285 */
286 if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG) {
04151caa
RG
287 /* only XPF records are allowed after a TSIG */
288 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.");
289 }
290
60a1c204 291 if(dr.d_type == QType::TSIG && dr.d_class == QClass::ANY) {
18f707fa 292 if(seenTSIG || dr.d_place != DNSResourceRecord::ADDITIONAL) {
86f1af1c 293 throw MOADNSException("Packet ("+d_qname.toLogString()+"|#"+std::to_string(d_qtype)+") has a TSIG record in an invalid position.");
60a1c204 294 }
04151caa 295 seenTSIG = true;
78f56b38 296 d_tsigPos = recordStartPos;
60a1c204 297 }
512c8492 298 }
e5986c84 299
04151caa 300#if 0
78f56b38
RG
301 if(pr.getPosition()!=packet.size()) {
302 throw MOADNSException("Packet ("+d_qname+"|#"+std::to_string(d_qtype)+") has trailing garbage ("+ std::to_string(pr.getPosition()) + " < " +
303 std::to_string(packet.size()) + ")");
ff6a1e7b 304 }
04151caa 305#endif
512c8492 306 }
78f56b38 307 catch(const std::out_of_range &re) {
629eaf49
BH
308 if(validPacket && d_header.tc) { // don't sweat it over truncated packets, but do adjust an, ns and arcount
309 if(n < d_header.ancount) {
4957a608 310 d_header.ancount=n; d_header.nscount = d_header.arcount = 0;
629eaf49
BH
311 }
312 else if(n < d_header.ancount + d_header.nscount) {
4957a608 313 d_header.nscount = n - d_header.ancount; d_header.arcount=0;
629eaf49
BH
314 }
315 else {
4957a608 316 d_header.arcount = n - d_header.ancount - d_header.nscount;
629eaf49
BH
317 }
318 }
319 else {
78f56b38 320 throw MOADNSException("Error parsing packet of "+std::to_string(packet.size())+" bytes (rd="+
335da0ba 321 std::to_string(d_header.rd)+
232f0877 322 "), out of bounds: "+string(re.what()));
629eaf49 323 }
ff6a1e7b 324 }
ff6a1e7b
BH
325}
326
10321a98 327
ff6a1e7b
BH
328void PacketReader::getDnsrecordheader(struct dnsrecordheader &ah)
329{
330 unsigned int n;
331 unsigned char *p=reinterpret_cast<unsigned char*>(&ah);
332
333 for(n=0; n < sizeof(dnsrecordheader); ++n)
334 p[n]=d_content.at(d_pos++);
335
336 ah.d_type=ntohs(ah.d_type);
337 ah.d_class=ntohs(ah.d_class);
338 ah.d_clen=ntohs(ah.d_clen);
339 ah.d_ttl=ntohl(ah.d_ttl);
8c1c9170
BH
340
341 d_startrecordpos=d_pos; // needed for getBlob later on
342 d_recordlen=ah.d_clen;
ff6a1e7b
BH
343}
344
345
092f210a 346void PacketReader::copyRecord(vector<unsigned char>& dest, uint16_t len)
ff6a1e7b
BH
347{
348 dest.resize(len);
bff744a8
BH
349 if(!len)
350 return;
351
092f210a 352 for(uint16_t n=0;n<len;++n) {
ff6a1e7b
BH
353 dest.at(n)=d_content.at(d_pos++);
354 }
355}
356
092f210a 357void PacketReader::copyRecord(unsigned char* dest, uint16_t len)
ff6a1e7b
BH
358{
359 if(d_pos + len > d_content.size())
10f4eea8 360 throw std::out_of_range("Attempt to copy outside of packet");
ff6a1e7b 361
bff744a8 362 memcpy(dest, &d_content.at(d_pos), len);
ff6a1e7b
BH
363 d_pos+=len;
364}
365
341930bb
BH
366void PacketReader::xfr48BitInt(uint64_t& ret)
367{
368 ret=0;
78f56b38 369 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 370 ret<<=8;
78f56b38 371 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 372 ret<<=8;
78f56b38 373 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 374 ret<<=8;
78f56b38 375 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 376 ret<<=8;
78f56b38 377 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 378 ret<<=8;
78f56b38 379 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 380}
ff6a1e7b 381
092f210a 382uint32_t PacketReader::get32BitInt()
ff6a1e7b 383{
092f210a 384 uint32_t ret=0;
78f56b38 385 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b 386 ret<<=8;
78f56b38 387 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b 388 ret<<=8;
78f56b38 389 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b 390 ret<<=8;
78f56b38 391 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b
BH
392
393 return ret;
394}
395
396
092f210a 397uint16_t PacketReader::get16BitInt()
ff6a1e7b 398{
092f210a 399 uint16_t ret=0;
78f56b38 400 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b 401 ret<<=8;
78f56b38 402 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b
BH
403
404 return ret;
405}
406
49a06471 407uint8_t PacketReader::get8BitInt()
ff6a1e7b
BH
408{
409 return d_content.at(d_pos++);
410}
411
8171ab83 412DNSName PacketReader::getName()
ff6a1e7b 413{
d926c0da 414 unsigned int consumed;
c1229531 415 try {
78f56b38 416 DNSName dn((const char*) d_content.data(), d_content.size(), d_pos, true /* uncompress */, 0 /* qtype */, 0 /* qclass */, &consumed, sizeof(dnsheader));
c1229531 417
418 d_pos+=consumed;
8171ab83 419 return dn;
c1229531 420 }
6177a176
RG
421 catch(const std::range_error& re) {
422 throw std::out_of_range(string("dnsname issue: ")+re.what());
423 }
424 catch(...) {
425 throw std::out_of_range("dnsname issue");
426 }
fb38d90b 427 throw PDNSException("PacketReader::getName(): name is empty");
ff6a1e7b
BH
428}
429
ef6a78d5
BH
430static string txtEscape(const string &name)
431{
432 string ret;
5dba9d25 433 char ebuf[5];
ef6a78d5 434
5dba9d25 435 for(string::const_iterator i=name.begin();i!=name.end();++i) {
fa1dc694 436 if((unsigned char) *i >= 127 || (unsigned char) *i < 32) {
5dba9d25
PD
437 snprintf(ebuf, sizeof(ebuf), "\\%03u", (unsigned char)*i);
438 ret += ebuf;
439 }
66b40966 440 else if(*i=='"' || *i=='\\'){
ef6a78d5
BH
441 ret += '\\';
442 ret += *i;
443 }
444 else
445 ret += *i;
5dba9d25 446 }
ef6a78d5
BH
447 return ret;
448}
449
450// exceptions thrown here do not result in logging in the main pdns auth server - just so you know!
84e1142d 451string PacketReader::getText(bool multi, bool lenField)
9d9c52ef
BH
452{
453 string ret;
454 ret.reserve(40);
ef6a78d5
BH
455 while(d_pos < d_startrecordpos + d_recordlen ) {
456 if(!ret.empty()) {
457 ret.append(1,' ');
458 }
84e1142d
PL
459 uint16_t labellen;
460 if(lenField)
78f56b38 461 labellen=static_cast<uint8_t>(d_content.at(d_pos++));
84e1142d
PL
462 else
463 labellen=d_recordlen - (d_pos - d_startrecordpos);
ef6a78d5
BH
464
465 ret.append(1,'"');
950f78df
BH
466 if(labellen) { // no need to do anything for an empty string
467 string val(&d_content.at(d_pos), &d_content.at(d_pos+labellen-1)+1);
468 ret.append(txtEscape(val)); // the end is one beyond the packet
469 }
ef6a78d5
BH
470 ret.append(1,'"');
471 d_pos+=labellen;
472 if(!multi)
473 break;
474 }
9d9c52ef 475
9d9c52ef
BH
476 return ret;
477}
ff6a1e7b 478
948a927f
PL
479string PacketReader::getUnquotedText(bool lenField)
480{
a94e5580 481 uint16_t stop_at;
948a927f 482 if(lenField)
78f56b38 483 stop_at = static_cast<uint8_t>(d_content.at(d_pos)) + d_pos + 1;
948a927f
PL
484 else
485 stop_at = d_recordlen;
486
d7bbbcf4
RG
487 /* think unsigned overflow */
488 if (stop_at < d_pos) {
489 throw std::out_of_range("getUnquotedText out of record range");
490 }
491
948a927f
PL
492 if(stop_at == d_pos)
493 return "";
494
495 d_pos++;
496 string ret(&d_content.at(d_pos), &d_content.at(stop_at));
497 d_pos = stop_at;
498 return ret;
499}
ef6a78d5 500
8c1c9170 501void PacketReader::xfrBlob(string& blob)
2bc83940 502try
8c1c9170 503{
b4d358e7
RG
504 if(d_recordlen && !(d_pos == (d_startrecordpos + d_recordlen))) {
505 if (d_pos > (d_startrecordpos + d_recordlen)) {
506 throw std::out_of_range("xfrBlob out of record range");
507 }
7f7b8d55 508 blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1);
b4d358e7
RG
509 }
510 else {
7f7b8d55 511 blob.clear();
b4d358e7 512 }
8c1c9170
BH
513
514 d_pos = d_startrecordpos + d_recordlen;
515}
2bc83940 516catch(...)
517{
518 throw std::out_of_range("xfrBlob out of range");
519}
59a0f653 520
2fe9d6f7
AT
521void PacketReader::xfrBlobNoSpaces(string& blob, int length) {
522 xfrBlob(blob, length);
523}
524
06ffdc52
BH
525void PacketReader::xfrBlob(string& blob, int length)
526{
2617464d 527 if(length) {
b4d358e7
RG
528 if (length < 0) {
529 throw std::out_of_range("xfrBlob out of range (negative length)");
530 }
531
0407751c 532 blob.assign(&d_content.at(d_pos), &d_content.at(d_pos + length - 1 ) + 1 );
b4d358e7 533
2617464d
BH
534 d_pos += length;
535 }
b4d358e7 536 else {
2617464d 537 blob.clear();
b4d358e7 538 }
06ffdc52
BH
539}
540
541
e4090157 542void PacketReader::xfrHexBlob(string& blob, bool keepReading)
59a0f653
BH
543{
544 xfrBlob(blob);
545}
b8e0f341 546
3343ad1f 547//FIXME400 remove this method completely
27ff60a3 548string simpleCompress(const string& elabel, const string& root)
b8e0f341 549{
27ff60a3 550 string label=elabel;
3343ad1f 551 // FIXME400: this relies on the semi-canonical escaped output from getName
83b746fd 552 if(strchr(label.c_str(), '\\')) {
553 boost::replace_all(label, "\\.", ".");
554 boost::replace_all(label, "\\032", " ");
555 boost::replace_all(label, "\\\\", "\\");
556 }
b8e0f341
BH
557 typedef vector<pair<unsigned int, unsigned int> > parts_t;
558 parts_t parts;
559 vstringtok(parts, label, ".");
560 string ret;
561 ret.reserve(label.size()+4);
562 for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) {
05a38bfa 563 if(!root.empty() && !strncasecmp(root.c_str(), label.c_str() + i->first, 1 + label.length() - i->first)) { // also match trailing 0, hence '1 +'
015db425
PD
564 const unsigned char rootptr[2]={0xc0,0x11};
565 ret.append((const char *) rootptr, 2);
7127879f
BH
566 return ret;
567 }
b8e0f341
BH
568 ret.append(1, (char)(i->second - i->first));
569 ret.append(label.c_str() + i->first, i->second - i->first);
570 }
571 ret.append(1, (char)0);
572 return ret;
573}
574
7127879f 575
2c73e580
BH
576/** Simple DNSPacketMangler. Ritual is: get a pointer into the packet and moveOffset() to beyond your needs
577 * If you survive that, feel free to read from the pointer */
578class DNSPacketMangler
579{
580public:
581 explicit DNSPacketMangler(std::string& packet)
886e2cf2
RG
582 : d_packet((char*) packet.c_str()), d_length(packet.length()), d_notyouroffset(12), d_offset(d_notyouroffset)
583 {}
584 DNSPacketMangler(char* packet, size_t length)
585 : d_packet(packet), d_length(length), d_notyouroffset(12), d_offset(d_notyouroffset)
2c73e580
BH
586 {}
587
4f9597f5
RG
588 /*! Advances past a wire-format domain name
589 * The name is not checked for adherence to length restrictions.
590 * Compression pointers are not followed.
591 */
c1780c41 592 void skipDomainName()
2c73e580
BH
593 {
594 uint8_t len;
595 while((len=get8BitInt())) {
596 if(len >= 0xc0) { // extended label
232f0877
CH
597 get8BitInt();
598 return;
2c73e580
BH
599 }
600 skipBytes(len);
601 }
602 }
4f9597f5 603
2c73e580
BH
604 void skipBytes(uint16_t bytes)
605 {
153d5065
RG
606 moveOffset(bytes);
607 }
608 void rewindBytes(uint16_t by)
609 {
610 rewindOffset(by);
2c73e580 611 }
0766890a
RG
612 uint32_t get32BitInt()
613 {
614 const char* p = d_packet + d_offset;
615 moveOffset(4);
616 uint32_t ret;
a683e8bd 617 memcpy(&ret, (void*)p, sizeof(ret));
0766890a
RG
618 return ntohl(ret);
619 }
2c73e580
BH
620 uint16_t get16BitInt()
621 {
886e2cf2 622 const char* p = d_packet + d_offset;
2c73e580
BH
623 moveOffset(2);
624 uint16_t ret;
a683e8bd 625 memcpy(&ret, (void*)p, sizeof(ret));
2c73e580
BH
626 return ntohs(ret);
627 }
628
629 uint8_t get8BitInt()
630 {
886e2cf2 631 const char* p = d_packet + d_offset;
2c73e580
BH
632 moveOffset(1);
633 return *p;
634 }
635
636 void skipRData()
637 {
638 int toskip = get16BitInt();
639 moveOffset(toskip);
640 }
153d5065 641
2c73e580
BH
642 void decreaseAndSkip32BitInt(uint32_t decrease)
643 {
886e2cf2 644 const char *p = d_packet + d_offset;
2c73e580 645 moveOffset(4);
153d5065 646
2c73e580
BH
647 uint32_t tmp;
648 memcpy(&tmp, (void*) p, sizeof(tmp));
649 tmp = ntohl(tmp);
650 tmp-=decrease;
651 tmp = htonl(tmp);
886e2cf2 652 memcpy(d_packet + d_offset-4, (const char*)&tmp, sizeof(tmp));
2c73e580 653 }
153d5065
RG
654 void setAndSkip32BitInt(uint32_t value)
655 {
656 moveOffset(4);
657
658 value = htonl(value);
659 memcpy(d_packet + d_offset-4, (const char*)&value, sizeof(value));
660 }
55baa1f2
RG
661 uint32_t getOffset() const
662 {
663 return d_offset;
664 }
2c73e580
BH
665private:
666 void moveOffset(uint16_t by)
667 {
668 d_notyouroffset += by;
886e2cf2 669 if(d_notyouroffset > d_length)
335da0ba 670 throw std::out_of_range("dns packet out of range: "+std::to_string(d_notyouroffset) +" > "
886e2cf2 671 + std::to_string(d_length) );
2c73e580 672 }
153d5065
RG
673 void rewindOffset(uint16_t by)
674 {
675 if(d_notyouroffset < by)
676 throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < "
677 + std::to_string(by));
678 d_notyouroffset -= by;
679 if(d_notyouroffset < 12)
680 throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < "
681 + std::to_string(12));
682 }
886e2cf2
RG
683 char* d_packet;
684 size_t d_length;
2c73e580
BH
685
686 uint32_t d_notyouroffset; // only 'moveOffset' can touch this
687 const uint32_t& d_offset; // look.. but don't touch
688
689};
690
153d5065
RG
691// method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
692void editDNSPacketTTL(char* packet, size_t length, std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)> visitor)
693{
694 if(length < sizeof(dnsheader))
695 return;
696 try
697 {
698 dnsheader dh;
699 memcpy((void*)&dh, (const dnsheader*)packet, sizeof(dh));
700 uint64_t numrecords = ntohs(dh.ancount) + ntohs(dh.nscount) + ntohs(dh.arcount);
701 DNSPacketMangler dpm(packet, length);
702
703 uint64_t n;
704 for(n=0; n < ntohs(dh.qdcount) ; ++n) {
c1780c41 705 dpm.skipDomainName();
153d5065
RG
706 /* type and class */
707 dpm.skipBytes(4);
708 }
709
710 for(n=0; n < numrecords; ++n) {
c1780c41 711 dpm.skipDomainName();
153d5065 712
d7ce446c 713 uint8_t section = n < ntohs(dh.ancount) ? 1 : (n < (ntohs(dh.ancount) + ntohs(dh.nscount)) ? 2 : 3);
153d5065
RG
714 uint16_t dnstype = dpm.get16BitInt();
715 uint16_t dnsclass = dpm.get16BitInt();
716
717 if(dnstype == QType::OPT) // not getting near that one with a stick
718 break;
719
720 uint32_t dnsttl = dpm.get32BitInt();
721 uint32_t newttl = visitor(section, dnsclass, dnstype, dnsttl);
722 if (newttl) {
723 dpm.rewindBytes(sizeof(newttl));
724 dpm.setAndSkip32BitInt(newttl);
725 }
726 dpm.skipRData();
727 }
728 }
729 catch(...)
730 {
731 return;
732 }
733}
734
2c73e580 735// method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
886e2cf2 736void ageDNSPacket(char* packet, size_t length, uint32_t seconds)
2c73e580 737{
886e2cf2 738 if(length < sizeof(dnsheader))
2c73e580
BH
739 return;
740 try
741 {
7def3a4b
RG
742 const dnsheader* dh = reinterpret_cast<const dnsheader*>(packet);
743 const uint64_t dqcount = ntohs(dh->qdcount);
744 const uint64_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
886e2cf2 745 DNSPacketMangler dpm(packet, length);
a683e8bd
RG
746
747 uint64_t n;
7def3a4b 748 for(n=0; n < dqcount; ++n) {
c1780c41 749 dpm.skipDomainName();
1819b930
RG
750 /* type and class */
751 dpm.skipBytes(4);
2c73e580
BH
752 }
753 // cerr<<"Skipped "<<n<<" questions, now parsing "<<numrecords<<" records"<<endl;
754 for(n=0; n < numrecords; ++n) {
c1780c41 755 dpm.skipDomainName();
2c73e580
BH
756
757 uint16_t dnstype = dpm.get16BitInt();
1819b930
RG
758 /* class */
759 dpm.skipBytes(2);
2c73e580
BH
760
761 if(dnstype == QType::OPT) // not aging that one with a stick
232f0877 762 break;
2c73e580
BH
763
764 dpm.decreaseAndSkip32BitInt(seconds);
765 dpm.skipRData();
766 }
767 }
768 catch(...)
769 {
770 return;
771 }
772}
886e2cf2
RG
773
774void ageDNSPacket(std::string& packet, uint32_t seconds)
775{
776 ageDNSPacket((char*)packet.c_str(), packet.length(), seconds);
777}
0766890a 778
47698274 779uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA)
0766890a
RG
780{
781 uint32_t result = std::numeric_limits<uint32_t>::max();
782 if(length < sizeof(dnsheader)) {
783 return result;
784 }
785 try
786 {
787 const dnsheader* dh = (const dnsheader*) packet;
788 DNSPacketMangler dpm(const_cast<char*>(packet), length);
789
790 const uint16_t qdcount = ntohs(dh->qdcount);
791 for(size_t n = 0; n < qdcount; ++n) {
c1780c41 792 dpm.skipDomainName();
1819b930
RG
793 /* type and class */
794 dpm.skipBytes(4);
0766890a
RG
795 }
796 const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
797 for(size_t n = 0; n < numrecords; ++n) {
c1780c41 798 dpm.skipDomainName();
0766890a 799 const uint16_t dnstype = dpm.get16BitInt();
1819b930 800 /* class */
47698274 801 const uint16_t dnsclass = dpm.get16BitInt();
0766890a 802
d7ce446c 803 if(dnstype == QType::OPT) {
0766890a 804 break;
d7ce446c
RG
805 }
806
807 /* report it if we see a SOA record in the AUTHORITY section */
cfd669b4 808 if(dnstype == QType::SOA && dnsclass == QClass::IN && seenAuthSOA != nullptr && n >= ntohs(dh->ancount) && n < (ntohs(dh->ancount) + ntohs(dh->nscount))) {
d7ce446c
RG
809 *seenAuthSOA = true;
810 }
0766890a
RG
811
812 const uint32_t ttl = dpm.get32BitInt();
47698274 813 if (result > ttl) {
0766890a 814 result = ttl;
47698274 815 }
0766890a
RG
816
817 dpm.skipRData();
818 }
819 }
820 catch(...)
821 {
822 }
823 return result;
824}
55baa1f2
RG
825
826uint32_t getDNSPacketLength(const char* packet, size_t length)
827{
828 uint32_t result = length;
829 if(length < sizeof(dnsheader)) {
830 return result;
831 }
832 try
833 {
53c57da7 834 const dnsheader* dh = reinterpret_cast<const dnsheader*>(packet);
55baa1f2
RG
835 DNSPacketMangler dpm(const_cast<char*>(packet), length);
836
837 const uint16_t qdcount = ntohs(dh->qdcount);
838 for(size_t n = 0; n < qdcount; ++n) {
c1780c41 839 dpm.skipDomainName();
1819b930
RG
840 /* type and class */
841 dpm.skipBytes(4);
55baa1f2
RG
842 }
843 const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
844 for(size_t n = 0; n < numrecords; ++n) {
c1780c41 845 dpm.skipDomainName();
1819b930
RG
846 /* type (2), class (2) and ttl (4) */
847 dpm.skipBytes(8);
55baa1f2
RG
848 dpm.skipRData();
849 }
850 result = dpm.getOffset();
851 }
852 catch(...)
853 {
854 }
855 return result;
856}
857
858uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type)
859{
860 uint16_t result = 0;
861 if(length < sizeof(dnsheader)) {
862 return result;
863 }
864 try
865 {
866 const dnsheader* dh = (const dnsheader*) packet;
867 DNSPacketMangler dpm(const_cast<char*>(packet), length);
868
869 const uint16_t qdcount = ntohs(dh->qdcount);
870 for(size_t n = 0; n < qdcount; ++n) {
c1780c41 871 dpm.skipDomainName();
55baa1f2
RG
872 if (section == 0) {
873 uint16_t dnstype = dpm.get16BitInt();
874 if (dnstype == type) {
875 result++;
876 }
1819b930
RG
877 /* class */
878 dpm.skipBytes(2);
55baa1f2 879 } else {
1819b930
RG
880 /* type and class */
881 dpm.skipBytes(4);
55baa1f2
RG
882 }
883 }
884 const uint16_t ancount = ntohs(dh->ancount);
885 for(size_t n = 0; n < ancount; ++n) {
c1780c41 886 dpm.skipDomainName();
55baa1f2
RG
887 if (section == 1) {
888 uint16_t dnstype = dpm.get16BitInt();
889 if (dnstype == type) {
890 result++;
891 }
1819b930
RG
892 /* class */
893 dpm.skipBytes(2);
55baa1f2 894 } else {
1819b930
RG
895 /* type and class */
896 dpm.skipBytes(4);
55baa1f2 897 }
1819b930
RG
898 /* ttl */
899 dpm.skipBytes(4);
55baa1f2
RG
900 dpm.skipRData();
901 }
902 const uint16_t nscount = ntohs(dh->nscount);
903 for(size_t n = 0; n < nscount; ++n) {
c1780c41 904 dpm.skipDomainName();
55baa1f2
RG
905 if (section == 2) {
906 uint16_t dnstype = dpm.get16BitInt();
907 if (dnstype == type) {
908 result++;
909 }
1819b930
RG
910 /* class */
911 dpm.skipBytes(2);
55baa1f2 912 } else {
1819b930
RG
913 /* type and class */
914 dpm.skipBytes(4);
55baa1f2 915 }
1819b930
RG
916 /* ttl */
917 dpm.skipBytes(4);
55baa1f2
RG
918 dpm.skipRData();
919 }
920 const uint16_t arcount = ntohs(dh->arcount);
921 for(size_t n = 0; n < arcount; ++n) {
c1780c41 922 dpm.skipDomainName();
55baa1f2
RG
923 if (section == 3) {
924 uint16_t dnstype = dpm.get16BitInt();
925 if (dnstype == type) {
926 result++;
927 }
1819b930
RG
928 /* class */
929 dpm.skipBytes(2);
55baa1f2 930 } else {
1819b930
RG
931 /* type and class */
932 dpm.skipBytes(4);
55baa1f2 933 }
1819b930
RG
934 /* ttl */
935 dpm.skipBytes(4);
55baa1f2
RG
936 dpm.skipRData();
937 }
938 }
939 catch(...)
940 {
941 }
942 return result;
943}
e7c732b8 944
e0fd37ec 945bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z)
e7c732b8
RG
946{
947 if (length < sizeof(dnsheader)) {
e0fd37ec 948 return false;
e7c732b8
RG
949 }
950
e0fd37ec
RG
951 *payloadSize = 0;
952 *z = 0;
953
e7c732b8
RG
954 try
955 {
956 const dnsheader* dh = (const dnsheader*) packet;
957 DNSPacketMangler dpm(const_cast<char*>(packet), length);
958
959 const uint16_t qdcount = ntohs(dh->qdcount);
960 for(size_t n = 0; n < qdcount; ++n) {
c1780c41 961 dpm.skipDomainName();
e7c732b8
RG
962 /* type and class */
963 dpm.skipBytes(4);
964 }
965 const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
966 for(size_t n = 0; n < numrecords; ++n) {
c1780c41 967 dpm.skipDomainName();
e7c732b8
RG
968 const uint16_t dnstype = dpm.get16BitInt();
969 const uint16_t dnsclass = dpm.get16BitInt();
970
971 if(dnstype == QType::OPT) {
e0fd37ec
RG
972 /* skip extended rcode and version */
973 dpm.skipBytes(2);
974 *z = dpm.get16BitInt();
975 *payloadSize = dnsclass;
976 return true;
e7c732b8
RG
977 }
978
979 /* TTL */
980 dpm.skipBytes(4);
981 dpm.skipRData();
982 }
983 }
984 catch(...)
985 {
986 }
e0fd37ec
RG
987
988 return false;
e7c732b8 989}