]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsparser.cc
add OpenSSL exception to PowerDNS, Netherlabs, van Dijk and Hubert copyrights
[thirdparty/pdns.git] / pdns / dnsparser.cc
1 /*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2005 - 2011 PowerDNS.COM BV
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
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
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 St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include "dnsparser.hh"
24 #include "dnswriter.hh"
25 #include <boost/lexical_cast.hpp>
26 #include <boost/algorithm/string.hpp>
27 #include <boost/format.hpp>
28
29 #include "namespaces.hh"
30
31 class UnknownRecordContent : public DNSRecordContent
32 {
33 public:
34 UnknownRecordContent(const DNSRecord& dr, PacketReader& pr)
35 : DNSRecordContent(dr.d_type), d_dr(dr)
36 {
37 pr.copyRecord(d_record, dr.d_clen);
38 }
39
40 UnknownRecordContent(const string& zone) : DNSRecordContent(0)
41 {
42 d_record.insert(d_record.end(), zone.begin(), zone.end());
43 }
44
45 string getZoneRepresentation() const
46 {
47 ostringstream str;
48 str<<"\\# "<<(unsigned int)d_record.size()<<" ";
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 }
54 return str.str();
55 }
56
57 void toPacket(DNSPacketWriter& pw)
58 {
59 string tmp((char*)&*d_record.begin(), d_record.size());
60 vector<string> parts;
61 stringtok(parts, tmp);
62 if(parts.size()!=3 && !(parts.size()==2 && equals(parts[1],"0")) )
63 throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got "+lexical_cast<string>(parts.size())+": "+tmp );
64 const string& relevant=(parts.size() > 2) ? parts[2] : "";
65 unsigned int total=atoi(parts[1].c_str());
66 if(relevant.size()!=2*total)
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());
68 string out;
69 out.reserve(total+1);
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 }
77 private:
78 DNSRecord d_dr;
79 vector<uint8_t> d_record;
80 };
81
82 static const string EncodeDNSLabel(const string& input)
83 {
84 if(input.length() == 1 && input[0]=='.') // otherwise we encode .. (long story)
85 return string (1, 0);
86
87 labelparts_t parts;
88 bool unescapedSomething = labeltokUnescape(parts, input);
89 string ret;
90
91 if(!unescapedSomething) {
92 for(labelparts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) {
93 ret.append(1, i->second - i->first);
94 ret.append(input.c_str() + i->first, i->second - i->first);
95 }
96
97 } else {
98 for(labelparts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) {
99 string part(input.c_str() + i->first, i->second - i->first);
100 boost::replace_all(part, "\\\\", "\\");
101 boost::replace_all(part, "\\.", ".");
102
103 ret.append(1, part.length());
104 ret.append(part);
105 }
106 }
107 ret.append(1, 0);
108 return ret;
109 }
110
111
112 shared_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
120
121 /* will look like: dnsheader, 5 bytes, encoded qname, dns record header, serialized data */
122
123 string encoded=EncodeDNSLabel(qname);
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
134 memcpy(&packet[pos], encoded.c_str(), encoded.size()); pos+=(uint16_t)encoded.size();
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
142 memcpy(&packet[pos], &drh, sizeof(drh)); pos+=sizeof(drh);
143 memcpy(&packet[pos], serialized.c_str(), serialized.size()); pos+=(uint16_t)serialized.size();
144
145 MOADNSParser mdp((char*)&*packet.begin(), (unsigned int)packet.size());
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 }
152
153 DNSRecordContent* DNSRecordContent::mastermake(const DNSRecord &dr,
154 PacketReader& pr)
155 {
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));
159 if(i==getTypemap().end() || !i->second) {
160 return new UnknownRecordContent(dr, pr);
161 }
162
163 return i->second(dr, pr);
164 }
165
166 DNSRecordContent* DNSRecordContent::mastermake(uint16_t qtype, uint16_t qclass,
167 const string& content)
168 {
169 zmakermap_t::const_iterator i=getZmakermap().find(make_pair(qclass, qtype));
170 if(i==getZmakermap().end()) {
171 return new UnknownRecordContent(content);
172 }
173
174 return i->second(content);
175 }
176
177 DNSRecordContent* 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);
183
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
195 DNSRecordContent::typemap_t& DNSRecordContent::getTypemap()
196 {
197 static DNSRecordContent::typemap_t typemap;
198 return typemap;
199 }
200
201 DNSRecordContent::n2typemap_t& DNSRecordContent::getN2Typemap()
202 {
203 static DNSRecordContent::n2typemap_t n2typemap;
204 return n2typemap;
205 }
206
207 DNSRecordContent::t2namemap_t& DNSRecordContent::getT2Namemap()
208 {
209 static DNSRecordContent::t2namemap_t t2namemap;
210 return t2namemap;
211 }
212
213
214 DNSRecordContent::zmakermap_t& DNSRecordContent::getZmakermap()
215 {
216 static DNSRecordContent::zmakermap_t zmakermap;
217 return zmakermap;
218 }
219
220 void 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
227 if(d_header.opcode != Opcode::Query && d_header.opcode != Opcode::Notify && d_header.opcode != Opcode::Update)
228 throw MOADNSException("Can't parse non-query packet with opcode="+ lexical_cast<string>(d_header.opcode));
229
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
235 uint16_t contentlen=len-sizeof(dnsheader);
236
237 d_content.resize(contentlen);
238 copy(packet+sizeof(dnsheader), packet+len, d_content.begin());
239
240 unsigned int n=0;
241
242 PacketReader pr(d_content);
243 bool validPacket=false;
244 try {
245 d_qtype = d_qclass = 0; // sometimes replies come in with no question, don't present garbage then
246
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
253 struct dnsrecordheader ah;
254 vector<unsigned char> record;
255 validPacket=true;
256 for(n=0;n < (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount); ++n) {
257 DNSRecord dr;
258
259 if(n < d_header.ancount)
260 dr.d_place=DNSRecord::Answer;
261 else if(n < d_header.ancount + d_header.nscount)
262 dr.d_place=DNSRecord::Nameserver;
263 else
264 dr.d_place=DNSRecord::Additional;
265
266 unsigned int recordStartPos=pr.d_pos;
267
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;
277
278 dr.d_content=boost::shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(dr, pr, d_header.opcode));
279 d_answers.push_back(make_pair(dr, pr.d_pos));
280
281 if(dr.d_type == QType::TSIG && dr.d_class == 0xff)
282 d_tsigPos = recordStartPos + sizeof(struct dnsheader);
283 }
284
285 #if 0
286 if(pr.d_pos!=contentlen) {
287 throw MOADNSException("Packet ("+d_qname+"|#"+lexical_cast<string>(d_qtype)+") has trailing garbage ("+ lexical_cast<string>(pr.d_pos) + " < " +
288 lexical_cast<string>(contentlen) + ")");
289 }
290 #endif
291 }
292 catch(std::out_of_range &re) {
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) {
295 d_header.ancount=n; d_header.nscount = d_header.arcount = 0;
296 }
297 else if(n < d_header.ancount + d_header.nscount) {
298 d_header.nscount = n - d_header.ancount; d_header.arcount=0;
299 }
300 else {
301 d_header.arcount = n - d_header.ancount - d_header.nscount;
302 }
303 }
304 else {
305 throw MOADNSException("Error parsing packet of "+lexical_cast<string>(len)+" bytes (rd="+
306 lexical_cast<string>(d_header.rd)+
307 "), out of bounds: "+string(re.what()));
308 }
309 }
310 }
311
312
313 void 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);
325
326 d_startrecordpos=d_pos; // needed for getBlob later on
327 d_recordlen=ah.d_clen;
328 }
329
330
331 void PacketReader::copyRecord(vector<unsigned char>& dest, uint16_t len)
332 {
333 dest.resize(len);
334 if(!len)
335 return;
336
337 for(uint16_t n=0;n<len;++n) {
338 dest.at(n)=d_content.at(d_pos++);
339 }
340 }
341
342 void PacketReader::copyRecord(unsigned char* dest, uint16_t len)
343 {
344 if(d_pos + len > d_content.size())
345 throw std::out_of_range("Attempt to copy outside of packet");
346
347 memcpy(dest, &d_content.at(d_pos), len);
348 d_pos+=len;
349 }
350
351 void 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 }
366
367 uint32_t PacketReader::get32BitInt()
368 {
369 uint32_t ret=0;
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
382 uint16_t PacketReader::get16BitInt()
383 {
384 return get16BitInt(d_content, d_pos);
385 }
386
387 uint16_t PacketReader::get16BitInt(const vector<unsigned char>&content, uint16_t& pos)
388 {
389 uint16_t ret=0;
390 ret+=content.at(pos++);
391 ret<<=8;
392 ret+=content.at(pos++);
393
394 return ret;
395 }
396
397 uint8_t PacketReader::get8BitInt()
398 {
399 return d_content.at(d_pos++);
400 }
401
402 string PacketReader::getLabel(unsigned int recurs)
403 {
404 string ret;
405 ret.reserve(40);
406 getLabelFromContent(d_content, d_pos, ret, recurs++);
407 return ret;
408 }
409
410 static string txtEscape(const string &name)
411 {
412 string ret;
413 char ebuf[5];
414
415 for(string::const_iterator i=name.begin();i!=name.end();++i) {
416 if((unsigned char) *i > 127 || (unsigned char) *i < 32) {
417 snprintf(ebuf, sizeof(ebuf), "\\%03u", (unsigned char)*i);
418 ret += ebuf;
419 }
420 else if(*i=='"' || *i=='\\'){
421 ret += '\\';
422 ret += *i;
423 }
424 else
425 ret += *i;
426 }
427 return ret;
428 }
429
430 // exceptions thrown here do not result in logging in the main pdns auth server - just so you know!
431 string PacketReader::getText(bool multi)
432 {
433 string ret;
434 ret.reserve(40);
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,'"');
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 }
446 ret.append(1,'"');
447 d_pos+=labellen;
448 if(!multi)
449 break;
450 }
451
452 return ret;
453 }
454
455
456 void PacketReader::getLabelFromContent(const vector<uint8_t>& content, uint16_t& frompos, string& ret, int recurs)
457 {
458 if(recurs > 1000) // the forward reference-check below should make this test 100% obsolete
459 throw MOADNSException("Loop");
460
461 for(;;) {
462 unsigned char labellen=content.at(frompos++);
463
464 if(!labellen) {
465 if(ret.empty())
466 ret.append(1,'.');
467 break;
468 }
469 if((labellen & 0xc0) == 0xc0) {
470 uint16_t offset=256*(labellen & ~0xc0) + (unsigned int)content.at(frompos++) - sizeof(dnsheader);
471 // cout<<"This is an offset, need to go to: "<<offset<<endl;
472
473 if(offset >= frompos-2)
474 throw MOADNSException("forward reference during label decompression");
475 return getLabelFromContent(content, offset, ret, ++recurs);
476 }
477 else {
478 // XXX FIXME THIS MIGHT BE VERY SLOW!
479 ret.reserve(ret.size() + labellen + 2);
480 for(string::size_type n = 0 ; n < labellen; ++n, frompos++) {
481 if(content.at(frompos)=='.' || content.at(frompos)=='\\') {
482 ret.append(1, '\\');
483 ret.append(1, content[frompos]);
484 }
485 else if(content.at(frompos)==' ') {
486 ret+="\\032";
487 }
488 else
489 ret.append(1, content[frompos]);
490 }
491 ret.append(1,'.');
492 }
493 }
494 }
495
496 void PacketReader::xfrBlob(string& blob)
497 {
498 if(d_recordlen && !(d_pos == (d_startrecordpos + d_recordlen)))
499 blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1);
500 else
501 blob.clear();
502
503 d_pos = d_startrecordpos + d_recordlen;
504 }
505
506 void PacketReader::xfrBlob(string& blob, int length)
507 {
508 if(length) {
509 blob.assign(&d_content.at(d_pos), &d_content.at(d_pos + length - 1 ) + 1 );
510
511 d_pos += length;
512 }
513 else
514 blob.clear();
515 }
516
517
518 void PacketReader::xfrHexBlob(string& blob, bool keepReading)
519 {
520 xfrBlob(blob);
521 }
522
523 string simpleCompress(const string& elabel, const string& root)
524 {
525 string label=elabel;
526 // FIXME: this relies on the semi-canonical escaped output from getLabelFromContent
527 boost::replace_all(label, "\\.", ".");
528 boost::replace_all(label, "\\032", " ");
529 boost::replace_all(label, "\\\\", "\\");
530 typedef vector<pair<unsigned int, unsigned int> > parts_t;
531 parts_t parts;
532 vstringtok(parts, label, ".");
533 string ret;
534 ret.reserve(label.size()+4);
535 for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) {
536 if(!root.empty() && !strncasecmp(root.c_str(), label.c_str() + i->first, 1 + label.length() - i->first)) { // also match trailing 0, hence '1 +'
537 const unsigned char rootptr[2]={0xc0,0x11};
538 ret.append((const char *) rootptr, 2);
539 return ret;
540 }
541 ret.append(1, (char)(i->second - i->first));
542 ret.append(label.c_str() + i->first, i->second - i->first);
543 }
544 ret.append(1, (char)0);
545 return ret;
546 }
547
548
549 void simpleExpandTo(const string& label, unsigned int frompos, string& ret)
550 {
551 unsigned int labellen=0;
552 while((labellen=label.at(frompos++))) {
553 ret.append(label.c_str()+frompos, labellen);
554 ret.append(1,'.');
555 frompos+=labellen;
556 }
557 }
558
559 /** Simple DNSPacketMangler. Ritual is: get a pointer into the packet and moveOffset() to beyond your needs
560 * If you survive that, feel free to read from the pointer */
561 class DNSPacketMangler
562 {
563 public:
564 explicit DNSPacketMangler(std::string& packet)
565 : d_packet(packet), d_notyouroffset(12), d_offset(d_notyouroffset)
566 {}
567
568 void skipLabel()
569 {
570 uint8_t len;
571 while((len=get8BitInt())) {
572 if(len >= 0xc0) { // extended label
573 get8BitInt();
574 return;
575 }
576 skipBytes(len);
577 }
578 }
579 void skipBytes(uint16_t bytes)
580 {
581 moveOffset(bytes);
582 }
583 uint16_t get16BitInt()
584 {
585 const char* p = d_packet.c_str() + d_offset;
586 moveOffset(2);
587 uint16_t ret;
588 memcpy(&ret, (void*)p, 2);
589 return ntohs(ret);
590 }
591
592 uint8_t get8BitInt()
593 {
594 const char* p = d_packet.c_str() + d_offset;
595 moveOffset(1);
596 return *p;
597 }
598
599 void skipRData()
600 {
601 int toskip = get16BitInt();
602 moveOffset(toskip);
603 }
604 void decreaseAndSkip32BitInt(uint32_t decrease)
605 {
606 const char *p = (const char*)d_packet.c_str() + d_offset;
607 moveOffset(4);
608
609 uint32_t tmp;
610 memcpy(&tmp, (void*) p, sizeof(tmp));
611 tmp = ntohl(tmp);
612 tmp-=decrease;
613 tmp = htonl(tmp);
614 d_packet.replace(d_offset-4, sizeof(tmp), (const char*)&tmp, sizeof(tmp));
615 }
616 private:
617 void moveOffset(uint16_t by)
618 {
619 d_notyouroffset += by;
620 if(d_notyouroffset > d_packet.length())
621 throw std::out_of_range("dns packet out of range: "+lexical_cast<string>(d_notyouroffset) +" > "
622 + lexical_cast<string>(d_packet.length()) );
623 }
624 std::string& d_packet;
625
626 uint32_t d_notyouroffset; // only 'moveOffset' can touch this
627 const uint32_t& d_offset; // look.. but don't touch
628
629 };
630
631 // method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it
632 void ageDNSPacket(std::string& packet, uint32_t seconds)
633 {
634 if(packet.length() < sizeof(dnsheader))
635 return;
636 try
637 {
638 dnsheader dh;
639 memcpy((void*)&dh, (const dnsheader*)packet.c_str(), sizeof(dh));
640 int numrecords = ntohs(dh.ancount) + ntohs(dh.nscount) + ntohs(dh.arcount);
641 DNSPacketMangler dpm(packet);
642
643 int n;
644 for(n=0; n < ntohs(dh.qdcount) ; ++n) {
645 dpm.skipLabel();
646 dpm.skipBytes(4); // qtype, qclass
647 }
648 // cerr<<"Skipped "<<n<<" questions, now parsing "<<numrecords<<" records"<<endl;
649 for(n=0; n < numrecords; ++n) {
650 dpm.skipLabel();
651
652 uint16_t dnstype = dpm.get16BitInt();
653 /* uint16_t dnsclass = */ dpm.get16BitInt();
654
655 if(dnstype == QType::OPT) // not aging that one with a stick
656 break;
657
658 dpm.decreaseAndSkip32BitInt(seconds);
659 dpm.skipRData();
660 }
661 }
662 catch(...)
663 {
664 return;
665 }
666 }