]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsparser.hh
Merge remote-tracking branch 'origin/master' into rec-edsn-unaligned-test
[thirdparty/pdns.git] / pdns / dnsparser.hh
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
BH
22#ifndef DNSPARSER_HH
23#define DNSPARSER_HH
24
25#include <map>
26#include <sstream>
27#include <stdexcept>
ff6a1e7b
BH
28#include <iostream>
29#include <vector>
30#include <errno.h>
5a57d2ea 31// #include <netinet/in.h>
5716fab3 32#include "misc.hh"
dd7da6cd 33
9a450843
BH
34#include <boost/tuple/tuple.hpp>
35#include <boost/tuple/tuple_comparison.hpp>
5a57d2ea 36#include "dns.hh"
ea634573 37#include "dnswriter.hh"
d926c0da
KM
38#include "dnsname.hh"
39#include "pdnsexception.hh"
f4352636 40#include "iputils.hh"
bff744a8
BH
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
10f4eea8 55#include "namespaces.hh"
61b26744 56#include "namespaces.hh"
7b1469bb
BH
57
58class MOADNSException : public runtime_error
59{
60public:
61 MOADNSException(const string& str) : runtime_error(str)
62 {}
63};
ff6a1e7b 64
ff6a1e7b
BH
65
66class MOADNSParser;
67
68class PacketReader
69{
70public:
78f56b38
RG
71 PacketReader(const std::string& content, uint16_t initialPos=sizeof(dnsheader))
72 : d_pos(initialPos), d_startrecordpos(initialPos), d_content(content)
eef10ff2 73 {
a683e8bd
RG
74 if(content.size() > std::numeric_limits<uint16_t>::max())
75 throw std::out_of_range("packet too large");
76
77 d_recordlen = (uint16_t) content.size();
745bf26e 78 not_used = 0;
eef10ff2 79 }
ff6a1e7b 80
092f210a
BH
81 uint32_t get32BitInt();
82 uint16_t get16BitInt();
bff744a8 83 uint8_t get8BitInt();
341930bb
BH
84
85 void xfr48BitInt(uint64_t& val);
bff744a8
BH
86
87 void xfr32BitInt(uint32_t& val)
88 {
89 val=get32BitInt();
90 }
91
cbf0e7f3
BH
92 void xfrIP(uint32_t& val)
93 {
94 xfr32BitInt(val);
79a9e9ad 95 val=htonl(val);
cbf0e7f3
BH
96 }
97
b9b28916
AT
98 void xfrIP6(std::string &val) {
99 xfrBlob(val, 16);
100 }
101
f4352636
PD
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
8bf26468
BH
116 void xfrTime(uint32_t& val)
117 {
118 xfr32BitInt(val);
119 }
120
121
bff744a8
BH
122 void xfr16BitInt(uint16_t& val)
123 {
124 val=get16BitInt();
125 }
126
8bf26468
BH
127 void xfrType(uint16_t& val)
128 {
129 xfr16BitInt(val);
130 }
131
132
8c1c9170
BH
133 void xfr8BitInt(uint8_t& val)
134 {
135 val=get8BitInt();
136 }
137
138
f21fc0aa 139 void xfrName(DNSName &name, bool compress=false, bool noDot=false)
bff744a8 140 {
143da54c 141 name=getName();
bff744a8
BH
142 }
143
84e1142d 144 void xfrText(string &text, bool multi=false, bool lenField=true)
bff744a8 145 {
84e1142d 146 text=getText(multi, lenField);
bff744a8
BH
147 }
148
948a927f
PL
149 void xfrUnquotedText(string &text, bool lenField){
150 text=getUnquotedText(lenField);
151 }
152
8c1c9170 153 void xfrBlob(string& blob);
2fe9d6f7 154 void xfrBlobNoSpaces(string& blob, int len);
06ffdc52 155 void xfrBlob(string& blob, int length);
e4090157 156 void xfrHexBlob(string& blob, bool keepReading=false);
8c1c9170 157
ff6a1e7b 158 void getDnsrecordheader(struct dnsrecordheader &ah);
092f210a
BH
159 void copyRecord(vector<unsigned char>& dest, uint16_t len);
160 void copyRecord(unsigned char* dest, uint16_t len);
2ce12d79 161
8171ab83 162 DNSName getName();
84e1142d 163 string getText(bool multi, bool lenField);
948a927f 164 string getUnquotedText(bool lenField);
ff6a1e7b 165
bff744a8 166
d476d7fb 167 bool eof() { return true; };
ddb79bca
AT
168 const string getRemaining() const {
169 return "";
170 };
d476d7fb 171
78f56b38
RG
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
ff6a1e7b 182private:
78f56b38 183 uint16_t d_pos;
8c1c9170 184 uint16_t d_startrecordpos; // needed for getBlob later on
abc1d928 185 uint16_t d_recordlen; // ditto
b816d574 186 uint16_t not_used; // Aligns the whole class on 8-byte boundries
78f56b38 187 const std::string& d_content;
ff6a1e7b
BH
188};
189
ea634573 190struct DNSRecord;
7fc69fd0 191
ff6a1e7b
BH
192class DNSRecordContent
193{
194public:
6177a176
RG
195 static std::shared_ptr<DNSRecordContent> mastermake(const DNSRecord &dr, PacketReader& pr);
196 static std::shared_ptr<DNSRecordContent> mastermake(const DNSRecord &dr, PacketReader& pr, uint16_t opcode);
197 static std::shared_ptr<DNSRecordContent> mastermake(uint16_t qtype, uint16_t qclass, const string& zone);
bff744a8 198
f21fc0aa 199 virtual std::string getZoneRepresentation(bool noDot=false) const = 0;
ff6a1e7b 200 virtual ~DNSRecordContent() {}
6c0670c3 201 virtual void toPacket(DNSPacketWriter& pw)=0;
675fa24c 202 virtual string serialize(const DNSName& qname, bool canonic=false, bool lowerCase=false) // it would rock if this were const, but it is too hard
ea634573
BH
203 {
204 vector<uint8_t> packet;
9fdc0834 205 DNSPacketWriter pw(packet, g_rootdnsname, 1);
9c92ad4b
BH
206 if(canonic)
207 pw.setCanonic(true);
208
7f5bf0ba
BH
209 if(lowerCase)
210 pw.setLowercase(true);
211
5a1f298f 212 pw.startRecord(qname, this->getType());
ea634573 213 this->toPacket(pw);
ea634573
BH
214
215 string record;
e636cab2 216 pw.getRecordPayload(record); // needs to be called before commit()
ea634573
BH
217 return record;
218 }
219
f18e430f 220 virtual bool operator==(const DNSRecordContent& rhs) const
221 {
222 return typeid(*this)==typeid(rhs) && this->getZoneRepresentation() == rhs.getZoneRepresentation();
223 }
224
561434a6 225 static shared_ptr<DNSRecordContent> unserialize(const DNSName& qname, uint16_t qtype, const string& serialized);
ff6a1e7b 226
2770fad0
BH
227 void doRecordCheck(const struct DNSRecord&){}
228
32122aab
RG
229 typedef std::shared_ptr<DNSRecordContent> makerfunc_t(const struct DNSRecord& dr, PacketReader& pr);
230 typedef std::shared_ptr<DNSRecordContent> zmakerfunc_t(const string& str);
6c0670c3
BH
231
232 static void regist(uint16_t cl, uint16_t ty, makerfunc_t* f, zmakerfunc_t* z, const char* name)
7fc69fd0 233 {
8a63d3ce 234 if(f)
49a06471 235 getTypemap()[make_pair(cl,ty)]=f;
8a63d3ce 236 if(z)
49a06471 237 getZmakermap()[make_pair(cl,ty)]=z;
8a63d3ce 238
108c321e
BH
239 getT2Namemap().insert(make_pair(make_pair(cl,ty), name));
240 getN2Typemap().insert(make_pair(name, make_pair(cl,ty)));
7fc69fd0
BH
241 }
242
ee1ada80
BH
243 static void unregist(uint16_t cl, uint16_t ty)
244 {
245 pair<uint16_t, uint16_t> key=make_pair(cl, ty);
246 getTypemap().erase(key);
247 getZmakermap().erase(key);
248 }
249
7fc69fd0
BH
250 static uint16_t TypeToNumber(const string& name)
251 {
e1469cfc 252 n2typemap_t::const_iterator iter = getN2Typemap().find(toUpper(name));
108c321e 253 if(iter != getN2Typemap().end())
274e6aae
BH
254 return iter->second.second;
255
f6209eaf 256 if(boost::starts_with(name, "TYPE") || boost::starts_with(name, "type"))
a683e8bd 257 return (uint16_t) pdns_stou(name.substr(4));
274e6aae 258
7fc69fd0 259 throw runtime_error("Unknown DNS type '"+name+"'");
7fc69fd0
BH
260 }
261
57e5f5f7 262 static const string NumberToType(uint16_t num, uint16_t classnum=1)
7fc69fd0 263 {
108c321e
BH
264 t2namemap_t::const_iterator iter = getT2Namemap().find(make_pair(classnum, num));
265 if(iter == getT2Namemap().end())
335da0ba
AT
266 return "TYPE" + std::to_string(num);
267 // throw runtime_error("Unknown DNS type with numerical id "+std::to_string(num));
274e6aae 268 return iter->second;
7fc69fd0
BH
269 }
270
5a1f298f 271 virtual uint16_t getType() const = 0;
7fc69fd0 272
ea634573 273protected:
092f210a 274 typedef std::map<std::pair<uint16_t, uint16_t>, makerfunc_t* > typemap_t;
6c0670c3 275 typedef std::map<std::pair<uint16_t, uint16_t>, zmakerfunc_t* > zmakermap_t;
108c321e
BH
276 typedef std::map<std::pair<uint16_t, uint16_t>, string > t2namemap_t;
277 typedef std::map<string, std::pair<uint16_t, uint16_t> > n2typemap_t;
49a06471 278 static typemap_t& getTypemap();
108c321e
BH
279 static t2namemap_t& getT2Namemap();
280 static n2typemap_t& getN2Typemap();
49a06471 281 static zmakermap_t& getZmakermap();
ff6a1e7b
BH
282};
283
284struct DNSRecord
285{
c613b06f
CHB
286 DNSRecord() : d_type(0), d_class(QClass::IN), d_ttl(0), d_clen(0), d_place(DNSResourceRecord::ANSWER)
287 {}
fbe23591 288 explicit DNSRecord(const DNSResourceRecord& rr);
f809c028 289 DNSName d_name;
249fb4c2 290 std::shared_ptr<DNSRecordContent> d_content;
092f210a
BH
291 uint16_t d_type;
292 uint16_t d_class;
293 uint32_t d_ttl;
294 uint16_t d_clen;
e693ff5a 295 DNSResourceRecord::Place d_place;
9a450843
BH
296
297 bool operator<(const DNSRecord& rhs) const
298 {
2dcd140a 299 if(tie(d_name, d_type, d_class, d_ttl) < tie(rhs.d_name, rhs.d_type, rhs.d_class, rhs.d_ttl))
2caf4671 300 return true;
301
2dcd140a 302 if(tie(d_name, d_type, d_class, d_ttl) != tie(rhs.d_name, rhs.d_type, rhs.d_class, rhs.d_ttl))
2caf4671 303 return false;
304
9a450843
BH
305 string lzrp, rzrp;
306 if(d_content)
5716fab3 307 lzrp=toLower(d_content->getZoneRepresentation());
9a450843 308 if(rhs.d_content)
5716fab3 309 rzrp=toLower(rhs.d_content->getZoneRepresentation());
9a450843 310
2caf4671 311 return lzrp < rzrp;
9a450843
BH
312 }
313
23721d33 314 // this orders in canonical order and keeps the SOA record on top
315 static bool prettyCompare(const DNSRecord& a, const DNSRecord& b)
316 {
317 auto aType = (a.d_type == QType::SOA) ? 0 : a.d_type;
318 auto bType = (b.d_type == QType::SOA) ? 0 : b.d_type;
319
320 if(a.d_name.canonCompare(b.d_name))
321 return true;
322 if(b.d_name.canonCompare(a.d_name))
323 return false;
324
325 if(tie(aType, a.d_class, a.d_ttl) < tie(bType, b.d_class, b.d_ttl))
326 return true;
327
328 if(tie(aType, a.d_class, a.d_ttl) != tie(bType, b.d_class, b.d_ttl))
329 return false;
330
331 string lzrp, rzrp;
332 if(a.d_content)
333 lzrp=toLower(a.d_content->getZoneRepresentation());
334 if(b.d_content)
335 rzrp=toLower(b.d_content->getZoneRepresentation());
336
337 return lzrp < rzrp;
338 }
339
340
9a450843
BH
341 bool operator==(const DNSRecord& rhs) const
342 {
90ba52e0 343 if(d_type != rhs.d_type || d_class != rhs.d_class || d_name != rhs.d_name)
344 return false;
345
f18e430f 346 return *d_content == *rhs.d_content;
9a450843 347 }
ff6a1e7b
BH
348};
349
90ba52e0 350struct DNSZoneRecord
351{
7fdee6fc 352 int domain_id{-1};
353 uint8_t scopeMask{0};
354 int signttl{0};
90ba52e0 355 DNSName wildcardname;
7fdee6fc 356 bool auth{true};
90ba52e0 357 DNSRecord dr;
358};
359
360
6c0670c3 361//! This class can be used to parse incoming packets, and is copyable
57e5f5f7 362class MOADNSParser : public boost::noncopyable
ff6a1e7b
BH
363{
364public:
6c0670c3 365 //! Parse from a string
78f56b38 366 MOADNSParser(bool query, const string& buffer): d_tsigPos(0)
ff6a1e7b 367 {
78f56b38 368 init(query, buffer);
ff6a1e7b
BH
369 }
370
6c0670c3 371 //! Parse from a pointer and length
27c0050c 372 MOADNSParser(bool query, const char *packet, unsigned int len) : d_tsigPos(0)
ff6a1e7b 373 {
78f56b38 374 init(query, std::string(packet, len));
ff6a1e7b 375 }
6c0670c3 376
561434a6 377 DNSName d_qname;
092f210a 378 uint16_t d_qclass, d_qtype;
e2ab3f63 379 //uint8_t d_rcode;
46443f87 380 dnsheader d_header;
ff6a1e7b
BH
381
382 typedef vector<pair<DNSRecord, uint16_t > > answers_t;
6c0670c3 383
b88526ce 384 //! All answers contained in this packet (everything *but* the question section)
ff6a1e7b
BH
385 answers_t d_answers;
386
60a1c204 387 uint16_t getTSIGPos() const
57e5f5f7
BH
388 {
389 return d_tsigPos;
390 }
ff6a1e7b 391private:
78f56b38 392 void init(bool query, const std::string& packet);
57e5f5f7 393 uint16_t d_tsigPos;
ff6a1e7b
BH
394};
395
7127879f 396string simpleCompress(const string& label, const string& root="");
886e2cf2 397void ageDNSPacket(char* packet, size_t length, uint32_t seconds);
2c73e580 398void ageDNSPacket(std::string& packet, uint32_t seconds);
153d5065 399void editDNSPacketTTL(char* packet, size_t length, std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)> visitor);
47698274 400uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA=nullptr);
55baa1f2
RG
401uint32_t getDNSPacketLength(const char* packet, size_t length);
402uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type);
e0fd37ec 403bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z);
a1d0d0e6 404
405template<typename T>
406std::shared_ptr<T> getRR(const DNSRecord& dr)
407{
408 return std::dynamic_pointer_cast<T>(dr.d_content);
409}
410
ff6a1e7b 411#endif