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