]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsparser.hh
Merge pull request #6424 from chbruyand/rec-cppcheck
[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 #include "iputils.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 #include "namespaces.hh"
57
58 class MOADNSException : public runtime_error
59 {
60 public:
61 MOADNSException(const string& str) : runtime_error(str)
62 {}
63 };
64
65
66 class MOADNSParser;
67
68 class PacketReader
69 {
70 public:
71 PacketReader(const vector<uint8_t>& content)
72 : d_pos(0), d_startrecordpos(0), d_content(content)
73 {
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();
78 not_used = 0;
79 }
80
81 uint32_t get32BitInt();
82 uint16_t get16BitInt();
83 uint8_t get8BitInt();
84
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
139 void xfrName(DNSName &name, bool compress=false, bool noDot=false)
140 {
141 name=getName();
142 }
143
144 void xfrText(string &text, bool multi=false, bool lenField=true)
145 {
146 text=getText(multi, lenField);
147 }
148
149 void xfrUnquotedText(string &text, bool lenField){
150 text=getUnquotedText(lenField);
151 }
152
153 void xfrBlob(string& blob);
154 void xfrBlobNoSpaces(string& blob, int len);
155 void xfrBlob(string& blob, int length);
156 void xfrHexBlob(string& blob, bool keepReading=false);
157
158 static uint16_t get16BitInt(const vector<unsigned char>&content, uint16_t& pos);
159
160 void getDnsrecordheader(struct dnsrecordheader &ah);
161 void copyRecord(vector<unsigned char>& dest, uint16_t len);
162 void copyRecord(unsigned char* dest, uint16_t len);
163
164 DNSName getName();
165 string getText(bool multi, bool lenField);
166 string getUnquotedText(bool lenField);
167
168 uint16_t d_pos;
169
170 bool eof() { return true; };
171 const string getRemaining() const {
172 return "";
173 };
174
175 private:
176 uint16_t d_startrecordpos; // needed for getBlob later on
177 uint16_t d_recordlen; // ditto
178 uint16_t not_used; // Aligns the whole class on 8-byte boundries
179 const vector<uint8_t>& d_content;
180 };
181
182 struct DNSRecord;
183
184 class DNSRecordContent
185 {
186 public:
187 static std::shared_ptr<DNSRecordContent> mastermake(const DNSRecord &dr, PacketReader& pr);
188 static std::shared_ptr<DNSRecordContent> mastermake(const DNSRecord &dr, PacketReader& pr, uint16_t opcode);
189 static std::shared_ptr<DNSRecordContent> mastermake(uint16_t qtype, uint16_t qclass, const string& zone);
190 static std::unique_ptr<DNSRecordContent> makeunique(uint16_t qtype, uint16_t qclass, const string& content);
191
192 virtual std::string getZoneRepresentation(bool noDot=false) const = 0;
193 virtual ~DNSRecordContent() {}
194 virtual void toPacket(DNSPacketWriter& pw)=0;
195 virtual string serialize(const DNSName& qname, bool canonic=false, bool lowerCase=false) // it would rock if this were const, but it is too hard
196 {
197 vector<uint8_t> packet;
198 DNSPacketWriter pw(packet, g_rootdnsname, 1);
199 if(canonic)
200 pw.setCanonic(true);
201
202 if(lowerCase)
203 pw.setLowercase(true);
204
205 pw.startRecord(qname, this->getType());
206 this->toPacket(pw);
207
208 string record;
209 pw.getRecordPayload(record); // needs to be called before commit()
210 return record;
211 }
212
213 virtual bool operator==(const DNSRecordContent& rhs) const
214 {
215 return typeid(*this)==typeid(rhs) && this->getZoneRepresentation() == rhs.getZoneRepresentation();
216 }
217
218 static shared_ptr<DNSRecordContent> unserialize(const DNSName& qname, uint16_t qtype, const string& serialized);
219
220 void doRecordCheck(const struct DNSRecord&){}
221
222 typedef DNSRecordContent* makerfunc_t(const struct DNSRecord& dr, PacketReader& pr);
223 typedef DNSRecordContent* zmakerfunc_t(const string& str);
224
225 static void regist(uint16_t cl, uint16_t ty, makerfunc_t* f, zmakerfunc_t* z, const char* name)
226 {
227 if(f)
228 getTypemap()[make_pair(cl,ty)]=f;
229 if(z)
230 getZmakermap()[make_pair(cl,ty)]=z;
231
232 getT2Namemap().insert(make_pair(make_pair(cl,ty), name));
233 getN2Typemap().insert(make_pair(name, make_pair(cl,ty)));
234 }
235
236 static void unregist(uint16_t cl, uint16_t ty)
237 {
238 pair<uint16_t, uint16_t> key=make_pair(cl, ty);
239 getTypemap().erase(key);
240 getZmakermap().erase(key);
241 }
242
243 static uint16_t TypeToNumber(const string& name)
244 {
245 n2typemap_t::const_iterator iter = getN2Typemap().find(toUpper(name));
246 if(iter != getN2Typemap().end())
247 return iter->second.second;
248
249 if(boost::starts_with(name, "TYPE") || boost::starts_with(name, "type"))
250 return (uint16_t) pdns_stou(name.substr(4));
251
252 throw runtime_error("Unknown DNS type '"+name+"'");
253 }
254
255 static const string NumberToType(uint16_t num, uint16_t classnum=1)
256 {
257 t2namemap_t::const_iterator iter = getT2Namemap().find(make_pair(classnum, num));
258 if(iter == getT2Namemap().end())
259 return "TYPE" + std::to_string(num);
260 // throw runtime_error("Unknown DNS type with numerical id "+std::to_string(num));
261 return iter->second;
262 }
263
264 virtual uint16_t getType() const = 0;
265
266 protected:
267 typedef std::map<std::pair<uint16_t, uint16_t>, makerfunc_t* > typemap_t;
268 typedef std::map<std::pair<uint16_t, uint16_t>, zmakerfunc_t* > zmakermap_t;
269 typedef std::map<std::pair<uint16_t, uint16_t>, string > t2namemap_t;
270 typedef std::map<string, std::pair<uint16_t, uint16_t> > n2typemap_t;
271 static typemap_t& getTypemap();
272 static t2namemap_t& getT2Namemap();
273 static n2typemap_t& getN2Typemap();
274 static zmakermap_t& getZmakermap();
275 };
276
277 struct DNSRecord
278 {
279 DNSRecord() : d_type(0), d_class(QClass::IN), d_ttl(0), d_clen(0), d_place(DNSResourceRecord::ANSWER)
280 {}
281 explicit DNSRecord(const DNSResourceRecord& rr);
282 DNSName d_name;
283 std::shared_ptr<DNSRecordContent> d_content;
284 uint16_t d_type;
285 uint16_t d_class;
286 uint32_t d_ttl;
287 uint16_t d_clen;
288 DNSResourceRecord::Place d_place;
289
290 bool operator<(const DNSRecord& rhs) const
291 {
292 if(tie(d_name, d_type, d_class, d_ttl) < tie(rhs.d_name, rhs.d_type, rhs.d_class, rhs.d_ttl))
293 return true;
294
295 if(tie(d_name, d_type, d_class, d_ttl) != tie(rhs.d_name, rhs.d_type, rhs.d_class, rhs.d_ttl))
296 return false;
297
298 string lzrp, rzrp;
299 if(d_content)
300 lzrp=toLower(d_content->getZoneRepresentation());
301 if(rhs.d_content)
302 rzrp=toLower(rhs.d_content->getZoneRepresentation());
303
304 return lzrp < rzrp;
305 }
306
307 // this orders in canonical order and keeps the SOA record on top
308 static bool prettyCompare(const DNSRecord& a, const DNSRecord& b)
309 {
310 auto aType = (a.d_type == QType::SOA) ? 0 : a.d_type;
311 auto bType = (b.d_type == QType::SOA) ? 0 : b.d_type;
312
313 if(a.d_name.canonCompare(b.d_name))
314 return true;
315 if(b.d_name.canonCompare(a.d_name))
316 return false;
317
318 if(tie(aType, a.d_class, a.d_ttl) < tie(bType, b.d_class, b.d_ttl))
319 return true;
320
321 if(tie(aType, a.d_class, a.d_ttl) != tie(bType, b.d_class, b.d_ttl))
322 return false;
323
324 string lzrp, rzrp;
325 if(a.d_content)
326 lzrp=toLower(a.d_content->getZoneRepresentation());
327 if(b.d_content)
328 rzrp=toLower(b.d_content->getZoneRepresentation());
329
330 return lzrp < rzrp;
331 }
332
333
334 bool operator==(const DNSRecord& rhs) const
335 {
336 if(d_type != rhs.d_type || d_class != rhs.d_class || d_name != rhs.d_name)
337 return false;
338
339 return *d_content == *rhs.d_content;
340 }
341 };
342
343 struct DNSZoneRecord
344 {
345 int domain_id{-1};
346 uint8_t scopeMask{0};
347 int signttl{0};
348 DNSName wildcardname;
349 bool auth{true};
350 DNSRecord dr;
351 };
352
353
354 //! This class can be used to parse incoming packets, and is copyable
355 class MOADNSParser : public boost::noncopyable
356 {
357 public:
358 //! Parse from a string
359 MOADNSParser(bool query, const string& buffer) : d_tsigPos(0)
360 {
361 init(query, buffer.c_str(), (unsigned int)buffer.size());
362 }
363
364 //! Parse from a pointer and length
365 MOADNSParser(bool query, const char *packet, unsigned int len) : d_tsigPos(0)
366 {
367 init(query, packet, len);
368 }
369
370 DNSName d_qname;
371 uint16_t d_qclass, d_qtype;
372 //uint8_t d_rcode;
373 dnsheader d_header;
374
375 typedef vector<pair<DNSRecord, uint16_t > > answers_t;
376
377 //! All answers contained in this packet (everything *but* the question section)
378 answers_t d_answers;
379
380 shared_ptr<PacketReader> getPacketReader(uint16_t offset)
381 {
382 shared_ptr<PacketReader> pr(new PacketReader(d_content));
383 pr->d_pos=offset;
384 return pr;
385 }
386
387 uint16_t getTSIGPos() const
388 {
389 return d_tsigPos;
390 }
391 private:
392 void getDnsrecordheader(struct dnsrecordheader &ah);
393 void init(bool query, const char *packet, unsigned int len);
394 vector<uint8_t> d_content;
395 uint16_t d_tsigPos;
396 };
397
398 string simpleCompress(const string& label, const string& root="");
399 void ageDNSPacket(char* packet, size_t length, uint32_t seconds);
400 void ageDNSPacket(std::string& packet, uint32_t seconds);
401 void editDNSPacketTTL(char* packet, size_t length, std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)> visitor);
402 uint32_t getDNSPacketMinTTL(const char* packet, size_t length);
403 uint32_t getDNSPacketLength(const char* packet, size_t length);
404 uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type);
405
406 template<typename T>
407 std::shared_ptr<T> getRR(const DNSRecord& dr)
408 {
409 return std::dynamic_pointer_cast<T>(dr.d_content);
410 }
411
412 #endif