]>
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" | |
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 | |
58 | class MOADNSException : public runtime_error | |
59 | { | |
60 | public: | |
61 | MOADNSException(const string& str) : runtime_error(str) | |
62 | {} | |
63 | }; | |
ff6a1e7b | 64 | |
ff6a1e7b BH |
65 | |
66 | class MOADNSParser; | |
67 | ||
68 | class PacketReader | |
69 | { | |
70 | public: | |
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 | 182 | private: |
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 | 190 | struct DNSRecord; |
7fc69fd0 | 191 | |
ff6a1e7b BH |
192 | class DNSRecordContent |
193 | { | |
194 | public: | |
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 | 273 | protected: |
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 | ||
284 | struct 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 | 350 | struct 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 | 362 | class MOADNSParser : public boost::noncopyable |
ff6a1e7b BH |
363 | { |
364 | public: | |
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 | 391 | private: |
78f56b38 | 392 | void init(bool query, const std::string& packet); |
57e5f5f7 | 393 | uint16_t d_tsigPos; |
ff6a1e7b BH |
394 | }; |
395 | ||
7127879f | 396 | string simpleCompress(const string& label, const string& root=""); |
886e2cf2 | 397 | void ageDNSPacket(char* packet, size_t length, uint32_t seconds); |
2c73e580 | 398 | void ageDNSPacket(std::string& packet, uint32_t seconds); |
153d5065 | 399 | void editDNSPacketTTL(char* packet, size_t length, std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)> visitor); |
47698274 | 400 | uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA=nullptr); |
55baa1f2 RG |
401 | uint32_t getDNSPacketLength(const char* packet, size_t length); |
402 | uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type); | |
e0fd37ec | 403 | bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z); |
a1d0d0e6 | 404 | |
405 | template<typename T> | |
406 | std::shared_ptr<T> getRR(const DNSRecord& dr) | |
407 | { | |
408 | return std::dynamic_pointer_cast<T>(dr.d_content); | |
409 | } | |
410 | ||
ff6a1e7b | 411 | #endif |