]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsparser.cc
Avoid throwing an exception in Logger::log().
[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
ef2ea4bf 86shared_ptr<DNSRecordContent> DNSRecordContent::deserialize(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);
6d02e928
OM
117 if (serialized.size() > 0) {
118 memcpy(&packet[pos], serialized.c_str(), serialized.size());
119 pos += (uint16_t) serialized.size();
120 }
ea634573 121
27c0050c 122 MOADNSParser mdp(false, (char*)&*packet.begin(), (unsigned int)packet.size());
ea634573 123 shared_ptr<DNSRecordContent> ret= mdp.d_answers.begin()->first.d_content;
ea634573
BH
124 return ret;
125}
ff6a1e7b 126
6177a176 127std::shared_ptr<DNSRecordContent> DNSRecordContent::mastermake(const DNSRecord &dr,
232f0877 128 PacketReader& pr)
ff6a1e7b 129{
7f7b8d55
BH
130 uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT
131
132 typemap_t::const_iterator i=getTypemap().find(make_pair(searchclass, dr.d_type));
49a06471 133 if(i==getTypemap().end() || !i->second) {
6177a176 134 return std::make_shared<UnknownRecordContent>(dr, pr);
ff6a1e7b 135 }
945a9ad4 136
32122aab 137 return i->second(dr, pr);
ff6a1e7b
BH
138}
139
6177a176 140std::shared_ptr<DNSRecordContent> DNSRecordContent::mastermake(uint16_t qtype, uint16_t qclass,
232f0877 141 const string& content)
6c0670c3 142{
49a06471
BH
143 zmakermap_t::const_iterator i=getZmakermap().find(make_pair(qclass, qtype));
144 if(i==getZmakermap().end()) {
6177a176 145 return std::make_shared<UnknownRecordContent>(content);
6c0670c3
BH
146 }
147
32122aab 148 return i->second(content);
6c0670c3
BH
149}
150
6177a176 151std::shared_ptr<DNSRecordContent> DNSRecordContent::mastermake(const DNSRecord &dr, PacketReader& pr, uint16_t oc) {
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
160 typemap_t::const_iterator i=getTypemap().find(make_pair(searchclass, dr.d_type));
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
168
49a06471
BH
169DNSRecordContent::typemap_t& DNSRecordContent::getTypemap()
170{
171 static DNSRecordContent::typemap_t typemap;
172 return typemap;
173}
174
108c321e 175DNSRecordContent::n2typemap_t& DNSRecordContent::getN2Typemap()
49a06471 176{
93e4cb97
BH
177 static DNSRecordContent::n2typemap_t n2typemap;
178 return n2typemap;
49a06471
BH
179}
180
108c321e
BH
181DNSRecordContent::t2namemap_t& DNSRecordContent::getT2Namemap()
182{
93e4cb97
BH
183 static DNSRecordContent::t2namemap_t t2namemap;
184 return t2namemap;
108c321e
BH
185}
186
49a06471
BH
187DNSRecordContent::zmakermap_t& DNSRecordContent::getZmakermap()
188{
189 static DNSRecordContent::zmakermap_t zmakermap;
190 return zmakermap;
191}
192
5708a729 193DNSRecord::DNSRecord(const DNSResourceRecord& rr): d_name(rr.qname)
fbe23591 194{
fbe23591 195 d_type = rr.qtype.getCode();
196 d_ttl = rr.ttl;
197 d_class = rr.qclass;
4950b140 198 d_place = DNSResourceRecord::ANSWER;
e24c18b0 199 d_clen = 0;
6177a176 200 d_content = DNSRecordContent::mastermake(d_type, rr.qclass, rr.content);
fbe23591 201}
202
4950b140
CH
203// If you call this and you are not parsing a packet coming from a socket, you are doing it wrong.
204DNSResourceRecord DNSResourceRecord::fromWire(const DNSRecord& d) {
205 DNSResourceRecord rr;
206 rr.qname = d.d_name;
207 rr.qtype = QType(d.d_type);
208 rr.ttl = d.d_ttl;
bb7123d3 209 rr.content = d.d_content->getZoneRepresentation(true);
4950b140
CH
210 rr.auth = false;
211 rr.qclass = d.d_class;
212 return rr;
213}
214
78f56b38 215void MOADNSParser::init(bool query, const std::string& packet)
ff6a1e7b 216{
78f56b38 217 if (packet.size() < sizeof(dnsheader))
ff6a1e7b
BH
218 throw MOADNSException("Packet shorter than minimal header");
219
78f56b38 220 memcpy(&d_header, packet.data(), sizeof(dnsheader));
ff6a1e7b 221
7945d472 222 if(d_header.opcode != Opcode::Query && d_header.opcode != Opcode::Notify && d_header.opcode != Opcode::Update)
335da0ba 223 throw MOADNSException("Can't parse non-query packet with opcode="+ std::to_string(d_header.opcode));
f27e6356 224
ff6a1e7b
BH
225 d_header.qdcount=ntohs(d_header.qdcount);
226 d_header.ancount=ntohs(d_header.ancount);
227 d_header.nscount=ntohs(d_header.nscount);
228 d_header.arcount=ntohs(d_header.arcount);
27c0050c
RG
229
230 if (query && (d_header.qdcount > 1))
231 throw MOADNSException("Query with QD > 1 ("+std::to_string(d_header.qdcount)+")");
ff6a1e7b 232
629eaf49 233 unsigned int n=0;
ff6a1e7b 234
78f56b38 235 PacketReader pr(packet);
7b1469bb 236 bool validPacket=false;
512c8492 237 try {
00bf10d3
BH
238 d_qtype = d_qclass = 0; // sometimes replies come in with no question, don't present garbage then
239
40207e64 240 for(n=0;n < d_header.qdcount; ++n) {
8ad443ea 241 d_qname=pr.getName();
40207e64
BH
242 d_qtype=pr.get16BitInt();
243 d_qclass=pr.get16BitInt();
244 }
245
512c8492
BH
246 struct dnsrecordheader ah;
247 vector<unsigned char> record;
04151caa 248 bool seenTSIG = false;
7b1469bb 249 validPacket=true;
756e82cf 250 d_answers.reserve((unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount));
c4ac5865 251 for(n=0;n < (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount); ++n) {
512c8492
BH
252 DNSRecord dr;
253
254 if(n < d_header.ancount)
e693ff5a 255 dr.d_place=DNSResourceRecord::ANSWER;
512c8492 256 else if(n < d_header.ancount + d_header.nscount)
e693ff5a 257 dr.d_place=DNSResourceRecord::AUTHORITY;
512c8492 258 else
e693ff5a 259 dr.d_place=DNSResourceRecord::ADDITIONAL;
04151caa 260
78f56b38 261 unsigned int recordStartPos=pr.getPosition();
57e5f5f7 262
f809c028 263 DNSName name=pr.getName();
04151caa 264
512c8492
BH
265 pr.getDnsrecordheader(ah);
266 dr.d_ttl=ah.d_ttl;
267 dr.d_type=ah.d_type;
268 dr.d_class=ah.d_class;
04151caa 269
f809c028 270 dr.d_name=name;
512c8492 271 dr.d_clen=ah.d_clen;
7b1469bb 272
c2a1b24d
PL
273 if (query &&
274 !(d_qtype == QType::IXFR && dr.d_place == DNSResourceRecord::AUTHORITY && dr.d_type == QType::SOA) && // IXFR queries have a SOA in their AUTHORITY section
275 (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 276// cerr<<"discarding RR, query is "<<query<<", place is "<<dr.d_place<<", type is "<<dr.d_type<<", class is "<<dr.d_class<<endl;
63fc7705 277 dr.d_content=std::make_shared<UnknownRecordContent>(dr, pr);
27c0050c
RG
278 }
279 else {
280// cerr<<"parsing RR, query is "<<query<<", place is "<<dr.d_place<<", type is "<<dr.d_type<<", class is "<<dr.d_class<<endl;
6177a176 281 dr.d_content=DNSRecordContent::mastermake(dr, pr, d_header.opcode);
27c0050c
RG
282 }
283
18f707fa
RG
284 /* XXX: XPF records should be allowed after TSIG as soon as the actual XPF option code has been assigned:
285 if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG && dr.d_type != QType::XPF)
286 */
287 if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG) {
04151caa
RG
288 /* only XPF records are allowed after a TSIG */
289 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.");
290 }
291
60a1c204 292 if(dr.d_type == QType::TSIG && dr.d_class == QClass::ANY) {
18f707fa 293 if(seenTSIG || dr.d_place != DNSResourceRecord::ADDITIONAL) {
86f1af1c 294 throw MOADNSException("Packet ("+d_qname.toLogString()+"|#"+std::to_string(d_qtype)+") has a TSIG record in an invalid position.");
60a1c204 295 }
04151caa 296 seenTSIG = true;
78f56b38 297 d_tsigPos = recordStartPos;
60a1c204 298 }
c771b712
RG
299
300 d_answers.push_back(make_pair(std::move(dr), pr.getPosition() - sizeof(dnsheader)));
512c8492 301 }
e5986c84 302
04151caa 303#if 0
78f56b38
RG
304 if(pr.getPosition()!=packet.size()) {
305 throw MOADNSException("Packet ("+d_qname+"|#"+std::to_string(d_qtype)+") has trailing garbage ("+ std::to_string(pr.getPosition()) + " < " +
306 std::to_string(packet.size()) + ")");
ff6a1e7b 307 }
04151caa 308#endif
512c8492 309 }
78f56b38 310 catch(const std::out_of_range &re) {
629eaf49
BH
311 if(validPacket && d_header.tc) { // don't sweat it over truncated packets, but do adjust an, ns and arcount
312 if(n < d_header.ancount) {
4957a608 313 d_header.ancount=n; d_header.nscount = d_header.arcount = 0;
629eaf49
BH
314 }
315 else if(n < d_header.ancount + d_header.nscount) {
4957a608 316 d_header.nscount = n - d_header.ancount; d_header.arcount=0;
629eaf49
BH
317 }
318 else {
4957a608 319 d_header.arcount = n - d_header.ancount - d_header.nscount;
629eaf49
BH
320 }
321 }
322 else {
78f56b38 323 throw MOADNSException("Error parsing packet of "+std::to_string(packet.size())+" bytes (rd="+
335da0ba 324 std::to_string(d_header.rd)+
232f0877 325 "), out of bounds: "+string(re.what()));
629eaf49 326 }
ff6a1e7b 327 }
ff6a1e7b
BH
328}
329
10321a98 330
ff6a1e7b
BH
331void PacketReader::getDnsrecordheader(struct dnsrecordheader &ah)
332{
333 unsigned int n;
334 unsigned char *p=reinterpret_cast<unsigned char*>(&ah);
335
336 for(n=0; n < sizeof(dnsrecordheader); ++n)
337 p[n]=d_content.at(d_pos++);
338
339 ah.d_type=ntohs(ah.d_type);
340 ah.d_class=ntohs(ah.d_class);
341 ah.d_clen=ntohs(ah.d_clen);
342 ah.d_ttl=ntohl(ah.d_ttl);
8c1c9170
BH
343
344 d_startrecordpos=d_pos; // needed for getBlob later on
345 d_recordlen=ah.d_clen;
ff6a1e7b
BH
346}
347
348
092f210a 349void PacketReader::copyRecord(vector<unsigned char>& dest, uint16_t len)
ff6a1e7b
BH
350{
351 dest.resize(len);
bff744a8
BH
352 if(!len)
353 return;
354
092f210a 355 for(uint16_t n=0;n<len;++n) {
ff6a1e7b
BH
356 dest.at(n)=d_content.at(d_pos++);
357 }
358}
359
092f210a 360void PacketReader::copyRecord(unsigned char* dest, uint16_t len)
ff6a1e7b
BH
361{
362 if(d_pos + len > d_content.size())
10f4eea8 363 throw std::out_of_range("Attempt to copy outside of packet");
ff6a1e7b 364
bff744a8 365 memcpy(dest, &d_content.at(d_pos), len);
ff6a1e7b
BH
366 d_pos+=len;
367}
368
341930bb
BH
369void PacketReader::xfr48BitInt(uint64_t& ret)
370{
371 ret=0;
78f56b38 372 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 373 ret<<=8;
78f56b38 374 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 375 ret<<=8;
78f56b38 376 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 377 ret<<=8;
78f56b38 378 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 379 ret<<=8;
78f56b38 380 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 381 ret<<=8;
78f56b38 382 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
341930bb 383}
ff6a1e7b 384
092f210a 385uint32_t PacketReader::get32BitInt()
ff6a1e7b 386{
092f210a 387 uint32_t ret=0;
78f56b38 388 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b 389 ret<<=8;
78f56b38 390 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b 391 ret<<=8;
78f56b38 392 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b 393 ret<<=8;
78f56b38 394 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b
BH
395
396 return ret;
397}
398
399
092f210a 400uint16_t PacketReader::get16BitInt()
ff6a1e7b 401{
092f210a 402 uint16_t ret=0;
78f56b38 403 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b 404 ret<<=8;
78f56b38 405 ret+=static_cast<uint8_t>(d_content.at(d_pos++));
ff6a1e7b
BH
406
407 return ret;
408}
409
49a06471 410uint8_t PacketReader::get8BitInt()
ff6a1e7b
BH
411{
412 return d_content.at(d_pos++);
413}
414
8171ab83 415DNSName PacketReader::getName()
ff6a1e7b 416{
d926c0da 417 unsigned int consumed;
c1229531 418 try {
78f56b38 419 DNSName dn((const char*) d_content.data(), d_content.size(), d_pos, true /* uncompress */, 0 /* qtype */, 0 /* qclass */, &consumed, sizeof(dnsheader));
c1229531 420
421 d_pos+=consumed;
8171ab83 422 return dn;
c1229531 423 }
6177a176
RG
424 catch(const std::range_error& re) {
425 throw std::out_of_range(string("dnsname issue: ")+re.what());
426 }
427 catch(...) {
428 throw std::out_of_range("dnsname issue");
429 }
fb38d90b 430 throw PDNSException("PacketReader::getName(): name is empty");
ff6a1e7b
BH
431}
432
ef6a78d5
BH
433static string txtEscape(const string &name)
434{
435 string ret;
5dba9d25 436 char ebuf[5];
ef6a78d5 437
5dba9d25 438 for(string::const_iterator i=name.begin();i!=name.end();++i) {
fa1dc694 439 if((unsigned char) *i >= 127 || (unsigned char) *i < 32) {
5dba9d25
PD
440 snprintf(ebuf, sizeof(ebuf), "\\%03u", (unsigned char)*i);
441 ret += ebuf;
442 }
66b40966 443 else if(*i=='"' || *i=='\\'){
ef6a78d5
BH
444 ret += '\\';
445 ret += *i;
446 }
447 else
448 ret += *i;
5dba9d25 449 }
ef6a78d5
BH
450 return ret;
451}
452
453// exceptions thrown here do not result in logging in the main pdns auth server - just so you know!
84e1142d 454string PacketReader::getText(bool multi, bool lenField)
9d9c52ef
BH
455{
456 string ret;
457 ret.reserve(40);
ef6a78d5
BH
458 while(d_pos < d_startrecordpos + d_recordlen ) {
459 if(!ret.empty()) {
460 ret.append(1,' ');
461 }
84e1142d
PL
462 uint16_t labellen;
463 if(lenField)
78f56b38 464 labellen=static_cast<uint8_t>(d_content.at(d_pos++));
84e1142d
PL
465 else
466 labellen=d_recordlen - (d_pos - d_startrecordpos);
ef6a78d5
BH
467
468 ret.append(1,'"');
950f78df
BH
469 if(labellen) { // no need to do anything for an empty string
470 string val(&d_content.at(d_pos), &d_content.at(d_pos+labellen-1)+1);
471 ret.append(txtEscape(val)); // the end is one beyond the packet
472 }
ef6a78d5
BH
473 ret.append(1,'"');
474 d_pos+=labellen;
475 if(!multi)
476 break;
477 }
9d9c52ef 478
9d9c52ef
BH
479 return ret;
480}
ff6a1e7b 481
948a927f
PL
482string PacketReader::getUnquotedText(bool lenField)
483{
a94e5580 484 uint16_t stop_at;
948a927f 485 if(lenField)
78f56b38 486 stop_at = static_cast<uint8_t>(d_content.at(d_pos)) + d_pos + 1;
948a927f
PL
487 else
488 stop_at = d_recordlen;
489
d7bbbcf4
RG
490 /* think unsigned overflow */
491 if (stop_at < d_pos) {
492 throw std::out_of_range("getUnquotedText out of record range");
493 }
494
948a927f
PL
495 if(stop_at == d_pos)
496 return "";
497
498 d_pos++;
499 string ret(&d_content.at(d_pos), &d_content.at(stop_at));
500 d_pos = stop_at;
501 return ret;
502}
ef6a78d5 503
8c1c9170 504void PacketReader::xfrBlob(string& blob)
2bc83940 505try
8c1c9170 506{
b4d358e7
RG
507 if(d_recordlen && !(d_pos == (d_startrecordpos + d_recordlen))) {
508 if (d_pos > (d_startrecordpos + d_recordlen)) {
509 throw std::out_of_range("xfrBlob out of record range");
510 }
7f7b8d55 511 blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1);
b4d358e7
RG
512 }
513 else {
7f7b8d55 514 blob.clear();
b4d358e7 515 }
8c1c9170
BH
516
517 d_pos = d_startrecordpos + d_recordlen;
518}
2bc83940 519catch(...)
520{
521 throw std::out_of_range("xfrBlob out of range");
522}
59a0f653 523
2fe9d6f7
AT
524void PacketReader::xfrBlobNoSpaces(string& blob, int length) {
525 xfrBlob(blob, length);
526}
527
06ffdc52
BH
528void PacketReader::xfrBlob(string& blob, int length)
529{
2617464d 530 if(length) {
b4d358e7
RG
531 if (length < 0) {
532 throw std::out_of_range("xfrBlob out of range (negative length)");
533 }
534
0407751c 535 blob.assign(&d_content.at(d_pos), &d_content.at(d_pos + length - 1 ) + 1 );
b4d358e7 536
2617464d
BH
537 d_pos += length;
538 }
b4d358e7 539 else {
2617464d 540 blob.clear();
b4d358e7 541 }
06ffdc52
BH
542}
543
544
e4090157 545void PacketReader::xfrHexBlob(string& blob, bool keepReading)
59a0f653
BH
546{
547 xfrBlob(blob);
548}
b8e0f341 549
3343ad1f 550//FIXME400 remove this method completely
27ff60a3 551string simpleCompress(const string& elabel, const string& root)
b8e0f341 552{
27ff60a3 553 string label=elabel;
3343ad1f 554 // FIXME400: this relies on the semi-canonical escaped output from getName
83b746fd 555 if(strchr(label.c_str(), '\\')) {
556 boost::replace_all(label, "\\.", ".");
557 boost::replace_all(label, "\\032", " ");
558 boost::replace_all(label, "\\\\", "\\");
559 }
b8e0f341
BH
560 typedef vector<pair<unsigned int, unsigned int> > parts_t;
561 parts_t parts;
562 vstringtok(parts, label, ".");
563 string ret;
564 ret.reserve(label.size()+4);
565 for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) {
05a38bfa 566 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
567 const unsigned char rootptr[2]={0xc0,0x11};
568 ret.append((const char *) rootptr, 2);
7127879f
BH
569 return ret;
570 }
b8e0f341
BH
571 ret.append(1, (char)(i->second - i->first));
572 ret.append(label.c_str() + i->first, i->second - i->first);
573 }
574 ret.append(1, (char)0);
575 return ret;
576}
577
153d5065
RG
578// method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
579void editDNSPacketTTL(char* packet, size_t length, std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)> visitor)
580{
581 if(length < sizeof(dnsheader))
582 return;
583 try
584 {
585 dnsheader dh;
586 memcpy((void*)&dh, (const dnsheader*)packet, sizeof(dh));
587 uint64_t numrecords = ntohs(dh.ancount) + ntohs(dh.nscount) + ntohs(dh.arcount);
588 DNSPacketMangler dpm(packet, length);
589
590 uint64_t n;
591 for(n=0; n < ntohs(dh.qdcount) ; ++n) {
c1780c41 592 dpm.skipDomainName();
153d5065
RG
593 /* type and class */
594 dpm.skipBytes(4);
595 }
596
597 for(n=0; n < numrecords; ++n) {
c1780c41 598 dpm.skipDomainName();
153d5065 599
d7ce446c 600 uint8_t section = n < ntohs(dh.ancount) ? 1 : (n < (ntohs(dh.ancount) + ntohs(dh.nscount)) ? 2 : 3);
153d5065
RG
601 uint16_t dnstype = dpm.get16BitInt();
602 uint16_t dnsclass = dpm.get16BitInt();
603
604 if(dnstype == QType::OPT) // not getting near that one with a stick
605 break;
606
607 uint32_t dnsttl = dpm.get32BitInt();
608 uint32_t newttl = visitor(section, dnsclass, dnstype, dnsttl);
609 if (newttl) {
610 dpm.rewindBytes(sizeof(newttl));
611 dpm.setAndSkip32BitInt(newttl);
612 }
613 dpm.skipRData();
614 }
615 }
616 catch(...)
617 {
618 return;
619 }
620}
621
2c73e580 622// method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
886e2cf2 623void ageDNSPacket(char* packet, size_t length, uint32_t seconds)
2c73e580 624{
886e2cf2 625 if(length < sizeof(dnsheader))
2c73e580
BH
626 return;
627 try
628 {
7def3a4b
RG
629 const dnsheader* dh = reinterpret_cast<const dnsheader*>(packet);
630 const uint64_t dqcount = ntohs(dh->qdcount);
631 const uint64_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
886e2cf2 632 DNSPacketMangler dpm(packet, length);
a683e8bd
RG
633
634 uint64_t n;
7def3a4b 635 for(n=0; n < dqcount; ++n) {
c1780c41 636 dpm.skipDomainName();
1819b930
RG
637 /* type and class */
638 dpm.skipBytes(4);
2c73e580
BH
639 }
640 // cerr<<"Skipped "<<n<<" questions, now parsing "<<numrecords<<" records"<<endl;
641 for(n=0; n < numrecords; ++n) {
c1780c41 642 dpm.skipDomainName();
2c73e580
BH
643
644 uint16_t dnstype = dpm.get16BitInt();
1819b930
RG
645 /* class */
646 dpm.skipBytes(2);
2c73e580
BH
647
648 if(dnstype == QType::OPT) // not aging that one with a stick
232f0877 649 break;
2c73e580
BH
650
651 dpm.decreaseAndSkip32BitInt(seconds);
652 dpm.skipRData();
653 }
654 }
655 catch(...)
656 {
657 return;
658 }
659}
886e2cf2
RG
660
661void ageDNSPacket(std::string& packet, uint32_t seconds)
662{
663 ageDNSPacket((char*)packet.c_str(), packet.length(), seconds);
664}
0766890a 665
47698274 666uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA)
0766890a
RG
667{
668 uint32_t result = std::numeric_limits<uint32_t>::max();
669 if(length < sizeof(dnsheader)) {
670 return result;
671 }
672 try
673 {
674 const dnsheader* dh = (const dnsheader*) packet;
675 DNSPacketMangler dpm(const_cast<char*>(packet), length);
676
677 const uint16_t qdcount = ntohs(dh->qdcount);
678 for(size_t n = 0; n < qdcount; ++n) {
c1780c41 679 dpm.skipDomainName();
1819b930
RG
680 /* type and class */
681 dpm.skipBytes(4);
0766890a
RG
682 }
683 const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
684 for(size_t n = 0; n < numrecords; ++n) {
c1780c41 685 dpm.skipDomainName();
0766890a 686 const uint16_t dnstype = dpm.get16BitInt();
1819b930 687 /* class */
47698274 688 const uint16_t dnsclass = dpm.get16BitInt();
0766890a 689
d7ce446c 690 if(dnstype == QType::OPT) {
0766890a 691 break;
d7ce446c
RG
692 }
693
694 /* report it if we see a SOA record in the AUTHORITY section */
cfd669b4 695 if(dnstype == QType::SOA && dnsclass == QClass::IN && seenAuthSOA != nullptr && n >= ntohs(dh->ancount) && n < (ntohs(dh->ancount) + ntohs(dh->nscount))) {
d7ce446c
RG
696 *seenAuthSOA = true;
697 }
0766890a
RG
698
699 const uint32_t ttl = dpm.get32BitInt();
47698274 700 if (result > ttl) {
0766890a 701 result = ttl;
47698274 702 }
0766890a
RG
703
704 dpm.skipRData();
705 }
706 }
707 catch(...)
708 {
709 }
710 return result;
711}
55baa1f2
RG
712
713uint32_t getDNSPacketLength(const char* packet, size_t length)
714{
715 uint32_t result = length;
716 if(length < sizeof(dnsheader)) {
717 return result;
718 }
719 try
720 {
53c57da7 721 const dnsheader* dh = reinterpret_cast<const dnsheader*>(packet);
55baa1f2
RG
722 DNSPacketMangler dpm(const_cast<char*>(packet), length);
723
724 const uint16_t qdcount = ntohs(dh->qdcount);
725 for(size_t n = 0; n < qdcount; ++n) {
c1780c41 726 dpm.skipDomainName();
1819b930
RG
727 /* type and class */
728 dpm.skipBytes(4);
55baa1f2
RG
729 }
730 const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
731 for(size_t n = 0; n < numrecords; ++n) {
c1780c41 732 dpm.skipDomainName();
1819b930
RG
733 /* type (2), class (2) and ttl (4) */
734 dpm.skipBytes(8);
55baa1f2
RG
735 dpm.skipRData();
736 }
737 result = dpm.getOffset();
738 }
739 catch(...)
740 {
741 }
742 return result;
743}
744
745uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type)
746{
747 uint16_t result = 0;
748 if(length < sizeof(dnsheader)) {
749 return result;
750 }
751 try
752 {
753 const dnsheader* dh = (const dnsheader*) packet;
754 DNSPacketMangler dpm(const_cast<char*>(packet), length);
755
756 const uint16_t qdcount = ntohs(dh->qdcount);
757 for(size_t n = 0; n < qdcount; ++n) {
c1780c41 758 dpm.skipDomainName();
55baa1f2
RG
759 if (section == 0) {
760 uint16_t dnstype = dpm.get16BitInt();
761 if (dnstype == type) {
762 result++;
763 }
1819b930
RG
764 /* class */
765 dpm.skipBytes(2);
55baa1f2 766 } else {
1819b930
RG
767 /* type and class */
768 dpm.skipBytes(4);
55baa1f2
RG
769 }
770 }
771 const uint16_t ancount = ntohs(dh->ancount);
772 for(size_t n = 0; n < ancount; ++n) {
c1780c41 773 dpm.skipDomainName();
55baa1f2
RG
774 if (section == 1) {
775 uint16_t dnstype = dpm.get16BitInt();
776 if (dnstype == type) {
777 result++;
778 }
1819b930
RG
779 /* class */
780 dpm.skipBytes(2);
55baa1f2 781 } else {
1819b930
RG
782 /* type and class */
783 dpm.skipBytes(4);
55baa1f2 784 }
1819b930
RG
785 /* ttl */
786 dpm.skipBytes(4);
55baa1f2
RG
787 dpm.skipRData();
788 }
789 const uint16_t nscount = ntohs(dh->nscount);
790 for(size_t n = 0; n < nscount; ++n) {
c1780c41 791 dpm.skipDomainName();
55baa1f2
RG
792 if (section == 2) {
793 uint16_t dnstype = dpm.get16BitInt();
794 if (dnstype == type) {
795 result++;
796 }
1819b930
RG
797 /* class */
798 dpm.skipBytes(2);
55baa1f2 799 } else {
1819b930
RG
800 /* type and class */
801 dpm.skipBytes(4);
55baa1f2 802 }
1819b930
RG
803 /* ttl */
804 dpm.skipBytes(4);
55baa1f2
RG
805 dpm.skipRData();
806 }
807 const uint16_t arcount = ntohs(dh->arcount);
808 for(size_t n = 0; n < arcount; ++n) {
c1780c41 809 dpm.skipDomainName();
55baa1f2
RG
810 if (section == 3) {
811 uint16_t dnstype = dpm.get16BitInt();
812 if (dnstype == type) {
813 result++;
814 }
1819b930
RG
815 /* class */
816 dpm.skipBytes(2);
55baa1f2 817 } else {
1819b930
RG
818 /* type and class */
819 dpm.skipBytes(4);
55baa1f2 820 }
1819b930
RG
821 /* ttl */
822 dpm.skipBytes(4);
55baa1f2
RG
823 dpm.skipRData();
824 }
825 }
826 catch(...)
827 {
828 }
829 return result;
830}
e7c732b8 831
e0fd37ec 832bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z)
e7c732b8
RG
833{
834 if (length < sizeof(dnsheader)) {
e0fd37ec 835 return false;
e7c732b8
RG
836 }
837
e0fd37ec
RG
838 *payloadSize = 0;
839 *z = 0;
840
e7c732b8
RG
841 try
842 {
843 const dnsheader* dh = (const dnsheader*) packet;
844 DNSPacketMangler dpm(const_cast<char*>(packet), length);
845
846 const uint16_t qdcount = ntohs(dh->qdcount);
847 for(size_t n = 0; n < qdcount; ++n) {
c1780c41 848 dpm.skipDomainName();
e7c732b8
RG
849 /* type and class */
850 dpm.skipBytes(4);
851 }
852 const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount);
853 for(size_t n = 0; n < numrecords; ++n) {
c1780c41 854 dpm.skipDomainName();
e7c732b8
RG
855 const uint16_t dnstype = dpm.get16BitInt();
856 const uint16_t dnsclass = dpm.get16BitInt();
857
858 if(dnstype == QType::OPT) {
e0fd37ec
RG
859 /* skip extended rcode and version */
860 dpm.skipBytes(2);
861 *z = dpm.get16BitInt();
862 *payloadSize = dnsclass;
863 return true;
e7c732b8
RG
864 }
865
866 /* TTL */
867 dpm.skipBytes(4);
868 dpm.skipRData();
869 }
870 }
871 catch(...)
872 {
873 }
e0fd37ec
RG
874
875 return false;
e7c732b8 876}