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