]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsparser.hh
Merge pull request #13387 from omoerbeek/rec-b-root-servers
[thirdparty/pdns.git] / pdns / dnsparser.hh
1 /*
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 */
22 #pragma once
23 #include <map>
24 #include <sstream>
25 #include <stdexcept>
26 #include <iostream>
27 #include <unordered_set>
28 #include <utility>
29 #include <vector>
30 #include <cerrno>
31 // #include <netinet/in.h>
32 #include "misc.hh"
33
34 #include "dns.hh"
35 #include "dnswriter.hh"
36 #include "dnsname.hh"
37 #include "noinitvector.hh"
38 #include "pdnsexception.hh"
39 #include "iputils.hh"
40 #include "svc-records.hh"
41
42 /** DNS records have three representations:
43 1) in the packet
44 2) parsed in a class, ready for use
45 3) in the zone
46
47 We should implement bidirectional transitions between 1&2 and 2&3.
48 Currently we have: 1 -> 2
49 2 -> 3
50
51 We can add: 2 -> 1 easily by reversing the packetwriter
52 And we might be able to reverse 2 -> 3 as well
53 */
54
55 #include "namespaces.hh"
56
57 class MOADNSException : public runtime_error
58 {
59 public:
60 MOADNSException(const string& str) : runtime_error(str)
61 {}
62 };
63
64
65 class MOADNSParser;
66
67 class PacketReader
68 {
69 public:
70 PacketReader(const std::string_view& content, uint16_t initialPos=sizeof(dnsheader))
71 : d_pos(initialPos), d_startrecordpos(initialPos), d_content(content)
72 {
73 if(content.size() > std::numeric_limits<uint16_t>::max())
74 throw std::out_of_range("packet too large");
75
76 d_recordlen = (uint16_t) content.size();
77 not_used = 0;
78 }
79
80 uint32_t get32BitInt();
81 uint16_t get16BitInt();
82 uint8_t get8BitInt();
83
84 void xfrNodeOrLocatorID(NodeOrLocatorID& val);
85 void xfr48BitInt(uint64_t& val);
86
87 void xfr32BitInt(uint32_t& val)
88 {
89 val=get32BitInt();
90 }
91
92 void xfrIP(uint32_t& val)
93 {
94 xfr32BitInt(val);
95 val=htonl(val);
96 }
97
98 void xfrIP6(std::string &val) {
99 xfrBlob(val, 16);
100 }
101
102 void xfrCAWithoutPort(uint8_t version, ComboAddress &val) {
103 string blob;
104 if (version == 4) xfrBlob(blob, 4);
105 else if (version == 6) xfrBlob(blob, 16);
106 else throw runtime_error("invalid IP protocol");
107 val = makeComboAddressFromRaw(version, blob);
108 }
109
110 void xfrCAPort(ComboAddress &val) {
111 uint16_t port;
112 xfr16BitInt(port);
113 val.sin4.sin_port = port;
114 }
115
116 void xfrTime(uint32_t& val)
117 {
118 xfr32BitInt(val);
119 }
120
121
122 void xfr16BitInt(uint16_t& val)
123 {
124 val=get16BitInt();
125 }
126
127 void xfrType(uint16_t& val)
128 {
129 xfr16BitInt(val);
130 }
131
132
133 void xfr8BitInt(uint8_t& val)
134 {
135 val=get8BitInt();
136 }
137
138 void xfrName(DNSName& name, bool /* compress */ = false, bool /* noDot */ = false)
139 {
140 name = getName();
141 }
142
143 void xfrText(string &text, bool multi=false, bool lenField=true)
144 {
145 text=getText(multi, lenField);
146 }
147
148 void xfrUnquotedText(string &text, bool lenField){
149 text=getUnquotedText(lenField);
150 }
151
152 void xfrBlob(string& blob);
153 void xfrBlobNoSpaces(string& blob, int len);
154 void xfrBlob(string& blob, int length);
155 void xfrHexBlob(string& blob, bool keepReading=false);
156 void xfrSvcParamKeyVals(set<SvcParam> &kvs);
157
158 void getDnsrecordheader(struct dnsrecordheader &ah);
159 void copyRecord(vector<unsigned char>& dest, uint16_t len);
160 void copyRecord(unsigned char* dest, uint16_t len);
161
162 DNSName getName();
163 string getText(bool multi, bool lenField);
164 string getUnquotedText(bool lenField);
165
166
167 bool eof() { return true; };
168 const string getRemaining() const {
169 return "";
170 };
171
172 uint16_t getPosition() const
173 {
174 return d_pos;
175 }
176
177 void skip(uint16_t n)
178 {
179 d_pos += n;
180 }
181
182 private:
183 uint16_t d_pos;
184 uint16_t d_startrecordpos; // needed for getBlob later on
185 uint16_t d_recordlen; // ditto
186 uint16_t not_used; // Aligns the whole class on 8-byte boundaries
187 const std::string_view d_content;
188 };
189
190 struct DNSRecord;
191
192 class DNSRecordContent
193 {
194 public:
195 static std::shared_ptr<DNSRecordContent> make(const DNSRecord& dr, PacketReader& pr);
196 static std::shared_ptr<DNSRecordContent> make(const DNSRecord& dr, PacketReader& pr, uint16_t opcode);
197 static std::shared_ptr<DNSRecordContent> make(uint16_t qtype, uint16_t qclass, const string& zone);
198 static string upgradeContent(const DNSName& qname, const QType& qtype, const string& content);
199
200 virtual std::string getZoneRepresentation(bool noDot=false) const = 0;
201 virtual ~DNSRecordContent() {}
202 virtual void toPacket(DNSPacketWriter& pw) const = 0;
203 // returns the wire format of the content, possibly including compressed pointers pointing to the owner name (unless canonic or lowerCase are set)
204 string serialize(const DNSName& qname, bool canonic=false, bool lowerCase=false) const
205 {
206 vector<uint8_t> packet;
207 DNSPacketWriter pw(packet, g_rootdnsname, 1);
208 if(canonic)
209 pw.setCanonic(true);
210
211 if(lowerCase)
212 pw.setLowercase(true);
213
214 pw.startRecord(qname, this->getType());
215 this->toPacket(pw);
216
217 string record;
218 pw.getRecordPayload(record); // needs to be called before commit()
219 return record;
220 }
221
222 virtual bool operator==(const DNSRecordContent& rhs) const
223 {
224 return typeid(*this)==typeid(rhs) && this->getZoneRepresentation() == rhs.getZoneRepresentation();
225 }
226
227 // parse the content in wire format, possibly including compressed pointers pointing to the owner name
228 static shared_ptr<DNSRecordContent> deserialize(const DNSName& qname, uint16_t qtype, const string& serialized);
229
230 void doRecordCheck(const struct DNSRecord&){}
231
232 typedef std::shared_ptr<DNSRecordContent> makerfunc_t(const struct DNSRecord& dr, PacketReader& pr);
233 typedef std::shared_ptr<DNSRecordContent> zmakerfunc_t(const string& str);
234
235 static void regist(uint16_t cl, uint16_t ty, makerfunc_t* f, zmakerfunc_t* z, const char* name)
236 {
237 if(f)
238 getTypemap()[pair(cl,ty)]=f;
239 if(z)
240 getZmakermap()[pair(cl,ty)]=z;
241
242 getT2Namemap().emplace(pair(cl, ty), name);
243 getN2Typemap().emplace(name, pair(cl, ty));
244 }
245
246 static void unregist(uint16_t cl, uint16_t ty)
247 {
248 auto key = pair(cl, ty);
249 getTypemap().erase(key);
250 getZmakermap().erase(key);
251 }
252
253 static bool isUnknownType(const string& name)
254 {
255 return boost::starts_with(name, "TYPE") || boost::starts_with(name, "type");
256 }
257
258 static uint16_t TypeToNumber(const string& name)
259 {
260 n2typemap_t::const_iterator iter = getN2Typemap().find(toUpper(name));
261 if(iter != getN2Typemap().end())
262 return iter->second.second;
263
264 if (isUnknownType(name)) {
265 return pdns::checked_stoi<uint16_t>(name.substr(4));
266 }
267
268 throw runtime_error("Unknown DNS type '"+name+"'");
269 }
270
271 static const string NumberToType(uint16_t num, uint16_t classnum = QClass::IN)
272 {
273 auto iter = getT2Namemap().find(pair(classnum, num));
274 if(iter == getT2Namemap().end())
275 return "TYPE" + std::to_string(num);
276 // throw runtime_error("Unknown DNS type with numerical id "+std::to_string(num));
277 return iter->second;
278 }
279
280 /**
281 * \brief Return whether we have implemented a content representation for this type
282 */
283 static bool isRegisteredType(uint16_t rtype, uint16_t rclass = QClass::IN);
284
285 virtual uint16_t getType() const = 0;
286
287 protected:
288 typedef std::map<std::pair<uint16_t, uint16_t>, makerfunc_t* > typemap_t;
289 typedef std::map<std::pair<uint16_t, uint16_t>, zmakerfunc_t* > zmakermap_t;
290 typedef std::map<std::pair<uint16_t, uint16_t>, string > t2namemap_t;
291 typedef std::map<string, std::pair<uint16_t, uint16_t> > n2typemap_t;
292 static typemap_t& getTypemap();
293 static t2namemap_t& getT2Namemap();
294 static n2typemap_t& getN2Typemap();
295 static zmakermap_t& getZmakermap();
296 };
297
298 struct DNSRecord
299 {
300 DNSRecord() :
301 d_class(QClass::IN)
302 {}
303 explicit DNSRecord(const DNSResourceRecord& rr);
304 DNSRecord(const std::string& name,
305 std::shared_ptr<DNSRecordContent> content,
306 const uint16_t type,
307 const uint16_t qclass = QClass::IN,
308 const uint32_t ttl = 86400,
309 const uint16_t clen = 0,
310 const DNSResourceRecord::Place place = DNSResourceRecord::ANSWER) :
311 d_name(DNSName(name)),
312 d_content(std::move(content)),
313 d_type(type),
314 d_class(qclass),
315 d_ttl(ttl),
316 d_clen(clen),
317 d_place(place) {}
318
319 DNSName d_name;
320 private:
321 std::shared_ptr<const DNSRecordContent> d_content;
322 public:
323 uint16_t d_type{};
324 uint16_t d_class{};
325 uint32_t d_ttl{};
326 uint16_t d_clen{};
327 DNSResourceRecord::Place d_place{DNSResourceRecord::ANSWER};
328
329 [[nodiscard]] std::string print(const std::string& indent = "") const
330 {
331 std::stringstream s;
332 s << indent << "Content = " << d_content->getZoneRepresentation() << std::endl;
333 s << indent << "Type = " << d_type << std::endl;
334 s << indent << "Class = " << d_class << std::endl;
335 s << indent << "TTL = " << d_ttl << std::endl;
336 s << indent << "clen = " << d_clen << std::endl;
337 s << indent << "Place = " << std::to_string(d_place) << std::endl;
338 return s.str();
339 }
340
341 void setContent(const std::shared_ptr<const DNSRecordContent>& content)
342 {
343 d_content = content;
344 }
345
346 void setContent(std::shared_ptr<const DNSRecordContent>&& content)
347 {
348 d_content = std::move(content);
349 }
350
351 [[nodiscard]] const std::shared_ptr<const DNSRecordContent>& getContent() const
352 {
353 return d_content;
354 }
355
356 bool operator<(const DNSRecord& rhs) const
357 {
358 if(std::tie(d_name, d_type, d_class, d_ttl) < std::tie(rhs.d_name, rhs.d_type, rhs.d_class, rhs.d_ttl))
359 return true;
360
361 if(std::tie(d_name, d_type, d_class, d_ttl) != std::tie(rhs.d_name, rhs.d_type, rhs.d_class, rhs.d_ttl))
362 return false;
363
364 string lzrp, rzrp;
365 if(d_content)
366 lzrp=toLower(d_content->getZoneRepresentation());
367 if(rhs.d_content)
368 rzrp=toLower(rhs.d_content->getZoneRepresentation());
369
370 return lzrp < rzrp;
371 }
372
373 // this orders in canonical order and keeps the SOA record on top
374 static bool prettyCompare(const DNSRecord& a, const DNSRecord& b)
375 {
376 auto aType = (a.d_type == QType::SOA) ? 0 : a.d_type;
377 auto bType = (b.d_type == QType::SOA) ? 0 : b.d_type;
378
379 if(a.d_name.canonCompare(b.d_name))
380 return true;
381 if(b.d_name.canonCompare(a.d_name))
382 return false;
383
384 if(std::tie(aType, a.d_class, a.d_ttl) < std::tie(bType, b.d_class, b.d_ttl))
385 return true;
386
387 if(std::tie(aType, a.d_class, a.d_ttl) != std::tie(bType, b.d_class, b.d_ttl))
388 return false;
389
390 string lzrp, rzrp;
391 if(a.d_content)
392 lzrp = a.d_content->getZoneRepresentation();
393 if(b.d_content)
394 rzrp = b.d_content->getZoneRepresentation();
395
396 switch (a.d_type) {
397 case QType::TXT:
398 case QType::SPF:
399 #if !defined(RECURSOR)
400 case QType::LUA:
401 #endif
402 return lzrp < rzrp;
403 default:
404 return toLower(lzrp) < toLower(rzrp);
405 }
406 }
407
408 bool operator==(const DNSRecord& rhs) const
409 {
410 if (d_type != rhs.d_type || d_class != rhs.d_class || d_name != rhs.d_name) {
411 return false;
412 }
413
414 return *d_content == *rhs.d_content;
415 }
416 };
417
418 struct DNSZoneRecord
419 {
420 int domain_id{-1};
421 uint8_t scopeMask{0};
422 int signttl{0};
423 DNSName wildcardname;
424 bool auth{true};
425 bool disabled{false};
426 DNSRecord dr;
427 };
428
429 class UnknownRecordContent : public DNSRecordContent
430 {
431 public:
432 UnknownRecordContent(const DNSRecord& dr, PacketReader& pr)
433 : d_dr(dr)
434 {
435 pr.copyRecord(d_record, dr.d_clen);
436 }
437
438 UnknownRecordContent(const string& zone);
439
440 string getZoneRepresentation(bool noDot) const override;
441 void toPacket(DNSPacketWriter& pw) const override;
442 uint16_t getType() const override
443 {
444 return d_dr.d_type;
445 }
446
447 const vector<uint8_t>& getRawContent() const
448 {
449 return d_record;
450 }
451
452 private:
453 DNSRecord d_dr;
454 vector<uint8_t> d_record;
455 };
456
457 //! This class can be used to parse incoming packets, and is copyable
458 class MOADNSParser : public boost::noncopyable
459 {
460 public:
461 //! Parse from a string
462 MOADNSParser(bool query, const string& buffer): d_tsigPos(0)
463 {
464 init(query, buffer);
465 }
466
467 //! Parse from a pointer and length
468 MOADNSParser(bool query, const char *packet, unsigned int len) : d_tsigPos(0)
469 {
470 init(query, std::string_view(packet, len));
471 }
472
473 DNSName d_qname;
474 uint16_t d_qclass, d_qtype;
475 //uint8_t d_rcode;
476 dnsheader d_header;
477
478 typedef vector<pair<DNSRecord, uint16_t > > answers_t;
479
480 //! All answers contained in this packet (everything *but* the question section)
481 answers_t d_answers;
482
483 uint16_t getTSIGPos() const
484 {
485 return d_tsigPos;
486 }
487
488 bool hasEDNS() const;
489
490 private:
491 void init(bool query, const std::string_view& packet);
492 uint16_t d_tsigPos;
493 };
494
495 string simpleCompress(const string& label, const string& root="");
496 void ageDNSPacket(char* packet, size_t length, uint32_t seconds, const dnsheader_aligned&);
497 void ageDNSPacket(std::string& packet, uint32_t seconds, const dnsheader_aligned&);
498 void editDNSPacketTTL(char* packet, size_t length, const std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)>& visitor);
499 void clearDNSPacketRecordTypes(vector<uint8_t>& packet, const std::unordered_set<QType>& qtypes);
500 void clearDNSPacketRecordTypes(PacketBuffer& packet, const std::unordered_set<QType>& qtypes);
501 void clearDNSPacketRecordTypes(char* packet, size_t& length, const std::unordered_set<QType>& qtypes);
502 uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA=nullptr);
503 uint32_t getDNSPacketLength(const char* packet, size_t length);
504 uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type);
505 bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z);
506 /* call the visitor for every records in the answer, authority and additional sections, passing the section, class, type, ttl, rdatalength and rdata
507 to the visitor. Stops whenever the visitor returns false or at the end of the packet */
508 bool visitDNSPacket(const std::string_view& packet, const std::function<bool(uint8_t, uint16_t, uint16_t, uint32_t, uint16_t, const char*)>& visitor);
509
510 template<typename T>
511 std::shared_ptr<const T> getRR(const DNSRecord& dr)
512 {
513 return std::dynamic_pointer_cast<const T>(dr.getContent());
514 }
515
516 /** Simple DNSPacketMangler. Ritual is: get a pointer into the packet and moveOffset() to beyond your needs
517 * If you survive that, feel free to read from the pointer */
518 class DNSPacketMangler
519 {
520 public:
521 explicit DNSPacketMangler(std::string& packet)
522 : d_packet(packet.data()), d_length(packet.length()), d_notyouroffset(12), d_offset(d_notyouroffset)
523 {}
524 DNSPacketMangler(char* packet, size_t length)
525 : d_packet(packet), d_length(length), d_notyouroffset(12), d_offset(d_notyouroffset)
526 {}
527
528 /*! Advances past a wire-format domain name
529 * The name is not checked for adherence to length restrictions.
530 * Compression pointers are not followed.
531 */
532 void skipDomainName()
533 {
534 uint8_t len;
535 while((len=get8BitInt())) {
536 if(len >= 0xc0) { // extended label
537 get8BitInt();
538 return;
539 }
540 skipBytes(len);
541 }
542 }
543
544 void skipBytes(uint16_t bytes)
545 {
546 moveOffset(bytes);
547 }
548 void rewindBytes(uint16_t by)
549 {
550 rewindOffset(by);
551 }
552 uint32_t get32BitInt()
553 {
554 const char* p = d_packet + d_offset;
555 moveOffset(4);
556 uint32_t ret;
557 memcpy(&ret, p, sizeof(ret));
558 return ntohl(ret);
559 }
560 uint16_t get16BitInt()
561 {
562 const char* p = d_packet + d_offset;
563 moveOffset(2);
564 uint16_t ret;
565 memcpy(&ret, p, sizeof(ret));
566 return ntohs(ret);
567 }
568
569 uint8_t get8BitInt()
570 {
571 const char* p = d_packet + d_offset;
572 moveOffset(1);
573 return *p;
574 }
575
576 void skipRData()
577 {
578 int toskip = get16BitInt();
579 moveOffset(toskip);
580 }
581
582 void decreaseAndSkip32BitInt(uint32_t decrease)
583 {
584 const char *p = d_packet + d_offset;
585 moveOffset(4);
586
587 uint32_t tmp;
588 memcpy(&tmp, p, sizeof(tmp));
589 tmp = ntohl(tmp);
590 if (tmp > decrease) {
591 tmp -= decrease;
592 } else {
593 tmp = 0;
594 }
595 tmp = htonl(tmp);
596 memcpy(d_packet + d_offset-4, (const char*)&tmp, sizeof(tmp));
597 }
598
599 void setAndSkip32BitInt(uint32_t value)
600 {
601 moveOffset(4);
602
603 value = htonl(value);
604 memcpy(d_packet + d_offset-4, (const char*)&value, sizeof(value));
605 }
606
607 uint32_t getOffset() const
608 {
609 return d_offset;
610 }
611
612 private:
613 void moveOffset(uint16_t by)
614 {
615 d_notyouroffset += by;
616 if(d_notyouroffset > d_length)
617 throw std::out_of_range("dns packet out of range: "+std::to_string(d_notyouroffset) +" > "
618 + std::to_string(d_length) );
619 }
620
621 void rewindOffset(uint16_t by)
622 {
623 if(d_notyouroffset < by)
624 throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < "
625 + std::to_string(by));
626 d_notyouroffset -= by;
627 if(d_notyouroffset < 12)
628 throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < "
629 + std::to_string(12));
630 }
631
632 char* d_packet;
633 size_t d_length;
634
635 uint32_t d_notyouroffset; // only 'moveOffset' can touch this
636 const uint32_t& d_offset; // look.. but don't touch
637 };