]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsparser.cc
our houseKeeping mthread could (and would) frequently be run several times simultaneo...
[thirdparty/pdns.git] / pdns / dnsparser.cc
CommitLineData
4192ca66
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
93e4cb97 3 Copyright (C) 2005 - 2011 PowerDNS.COM BV
4192ca66
BH
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation
8
f782fe38
MH
9 Additionally, the license of this program contains a special
10 exception which allows to distribute the program in binary form when
11 it is linked against OpenSSL.
12
4192ca66
BH
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
06bd9ccf 20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
4192ca66
BH
21*/
22
ff6a1e7b 23#include "dnsparser.hh"
6c0670c3 24#include "dnswriter.hh"
ff6a1e7b 25#include <boost/lexical_cast.hpp>
4cf74e6f 26#include <boost/algorithm/string.hpp>
5dba9d25 27#include <boost/format.hpp>
ff6a1e7b 28
61b26744 29#include "namespaces.hh"
ff6a1e7b
BH
30
31class UnknownRecordContent : public DNSRecordContent
32{
33public:
7fc69fd0 34 UnknownRecordContent(const DNSRecord& dr, PacketReader& pr)
ea634573 35 : DNSRecordContent(dr.d_type), d_dr(dr)
ff6a1e7b 36 {
7fc69fd0 37 pr.copyRecord(d_record, dr.d_clen);
ff6a1e7b
BH
38 }
39
ea634573 40 UnknownRecordContent(const string& zone) : DNSRecordContent(0)
6c0670c3
BH
41 {
42 d_record.insert(d_record.end(), zone.begin(), zone.end());
43 }
44
ff6a1e7b
BH
45 string getZoneRepresentation() const
46 {
47 ostringstream str;
705f31ae 48 str<<"\\# "<<(unsigned int)d_record.size()<<" ";
ff6a1e7b
BH
49 char hex[4];
50 for(size_t n=0; n<d_record.size(); ++n) {
51 snprintf(hex,sizeof(hex)-1, "%02x", d_record.at(n));
52 str << hex;
53 }
ff6a1e7b
BH
54 return str.str();
55 }
56
6c0670c3
BH
57 void toPacket(DNSPacketWriter& pw)
58 {
d0b471d3 59 string tmp((char*)&*d_record.begin(), d_record.size());
6c0670c3
BH
60 vector<string> parts;
61 stringtok(parts, tmp);
4cf74e6f 62 if(parts.size()!=3 && !(parts.size()==2 && equals(parts[1],"0")) )
a9af3782 63 throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got "+lexical_cast<string>(parts.size())+": "+tmp );
4cf74e6f 64 const string& relevant=(parts.size() > 2) ? parts[2] : "";
6c0670c3
BH
65 unsigned int total=atoi(parts[1].c_str());
66 if(relevant.size()!=2*total)
70d5a663 67 throw MOADNSException((boost::format("invalid unknown record length for label %s: size not equal to length field (%d != %d)") % d_dr.d_label.c_str() % relevant.size() % (2*total)).str());
6c0670c3 68 string out;
cc835f89 69 out.reserve(total+1);
6c0670c3
BH
70 for(unsigned int n=0; n < total; ++n) {
71 int c;
72 sscanf(relevant.c_str()+2*n, "%02x", &c);
73 out.append(1, (char)c);
74 }
75 pw.xfrBlob(out);
76 }
ff6a1e7b 77private:
6c0670c3
BH
78 DNSRecord d_dr;
79 vector<uint8_t> d_record;
ff6a1e7b
BH
80};
81
9bd9588f 82static const string EncodeDNSLabel(const string& input)
ea634573 83{
9bd9588f
BH
84 if(input.length() == 1 && input[0]=='.') // otherwise we encode .. (long story)
85 return string (1, 0);
86
213f6de6
BH
87 labelparts_t parts;
88 bool unescapedSomething = labeltokUnescape(parts, input);
89 string ret;
9bd9588f 90
213f6de6
BH
91 if(!unescapedSomething) {
92 for(labelparts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) {
9bd9588f
BH
93 ret.append(1, i->second - i->first);
94 ret.append(input.c_str() + i->first, i->second - i->first);
213f6de6
BH
95 }
96
97 } else {
98 for(labelparts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) {
9bd9588f
BH
99 string part(input.c_str() + i->first, i->second - i->first);
100 boost::replace_all(part, "\\\\", "\\");
101 boost::replace_all(part, "\\.", ".");
213f6de6
BH
102
103 ret.append(1, part.length());
104 ret.append(part);
105 }
106 }
107 ret.append(1, 0);
9bd9588f
BH
108 return ret;
109}
ff6a1e7b 110
213f6de6 111
ea634573
BH
112shared_ptr<DNSRecordContent> DNSRecordContent::unserialize(const string& qname, uint16_t qtype, const string& serialized)
113{
114 dnsheader dnsheader;
115 memset(&dnsheader, 0, sizeof(dnsheader));
116 dnsheader.qdcount=htons(1);
117 dnsheader.ancount=htons(1);
118
119 vector<uint8_t> packet; // build pseudo packet
678ce973
BH
120
121 /* will look like: dnsheader, 5 bytes, encoded qname, dns record header, serialized data */
ea634573
BH
122
123 string encoded=EncodeDNSLabel(qname);
678ce973
BH
124
125 packet.resize(sizeof(dnsheader) + 5 + encoded.size() + sizeof(struct dnsrecordheader) + serialized.size());
126
127 uint16_t pos=0;
128
129 memcpy(&packet[0], &dnsheader, sizeof(dnsheader)); pos+=sizeof(dnsheader);
130
131 char tmp[6]="\x0" "\x0\x1" "\x0\x1"; // root question for ns_t_a
132 memcpy(&packet[pos], &tmp, 5); pos+=5;
133
705f31ae 134 memcpy(&packet[pos], encoded.c_str(), encoded.size()); pos+=(uint16_t)encoded.size();
ea634573
BH
135
136 struct dnsrecordheader drh;
137 drh.d_type=htons(qtype);
138 drh.d_class=htons(1);
139 drh.d_ttl=0;
140 drh.d_clen=htons(serialized.size());
141
678ce973 142 memcpy(&packet[pos], &drh, sizeof(drh)); pos+=sizeof(drh);
705f31ae 143 memcpy(&packet[pos], serialized.c_str(), serialized.size()); pos+=(uint16_t)serialized.size();
ea634573 144
705f31ae 145 MOADNSParser mdp((char*)&*packet.begin(), (unsigned int)packet.size());
ea634573
BH
146 shared_ptr<DNSRecordContent> ret= mdp.d_answers.begin()->first.d_content;
147 ret->header.d_type=ret->d_qtype;
148 ret->label=mdp.d_answers.begin()->first.d_label;
149 ret->header.d_ttl=mdp.d_answers.begin()->first.d_ttl;
150 return ret;
151}
ff6a1e7b 152
7fc69fd0 153DNSRecordContent* DNSRecordContent::mastermake(const DNSRecord &dr,
232f0877 154 PacketReader& pr)
ff6a1e7b 155{
7f7b8d55
BH
156 uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT
157
158 typemap_t::const_iterator i=getTypemap().find(make_pair(searchclass, dr.d_type));
49a06471 159 if(i==getTypemap().end() || !i->second) {
7fc69fd0 160 return new UnknownRecordContent(dr, pr);
ff6a1e7b 161 }
945a9ad4 162
7fc69fd0 163 return i->second(dr, pr);
ff6a1e7b
BH
164}
165
6c0670c3 166DNSRecordContent* DNSRecordContent::mastermake(uint16_t qtype, uint16_t qclass,
232f0877 167 const string& content)
6c0670c3 168{
49a06471
BH
169 zmakermap_t::const_iterator i=getZmakermap().find(make_pair(qclass, qtype));
170 if(i==getZmakermap().end()) {
6c0670c3
BH
171 return new UnknownRecordContent(content);
172 }
173
174 return i->second(content);
175}
176
7945d472
RA
177DNSRecordContent* DNSRecordContent::mastermake(const DNSRecord &dr, PacketReader& pr, uint16_t oc) {
178 // For opcode UPDATE and where the DNSRecord is an answer record, we don't care about content, because this is
179 // not used within the prerequisite section of RFC2136, so - we can simply use unknownrecordcontent.
180 // For section 3.2.3, we do need content so we need to get it properly. But only for the correct Qclasses.
181 if (oc == Opcode::Update && dr.d_place == DNSRecord::Answer && dr.d_class != 1)
182 return new UnknownRecordContent(dr, pr);
914353ca 183
7945d472
RA
184 uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT
185
186 typemap_t::const_iterator i=getTypemap().find(make_pair(searchclass, dr.d_type));
187 if(i==getTypemap().end() || !i->second) {
188 return new UnknownRecordContent(dr, pr);
189 }
190
191 return i->second(dr, pr);
192}
193
194
49a06471
BH
195DNSRecordContent::typemap_t& DNSRecordContent::getTypemap()
196{
197 static DNSRecordContent::typemap_t typemap;
198 return typemap;
199}
200
108c321e 201DNSRecordContent::n2typemap_t& DNSRecordContent::getN2Typemap()
49a06471 202{
93e4cb97
BH
203 static DNSRecordContent::n2typemap_t n2typemap;
204 return n2typemap;
49a06471
BH
205}
206
108c321e
BH
207DNSRecordContent::t2namemap_t& DNSRecordContent::getT2Namemap()
208{
93e4cb97
BH
209 static DNSRecordContent::t2namemap_t t2namemap;
210 return t2namemap;
108c321e
BH
211}
212
213
49a06471
BH
214DNSRecordContent::zmakermap_t& DNSRecordContent::getZmakermap()
215{
216 static DNSRecordContent::zmakermap_t zmakermap;
217 return zmakermap;
218}
219
ff6a1e7b
BH
220void MOADNSParser::init(const char *packet, unsigned int len)
221{
222 if(len < sizeof(dnsheader))
223 throw MOADNSException("Packet shorter than minimal header");
224
225 memcpy(&d_header, packet, sizeof(dnsheader));
226
7945d472 227 if(d_header.opcode != Opcode::Query && d_header.opcode != Opcode::Notify && d_header.opcode != Opcode::Update)
f27e6356
BH
228 throw MOADNSException("Can't parse non-query packet with opcode="+ lexical_cast<string>(d_header.opcode));
229
ff6a1e7b
BH
230 d_header.qdcount=ntohs(d_header.qdcount);
231 d_header.ancount=ntohs(d_header.ancount);
232 d_header.nscount=ntohs(d_header.nscount);
233 d_header.arcount=ntohs(d_header.arcount);
234
092f210a 235 uint16_t contentlen=len-sizeof(dnsheader);
ff6a1e7b
BH
236
237 d_content.resize(contentlen);
238 copy(packet+sizeof(dnsheader), packet+len, d_content.begin());
239
629eaf49 240 unsigned int n=0;
ff6a1e7b
BH
241
242 PacketReader pr(d_content);
7b1469bb 243 bool validPacket=false;
512c8492 244 try {
00bf10d3
BH
245 d_qtype = d_qclass = 0; // sometimes replies come in with no question, don't present garbage then
246
40207e64
BH
247 for(n=0;n < d_header.qdcount; ++n) {
248 d_qname=pr.getLabel();
249 d_qtype=pr.get16BitInt();
250 d_qclass=pr.get16BitInt();
251 }
252
512c8492
BH
253 struct dnsrecordheader ah;
254 vector<unsigned char> record;
7b1469bb 255 validPacket=true;
c4ac5865 256 for(n=0;n < (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount); ++n) {
512c8492
BH
257 DNSRecord dr;
258
259 if(n < d_header.ancount)
4957a608 260 dr.d_place=DNSRecord::Answer;
512c8492 261 else if(n < d_header.ancount + d_header.nscount)
4957a608 262 dr.d_place=DNSRecord::Nameserver;
512c8492 263 else
4957a608 264 dr.d_place=DNSRecord::Additional;
512c8492 265
57e5f5f7
BH
266 unsigned int recordStartPos=pr.d_pos;
267
512c8492
BH
268 string label=pr.getLabel();
269
270 pr.getDnsrecordheader(ah);
271 dr.d_ttl=ah.d_ttl;
272 dr.d_type=ah.d_type;
273 dr.d_class=ah.d_class;
274
275 dr.d_label=label;
276 dr.d_clen=ah.d_clen;
7b1469bb 277
7945d472 278 dr.d_content=boost::shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(dr, pr, d_header.opcode));
bc9b5203 279 d_answers.push_back(make_pair(dr, pr.d_pos));
57e5f5f7
BH
280
281 if(dr.d_type == QType::TSIG && dr.d_class == 0xff)
4957a608 282 d_tsigPos = recordStartPos + sizeof(struct dnsheader);
512c8492 283 }
e5986c84
BH
284
285#if 0
512c8492 286 if(pr.d_pos!=contentlen) {
29a14b24 287 throw MOADNSException("Packet ("+d_qname+"|#"+lexical_cast<string>(d_qtype)+") has trailing garbage ("+ lexical_cast<string>(pr.d_pos) + " < " +
232f0877 288 lexical_cast<string>(contentlen) + ")");
ff6a1e7b 289 }
e5986c84 290#endif
512c8492 291 }
10f4eea8 292 catch(std::out_of_range &re) {
629eaf49
BH
293 if(validPacket && d_header.tc) { // don't sweat it over truncated packets, but do adjust an, ns and arcount
294 if(n < d_header.ancount) {
4957a608 295 d_header.ancount=n; d_header.nscount = d_header.arcount = 0;
629eaf49
BH
296 }
297 else if(n < d_header.ancount + d_header.nscount) {
4957a608 298 d_header.nscount = n - d_header.ancount; d_header.arcount=0;
629eaf49
BH
299 }
300 else {
4957a608 301 d_header.arcount = n - d_header.ancount - d_header.nscount;
629eaf49
BH
302 }
303 }
304 else {
7b1469bb 305 throw MOADNSException("Error parsing packet of "+lexical_cast<string>(len)+" bytes (rd="+
232f0877
CH
306 lexical_cast<string>(d_header.rd)+
307 "), out of bounds: "+string(re.what()));
629eaf49 308 }
ff6a1e7b 309 }
ff6a1e7b
BH
310}
311
10321a98 312
ff6a1e7b
BH
313void PacketReader::getDnsrecordheader(struct dnsrecordheader &ah)
314{
315 unsigned int n;
316 unsigned char *p=reinterpret_cast<unsigned char*>(&ah);
317
318 for(n=0; n < sizeof(dnsrecordheader); ++n)
319 p[n]=d_content.at(d_pos++);
320
321 ah.d_type=ntohs(ah.d_type);
322 ah.d_class=ntohs(ah.d_class);
323 ah.d_clen=ntohs(ah.d_clen);
324 ah.d_ttl=ntohl(ah.d_ttl);
8c1c9170
BH
325
326 d_startrecordpos=d_pos; // needed for getBlob later on
327 d_recordlen=ah.d_clen;
ff6a1e7b
BH
328}
329
330
092f210a 331void PacketReader::copyRecord(vector<unsigned char>& dest, uint16_t len)
ff6a1e7b
BH
332{
333 dest.resize(len);
bff744a8
BH
334 if(!len)
335 return;
336
092f210a 337 for(uint16_t n=0;n<len;++n) {
ff6a1e7b
BH
338 dest.at(n)=d_content.at(d_pos++);
339 }
340}
341
092f210a 342void PacketReader::copyRecord(unsigned char* dest, uint16_t len)
ff6a1e7b
BH
343{
344 if(d_pos + len > d_content.size())
10f4eea8 345 throw std::out_of_range("Attempt to copy outside of packet");
ff6a1e7b 346
bff744a8 347 memcpy(dest, &d_content.at(d_pos), len);
ff6a1e7b
BH
348 d_pos+=len;
349}
350
341930bb
BH
351void PacketReader::xfr48BitInt(uint64_t& ret)
352{
353 ret=0;
354 ret+=d_content.at(d_pos++);
355 ret<<=8;
356 ret+=d_content.at(d_pos++);
357 ret<<=8;
358 ret+=d_content.at(d_pos++);
359 ret<<=8;
360 ret+=d_content.at(d_pos++);
361 ret<<=8;
362 ret+=d_content.at(d_pos++);
363 ret<<=8;
364 ret+=d_content.at(d_pos++);
365}
ff6a1e7b 366
092f210a 367uint32_t PacketReader::get32BitInt()
ff6a1e7b 368{
092f210a 369 uint32_t ret=0;
ff6a1e7b
BH
370 ret+=d_content.at(d_pos++);
371 ret<<=8;
372 ret+=d_content.at(d_pos++);
373 ret<<=8;
374 ret+=d_content.at(d_pos++);
375 ret<<=8;
376 ret+=d_content.at(d_pos++);
377
378 return ret;
379}
380
381
092f210a 382uint16_t PacketReader::get16BitInt()
ff6a1e7b
BH
383{
384 return get16BitInt(d_content, d_pos);
385}
386
092f210a 387uint16_t PacketReader::get16BitInt(const vector<unsigned char>&content, uint16_t& pos)
ff6a1e7b 388{
092f210a 389 uint16_t ret=0;
ff6a1e7b
BH
390 ret+=content.at(pos++);
391 ret<<=8;
392 ret+=content.at(pos++);
393
394 return ret;
395}
396
49a06471 397uint8_t PacketReader::get8BitInt()
ff6a1e7b
BH
398{
399 return d_content.at(d_pos++);
400}
401
ff6a1e7b
BH
402string PacketReader::getLabel(unsigned int recurs)
403{
1ab67463
BH
404 string ret;
405 ret.reserve(40);
406 getLabelFromContent(d_content, d_pos, ret, recurs++);
407 return ret;
ff6a1e7b
BH
408}
409
ef6a78d5
BH
410static string txtEscape(const string &name)
411{
412 string ret;
5dba9d25 413 char ebuf[5];
ef6a78d5 414
5dba9d25 415 for(string::const_iterator i=name.begin();i!=name.end();++i) {
837f4b49 416 if((unsigned char) *i > 127 || (unsigned char) *i < 32) {
5dba9d25
PD
417 snprintf(ebuf, sizeof(ebuf), "\\%03u", (unsigned char)*i);
418 ret += ebuf;
419 }
66b40966 420 else if(*i=='"' || *i=='\\'){
ef6a78d5
BH
421 ret += '\\';
422 ret += *i;
423 }
424 else
425 ret += *i;
5dba9d25 426 }
ef6a78d5
BH
427 return ret;
428}
429
430// exceptions thrown here do not result in logging in the main pdns auth server - just so you know!
431string PacketReader::getText(bool multi)
9d9c52ef
BH
432{
433 string ret;
434 ret.reserve(40);
ef6a78d5
BH
435 while(d_pos < d_startrecordpos + d_recordlen ) {
436 if(!ret.empty()) {
437 ret.append(1,' ');
438 }
439 unsigned char labellen=d_content.at(d_pos++);
440
441 ret.append(1,'"');
950f78df
BH
442 if(labellen) { // no need to do anything for an empty string
443 string val(&d_content.at(d_pos), &d_content.at(d_pos+labellen-1)+1);
444 ret.append(txtEscape(val)); // the end is one beyond the packet
445 }
ef6a78d5
BH
446 ret.append(1,'"');
447 d_pos+=labellen;
448 if(!multi)
449 break;
450 }
9d9c52ef 451
9d9c52ef
BH
452 return ret;
453}
ff6a1e7b 454
ef6a78d5 455
49a06471 456void PacketReader::getLabelFromContent(const vector<uint8_t>& content, uint16_t& frompos, string& ret, int recurs)
ff6a1e7b 457{
7c923cc0 458 if(recurs > 1000) // the forward reference-check below should make this test 100% obsolete
ff6a1e7b 459 throw MOADNSException("Loop");
8682c32b 460 // it is tempting to call reserve on ret, but it turns out it creates a malloc/free storm in the loop
ff6a1e7b
BH
461 for(;;) {
462 unsigned char labellen=content.at(frompos++);
1ab67463 463
ff6a1e7b 464 if(!labellen) {
7738a23f 465 if(ret.empty())
4957a608 466 ret.append(1,'.');
ff6a1e7b
BH
467 break;
468 }
0ffd51d5 469 else if((labellen & 0xc0) == 0xc0) {
092f210a 470 uint16_t offset=256*(labellen & ~0xc0) + (unsigned int)content.at(frompos++) - sizeof(dnsheader);
4957a608 471 // cout<<"This is an offset, need to go to: "<<offset<<endl;
7c923cc0
PD
472
473 if(offset >= frompos-2)
474 throw MOADNSException("forward reference during label decompression");
1ab67463 475 return getLabelFromContent(content, offset, ret, ++recurs);
ff6a1e7b 476 }
0ffd51d5 477 else if(labellen > 63)
478 throw MOADNSException("Overly long label during label decompression ("+lexical_cast<string>((unsigned int)labellen)+")");
ff6a1e7b 479 else {
38e655b6 480 // XXX FIXME THIS MIGHT BE VERY SLOW!
8682c32b 481
38e655b6 482 for(string::size_type n = 0 ; n < labellen; ++n, frompos++) {
7ecd3576 483 if(content.at(frompos)=='.' || content.at(frompos)=='\\') {
4957a608 484 ret.append(1, '\\');
7ecd3576
BH
485 ret.append(1, content[frompos]);
486 }
487 else if(content.at(frompos)==' ') {
488 ret+="\\032";
489 }
490 else
491 ret.append(1, content[frompos]);
38e655b6 492 }
7738a23f 493 ret.append(1,'.');
ff6a1e7b
BH
494 }
495 }
ff6a1e7b 496}
8c1c9170
BH
497
498void PacketReader::xfrBlob(string& blob)
2bc83940 499try
8c1c9170 500{
e2c162d3 501 if(d_recordlen && !(d_pos == (d_startrecordpos + d_recordlen)))
7f7b8d55
BH
502 blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1);
503 else
504 blob.clear();
8c1c9170
BH
505
506 d_pos = d_startrecordpos + d_recordlen;
507}
2bc83940 508catch(...)
509{
510 throw std::out_of_range("xfrBlob out of range");
511}
59a0f653 512
06ffdc52
BH
513void PacketReader::xfrBlob(string& blob, int length)
514{
2617464d 515 if(length) {
0407751c 516 blob.assign(&d_content.at(d_pos), &d_content.at(d_pos + length - 1 ) + 1 );
2617464d
BH
517
518 d_pos += length;
519 }
520 else
521 blob.clear();
06ffdc52
BH
522}
523
524
e4090157 525void PacketReader::xfrHexBlob(string& blob, bool keepReading)
59a0f653
BH
526{
527 xfrBlob(blob);
528}
b8e0f341 529
27ff60a3 530string simpleCompress(const string& elabel, const string& root)
b8e0f341 531{
27ff60a3
PD
532 string label=elabel;
533 // FIXME: this relies on the semi-canonical escaped output from getLabelFromContent
83b746fd 534 if(strchr(label.c_str(), '\\')) {
535 boost::replace_all(label, "\\.", ".");
536 boost::replace_all(label, "\\032", " ");
537 boost::replace_all(label, "\\\\", "\\");
538 }
b8e0f341
BH
539 typedef vector<pair<unsigned int, unsigned int> > parts_t;
540 parts_t parts;
541 vstringtok(parts, label, ".");
542 string ret;
543 ret.reserve(label.size()+4);
544 for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) {
05a38bfa 545 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
546 const unsigned char rootptr[2]={0xc0,0x11};
547 ret.append((const char *) rootptr, 2);
7127879f
BH
548 return ret;
549 }
b8e0f341
BH
550 ret.append(1, (char)(i->second - i->first));
551 ret.append(label.c_str() + i->first, i->second - i->first);
552 }
553 ret.append(1, (char)0);
554 return ret;
555}
556
7127879f 557
b8e0f341
BH
558void simpleExpandTo(const string& label, unsigned int frompos, string& ret)
559{
560 unsigned int labellen=0;
53edfc45 561 while((labellen=(unsigned char)label.at(frompos++))) {
b8e0f341
BH
562 ret.append(label.c_str()+frompos, labellen);
563 ret.append(1,'.');
564 frompos+=labellen;
565 }
566}
2c73e580
BH
567
568/** Simple DNSPacketMangler. Ritual is: get a pointer into the packet and moveOffset() to beyond your needs
569 * If you survive that, feel free to read from the pointer */
570class DNSPacketMangler
571{
572public:
573 explicit DNSPacketMangler(std::string& packet)
574 : d_packet(packet), d_notyouroffset(12), d_offset(d_notyouroffset)
575 {}
576
577 void skipLabel()
578 {
579 uint8_t len;
580 while((len=get8BitInt())) {
581 if(len >= 0xc0) { // extended label
232f0877
CH
582 get8BitInt();
583 return;
2c73e580
BH
584 }
585 skipBytes(len);
586 }
587 }
588 void skipBytes(uint16_t bytes)
589 {
590 moveOffset(bytes);
591 }
592 uint16_t get16BitInt()
593 {
594 const char* p = d_packet.c_str() + d_offset;
595 moveOffset(2);
596 uint16_t ret;
597 memcpy(&ret, (void*)p, 2);
598 return ntohs(ret);
599 }
600
601 uint8_t get8BitInt()
602 {
603 const char* p = d_packet.c_str() + d_offset;
604 moveOffset(1);
605 return *p;
606 }
607
608 void skipRData()
609 {
610 int toskip = get16BitInt();
611 moveOffset(toskip);
612 }
613 void decreaseAndSkip32BitInt(uint32_t decrease)
614 {
615 const char *p = (const char*)d_packet.c_str() + d_offset;
616 moveOffset(4);
617
618 uint32_t tmp;
619 memcpy(&tmp, (void*) p, sizeof(tmp));
620 tmp = ntohl(tmp);
621 tmp-=decrease;
622 tmp = htonl(tmp);
623 d_packet.replace(d_offset-4, sizeof(tmp), (const char*)&tmp, sizeof(tmp));
624 }
625private:
626 void moveOffset(uint16_t by)
627 {
628 d_notyouroffset += by;
629 if(d_notyouroffset > d_packet.length())
10f4eea8 630 throw std::out_of_range("dns packet out of range: "+lexical_cast<string>(d_notyouroffset) +" > "
2c73e580
BH
631 + lexical_cast<string>(d_packet.length()) );
632 }
633 std::string& d_packet;
634
635 uint32_t d_notyouroffset; // only 'moveOffset' can touch this
636 const uint32_t& d_offset; // look.. but don't touch
637
638};
639
640// method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
641void ageDNSPacket(std::string& packet, uint32_t seconds)
642{
44b67752 643 if(packet.length() < sizeof(dnsheader))
2c73e580
BH
644 return;
645 try
646 {
44b67752
BH
647 dnsheader dh;
648 memcpy((void*)&dh, (const dnsheader*)packet.c_str(), sizeof(dh));
649 int numrecords = ntohs(dh.ancount) + ntohs(dh.nscount) + ntohs(dh.arcount);
2c73e580
BH
650 DNSPacketMangler dpm(packet);
651
652 int n;
44b67752 653 for(n=0; n < ntohs(dh.qdcount) ; ++n) {
2c73e580
BH
654 dpm.skipLabel();
655 dpm.skipBytes(4); // qtype, qclass
656 }
657 // cerr<<"Skipped "<<n<<" questions, now parsing "<<numrecords<<" records"<<endl;
658 for(n=0; n < numrecords; ++n) {
659 dpm.skipLabel();
660
661 uint16_t dnstype = dpm.get16BitInt();
88a754aa 662 /* uint16_t dnsclass = */ dpm.get16BitInt();
2c73e580
BH
663
664 if(dnstype == QType::OPT) // not aging that one with a stick
232f0877 665 break;
2c73e580
BH
666
667 dpm.decreaseAndSkip32BitInt(seconds);
668 dpm.skipRData();
669 }
670 }
671 catch(...)
672 {
673 return;
674 }
675}