]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsparser.hh
rec: Set ecs-ipv4-cache-bits and ecs-ipv6-cache-bits in the tests
[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 #ifndef DNSPARSER_HH
23 #define DNSPARSER_HH
24
25 #include <map>
26 #include <sstream>
27 #include <stdexcept>
28 #include <iostream>
29 #include <vector>
30 #include <errno.h>
31 // #include <netinet/in.h>
32 #include "misc.hh"
33
34 #include <boost/tuple/tuple.hpp>
35 #include <boost/tuple/tuple_comparison.hpp>
36 #include "dns.hh"
37 #include "dnswriter.hh"
38 #include "dnsname.hh"
39 #include "pdnsexception.hh"
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
54 #include "namespaces.hh"
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 vector<uint8_t>& content)
71 : d_pos(0), d_startrecordpos(0), 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 xfr48BitInt(uint64_t& val);
85
86 void xfr32BitInt(uint32_t& val)
87 {
88 val=get32BitInt();
89 }
90
91 void xfrIP(uint32_t& val)
92 {
93 xfr32BitInt(val);
94 val=htonl(val);
95 }
96
97 void xfrIP6(std::string &val) {
98 xfrBlob(val, 16);
99 }
100
101 void xfrTime(uint32_t& val)
102 {
103 xfr32BitInt(val);
104 }
105
106
107 void xfr16BitInt(uint16_t& val)
108 {
109 val=get16BitInt();
110 }
111
112 void xfrType(uint16_t& val)
113 {
114 xfr16BitInt(val);
115 }
116
117
118 void xfr8BitInt(uint8_t& val)
119 {
120 val=get8BitInt();
121 }
122
123
124 void xfrName(DNSName &name, bool compress=false, bool noDot=false)
125 {
126 name=getName();
127 }
128
129 void xfrText(string &text, bool multi=false, bool lenField=true)
130 {
131 text=getText(multi, lenField);
132 }
133
134 void xfrUnquotedText(string &text, bool lenField){
135 text=getUnquotedText(lenField);
136 }
137
138 void xfrBlob(string& blob);
139 void xfrBlobNoSpaces(string& blob, int len);
140 void xfrBlob(string& blob, int length);
141 void xfrHexBlob(string& blob, bool keepReading=false);
142
143 static uint16_t get16BitInt(const vector<unsigned char>&content, uint16_t& pos);
144
145 void getDnsrecordheader(struct dnsrecordheader &ah);
146 void copyRecord(vector<unsigned char>& dest, uint16_t len);
147 void copyRecord(unsigned char* dest, uint16_t len);
148
149 DNSName getName();
150 string getText(bool multi, bool lenField);
151 string getUnquotedText(bool lenField);
152
153 uint16_t d_pos;
154
155 bool eof() { return true; };
156
157 private:
158 uint16_t d_startrecordpos; // needed for getBlob later on
159 uint16_t d_recordlen; // ditto
160 uint16_t not_used; // Aligns the whole class on 8-byte boundries
161 const vector<uint8_t>& d_content;
162 };
163
164 struct DNSRecord;
165
166 class DNSRecordContent
167 {
168 public:
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);
172
173 virtual std::string getZoneRepresentation(bool noDot=false) const = 0;
174 virtual ~DNSRecordContent() {}
175 virtual void toPacket(DNSPacketWriter& pw)=0;
176 virtual string serialize(const DNSName& qname, bool canonic=false, bool lowerCase=false) // it would rock if this were const, but it is too hard
177 {
178 vector<uint8_t> packet;
179 DNSPacketWriter pw(packet, g_rootdnsname, 1);
180 if(canonic)
181 pw.setCanonic(true);
182
183 if(lowerCase)
184 pw.setLowercase(true);
185
186 pw.startRecord(qname, this->getType());
187 this->toPacket(pw);
188
189 string record;
190 pw.getRecordPayload(record); // needs to be called before commit()
191 return record;
192 }
193
194 virtual bool operator==(const DNSRecordContent& rhs) const
195 {
196 return typeid(*this)==typeid(rhs) && this->getZoneRepresentation() == rhs.getZoneRepresentation();
197 }
198
199 static shared_ptr<DNSRecordContent> unserialize(const DNSName& qname, uint16_t qtype, const string& serialized);
200
201 void doRecordCheck(const struct DNSRecord&){}
202
203 typedef std::shared_ptr<DNSRecordContent> makerfunc_t(const struct DNSRecord& dr, PacketReader& pr);
204 typedef std::shared_ptr<DNSRecordContent> zmakerfunc_t(const string& str);
205
206 static void regist(uint16_t cl, uint16_t ty, makerfunc_t* f, zmakerfunc_t* z, const char* name)
207 {
208 if(f)
209 getTypemap()[make_pair(cl,ty)]=f;
210 if(z)
211 getZmakermap()[make_pair(cl,ty)]=z;
212
213 getT2Namemap().insert(make_pair(make_pair(cl,ty), name));
214 getN2Typemap().insert(make_pair(name, make_pair(cl,ty)));
215 }
216
217 static void unregist(uint16_t cl, uint16_t ty)
218 {
219 pair<uint16_t, uint16_t> key=make_pair(cl, ty);
220 getTypemap().erase(key);
221 getZmakermap().erase(key);
222 }
223
224 static uint16_t TypeToNumber(const string& name)
225 {
226 n2typemap_t::const_iterator iter = getN2Typemap().find(toUpper(name));
227 if(iter != getN2Typemap().end())
228 return iter->second.second;
229
230 if(boost::starts_with(name, "TYPE") || boost::starts_with(name, "type"))
231 return (uint16_t) pdns_stou(name.substr(4));
232
233 throw runtime_error("Unknown DNS type '"+name+"'");
234 }
235
236 static const string NumberToType(uint16_t num, uint16_t classnum=1)
237 {
238 t2namemap_t::const_iterator iter = getT2Namemap().find(make_pair(classnum, num));
239 if(iter == getT2Namemap().end())
240 return "TYPE" + std::to_string(num);
241 // throw runtime_error("Unknown DNS type with numerical id "+std::to_string(num));
242 return iter->second;
243 }
244
245 virtual uint16_t getType() const = 0;
246
247 protected:
248 typedef std::map<std::pair<uint16_t, uint16_t>, makerfunc_t* > typemap_t;
249 typedef std::map<std::pair<uint16_t, uint16_t>, zmakerfunc_t* > zmakermap_t;
250 typedef std::map<std::pair<uint16_t, uint16_t>, string > t2namemap_t;
251 typedef std::map<string, std::pair<uint16_t, uint16_t> > n2typemap_t;
252 static typemap_t& getTypemap();
253 static t2namemap_t& getT2Namemap();
254 static n2typemap_t& getN2Typemap();
255 static zmakermap_t& getZmakermap();
256 };
257
258 struct DNSRecord
259 {
260 DNSRecord() {
261 d_type = 0;
262 d_class = QClass::IN;
263 d_ttl = 0;
264 d_clen = 0;
265 d_place = DNSResourceRecord::ANSWER;
266 }
267 explicit DNSRecord(const DNSResourceRecord& rr);
268 DNSName d_name;
269 std::shared_ptr<DNSRecordContent> d_content;
270 uint16_t d_type;
271 uint16_t d_class;
272 uint32_t d_ttl;
273 uint16_t d_clen;
274 DNSResourceRecord::Place d_place;
275
276 bool operator<(const DNSRecord& rhs) const
277 {
278 if(tie(d_name, d_type, d_class, d_ttl) < tie(rhs.d_name, rhs.d_type, rhs.d_class, rhs.d_ttl))
279 return true;
280
281 if(tie(d_name, d_type, d_class, d_ttl) != tie(rhs.d_name, rhs.d_type, rhs.d_class, rhs.d_ttl))
282 return false;
283
284 string lzrp, rzrp;
285 if(d_content)
286 lzrp=toLower(d_content->getZoneRepresentation());
287 if(rhs.d_content)
288 rzrp=toLower(rhs.d_content->getZoneRepresentation());
289
290 return lzrp < rzrp;
291 }
292
293 // this orders in canonical order and keeps the SOA record on top
294 static bool prettyCompare(const DNSRecord& a, const DNSRecord& b)
295 {
296 auto aType = (a.d_type == QType::SOA) ? 0 : a.d_type;
297 auto bType = (b.d_type == QType::SOA) ? 0 : b.d_type;
298
299 if(a.d_name.canonCompare(b.d_name))
300 return true;
301 if(b.d_name.canonCompare(a.d_name))
302 return false;
303
304 if(tie(aType, a.d_class, a.d_ttl) < tie(bType, b.d_class, b.d_ttl))
305 return true;
306
307 if(tie(aType, a.d_class, a.d_ttl) != tie(bType, b.d_class, b.d_ttl))
308 return false;
309
310 string lzrp, rzrp;
311 if(a.d_content)
312 lzrp=toLower(a.d_content->getZoneRepresentation());
313 if(b.d_content)
314 rzrp=toLower(b.d_content->getZoneRepresentation());
315
316 return lzrp < rzrp;
317 }
318
319
320 bool operator==(const DNSRecord& rhs) const
321 {
322 if(d_type != rhs.d_type || d_class != rhs.d_class || d_name != rhs.d_name)
323 return false;
324
325 return *d_content == *rhs.d_content;
326 }
327 };
328
329 struct DNSZoneRecord
330 {
331 int domain_id{-1};
332 uint8_t scopeMask{0};
333 int signttl{0};
334 DNSName wildcardname;
335 bool auth{true};
336 DNSRecord dr;
337 };
338
339
340 //! This class can be used to parse incoming packets, and is copyable
341 class MOADNSParser : public boost::noncopyable
342 {
343 public:
344 //! Parse from a string
345 MOADNSParser(bool query, const string& buffer) : d_tsigPos(0)
346 {
347 init(query, buffer.c_str(), (unsigned int)buffer.size());
348 }
349
350 //! Parse from a pointer and length
351 MOADNSParser(bool query, const char *packet, unsigned int len) : d_tsigPos(0)
352 {
353 init(query, packet, len);
354 }
355
356 DNSName d_qname;
357 uint16_t d_qclass, d_qtype;
358 //uint8_t d_rcode;
359 dnsheader d_header;
360
361 typedef vector<pair<DNSRecord, uint16_t > > answers_t;
362
363 //! All answers contained in this packet (everything *but* the question section)
364 answers_t d_answers;
365
366 shared_ptr<PacketReader> getPacketReader(uint16_t offset)
367 {
368 shared_ptr<PacketReader> pr(new PacketReader(d_content));
369 pr->d_pos=offset;
370 return pr;
371 }
372
373 uint16_t getTSIGPos() const
374 {
375 return d_tsigPos;
376 }
377 private:
378 void getDnsrecordheader(struct dnsrecordheader &ah);
379 void init(bool query, const char *packet, unsigned int len);
380 vector<uint8_t> d_content;
381 uint16_t d_tsigPos;
382 };
383
384 string simpleCompress(const string& label, const string& root="");
385 void ageDNSPacket(char* packet, size_t length, uint32_t seconds);
386 void ageDNSPacket(std::string& packet, uint32_t seconds);
387 void editDNSPacketTTL(char* packet, size_t length, std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)> visitor);
388 uint32_t getDNSPacketMinTTL(const char* packet, size_t length);
389 uint32_t getDNSPacketLength(const char* packet, size_t length);
390 uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type);
391
392 template<typename T>
393 std::shared_ptr<T> getRR(const DNSRecord& dr)
394 {
395 return std::dynamic_pointer_cast<T>(dr.d_content);
396 }
397
398 #endif