/src/pdns/pdns/dnsparser.hh
Line | Count | Source (jump to first uncovered line) |
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 | | #pragma once |
23 | | #include <atomic> |
24 | | #include <map> |
25 | | #include <sstream> |
26 | | #include <stdexcept> |
27 | | #include <iostream> |
28 | | #include <unordered_set> |
29 | | #include <utility> |
30 | | #include <vector> |
31 | | #include <cerrno> |
32 | | // #include <netinet/in.h> |
33 | | #include "misc.hh" |
34 | | |
35 | | #include "dns.hh" |
36 | | #include "dnswriter.hh" |
37 | | #include "dnsname.hh" |
38 | | #include "noinitvector.hh" |
39 | | #include "pdnsexception.hh" |
40 | | #include "iputils.hh" |
41 | | #include "svc-records.hh" |
42 | | |
43 | | /** DNS records have three representations: |
44 | | 1) in the packet |
45 | | 2) parsed in a class, ready for use |
46 | | 3) in the zone |
47 | | |
48 | | We should implement bidirectional transitions between 1&2 and 2&3. |
49 | | Currently we have: 1 -> 2 |
50 | | 2 -> 3 |
51 | | |
52 | | We can add: 2 -> 1 easily by reversing the packetwriter |
53 | | And we might be able to reverse 2 -> 3 as well |
54 | | */ |
55 | | |
56 | | #include "namespaces.hh" |
57 | | |
58 | | class MOADNSException : public runtime_error |
59 | | { |
60 | | public: |
61 | | MOADNSException(const string& str) : runtime_error(str) |
62 | 0 | {} |
63 | | }; |
64 | | |
65 | | |
66 | | class MOADNSParser; |
67 | | |
68 | | class PacketReader |
69 | | { |
70 | | public: |
71 | | PacketReader(const std::string_view& content, uint16_t initialPos=sizeof(dnsheader), bool internalRepresentation = false) |
72 | | : d_pos(initialPos), d_startrecordpos(initialPos), d_content(content), d_internal(internalRepresentation) |
73 | 0 | { |
74 | 0 | if(content.size() > std::numeric_limits<uint16_t>::max()) |
75 | 0 | throw std::out_of_range("packet too large"); |
76 | 0 |
|
77 | 0 | d_recordlen = (uint16_t) content.size(); |
78 | 0 | not_used = 0; |
79 | 0 | } |
80 | | |
81 | | uint32_t get32BitInt(); |
82 | | uint16_t get16BitInt(); |
83 | | uint8_t get8BitInt(); |
84 | | |
85 | | void xfrNodeOrLocatorID(NodeOrLocatorID& val); |
86 | | void xfr48BitInt(uint64_t& val); |
87 | | |
88 | | void xfr32BitInt(uint32_t& val) |
89 | 0 | { |
90 | 0 | val=get32BitInt(); |
91 | 0 | } |
92 | | |
93 | | void xfrIP(uint32_t& val) |
94 | 0 | { |
95 | 0 | xfr32BitInt(val); |
96 | 0 | val=htonl(val); |
97 | 0 | } |
98 | | |
99 | 0 | void xfrIP6(std::string &val) { |
100 | 0 | xfrBlob(val, 16); |
101 | 0 | } |
102 | | |
103 | 0 | void xfrCAWithoutPort(uint8_t version, ComboAddress &val) { |
104 | 0 | string blob; |
105 | 0 | if (version == 4) xfrBlob(blob, 4); |
106 | 0 | else if (version == 6) xfrBlob(blob, 16); |
107 | 0 | else throw runtime_error("invalid IP protocol"); |
108 | 0 | val = makeComboAddressFromRaw(version, blob); |
109 | 0 | } |
110 | | |
111 | 0 | void xfrCAPort(ComboAddress &val) { |
112 | 0 | uint16_t port; |
113 | 0 | xfr16BitInt(port); |
114 | 0 | val.sin4.sin_port = port; |
115 | 0 | } |
116 | | |
117 | | void xfrTime(uint32_t& val) |
118 | 0 | { |
119 | 0 | xfr32BitInt(val); |
120 | 0 | } |
121 | | |
122 | | |
123 | | void xfr16BitInt(uint16_t& val) |
124 | 0 | { |
125 | 0 | val=get16BitInt(); |
126 | 0 | } |
127 | | |
128 | | void xfrType(uint16_t& val) |
129 | 0 | { |
130 | 0 | xfr16BitInt(val); |
131 | 0 | } |
132 | | |
133 | | |
134 | | void xfr8BitInt(uint8_t& val) |
135 | 0 | { |
136 | 0 | val=get8BitInt(); |
137 | 0 | } |
138 | | |
139 | | void xfrName(DNSName& name, bool /* compress */ = false) |
140 | 0 | { |
141 | 0 | name = getName(); |
142 | 0 | } |
143 | | |
144 | | void xfrText(string &text, bool multi=false, bool lenField=true) |
145 | 0 | { |
146 | 0 | text=getText(multi, lenField); |
147 | 0 | } |
148 | | |
149 | 0 | void xfrUnquotedText(string &text, bool lenField){ |
150 | 0 | text=getUnquotedText(lenField); |
151 | 0 | } |
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 | | void xfrSvcParamKeyVals(set<SvcParam> &kvs); |
158 | | |
159 | | void getDnsrecordheader(struct dnsrecordheader &ah); |
160 | | void copyRecord(vector<unsigned char>& dest, uint16_t len); |
161 | | void copyRecord(unsigned char* dest, uint16_t len); |
162 | | |
163 | | DNSName getName(); |
164 | | string getText(bool multi, bool lenField); |
165 | | string getUnquotedText(bool lenField); |
166 | | |
167 | | |
168 | 0 | bool eof() { return true; }; |
169 | 0 | const string getRemaining() const { |
170 | 0 | return ""; |
171 | 0 | }; |
172 | | |
173 | | uint16_t getPosition() const |
174 | 0 | { |
175 | 0 | return d_pos; |
176 | 0 | } |
177 | | |
178 | | void skip(uint16_t n) |
179 | 0 | { |
180 | 0 | d_pos += n; |
181 | 0 | } |
182 | | |
183 | | private: |
184 | | uint16_t d_pos; |
185 | | uint16_t d_startrecordpos; // needed for getBlob later on |
186 | | uint16_t d_recordlen; // ditto |
187 | | uint16_t not_used; // Aligns the whole class on 8-byte boundaries |
188 | | const std::string_view d_content; |
189 | | bool d_internal; |
190 | | }; |
191 | | |
192 | | struct DNSRecord; |
193 | | |
194 | | class DNSRecordContent |
195 | | { |
196 | | public: |
197 | | static std::shared_ptr<DNSRecordContent> make(const DNSRecord& dr, PacketReader& pr); |
198 | | static std::shared_ptr<DNSRecordContent> make(const DNSRecord& dr, PacketReader& pr, uint16_t opcode); |
199 | | static std::shared_ptr<DNSRecordContent> make(uint16_t qtype, uint16_t qclass, const string& zone); |
200 | | static string upgradeContent(const DNSName& qname, const QType& qtype, const string& content); |
201 | | |
202 | | virtual std::string getZoneRepresentation(bool noDot=false) const = 0; |
203 | | virtual ~DNSRecordContent() = default; |
204 | | virtual void toPacket(DNSPacketWriter& pw) const = 0; |
205 | | // returns the wire format of the content or the full record, possibly including compressed pointers pointing to the owner name (unless canonic or lowerCase are set) |
206 | | [[nodiscard]] string serialize(const DNSName& qname, bool canonic = false, bool lowerCase = false, bool full = false) const |
207 | 0 | { |
208 | 0 | vector<uint8_t> packet; |
209 | 0 | DNSPacketWriter packetWriter(packet, g_rootdnsname, QType::A); |
210 | 0 |
|
211 | 0 | if (canonic) { |
212 | 0 | packetWriter.setCanonic(true); |
213 | 0 | } |
214 | 0 | if (lowerCase) { |
215 | 0 | packetWriter.setLowercase(true); |
216 | 0 | } |
217 | 0 |
|
218 | 0 | packetWriter.startRecord(qname, getType()); |
219 | 0 | toPacket(packetWriter); |
220 | 0 |
|
221 | 0 | string record; |
222 | 0 | if (full) { |
223 | 0 | packetWriter.getWireFormatContent(record); // needs to be called before commit() |
224 | 0 | } else { |
225 | 0 | packetWriter.getRecordPayload(record); // needs to be called before commit() |
226 | 0 | } |
227 | 0 | return record; |
228 | 0 | } |
229 | | |
230 | | virtual bool operator==(const DNSRecordContent& rhs) const |
231 | 0 | { |
232 | 0 | return typeid(*this)==typeid(rhs) && this->getZoneRepresentation() == rhs.getZoneRepresentation(); |
233 | 0 | } |
234 | | |
235 | | // parse the content in wire format, possibly including compressed pointers pointing to the owner name. |
236 | | // internalRepresentation is set when the data comes from an internal source, |
237 | | // such as the LMDB backend. |
238 | | static shared_ptr<DNSRecordContent> deserialize(const DNSName& qname, uint16_t qtype, const string& serialized, uint16_t qclass=QClass::IN, bool internalRepresentation = false); |
239 | | |
240 | 0 | void doRecordCheck(const struct DNSRecord&){} |
241 | | |
242 | | typedef std::shared_ptr<DNSRecordContent> makerfunc_t(const struct DNSRecord& dr, PacketReader& pr); |
243 | | typedef std::shared_ptr<DNSRecordContent> zmakerfunc_t(const string& str); |
244 | | |
245 | | static void regist(uint16_t cl, uint16_t ty, makerfunc_t* f, zmakerfunc_t* z, const char* name) |
246 | 0 | { |
247 | 0 | assert(!d_locked.load()); // NOLINT: it's the API |
248 | 0 | if(f) |
249 | 0 | getTypemap()[pair(cl,ty)]=f; |
250 | 0 | if(z) |
251 | 0 | getZmakermap()[pair(cl,ty)]=z; |
252 | 0 |
|
253 | 0 | getT2Namemap().emplace(pair(cl, ty), name); |
254 | 0 | getN2Typemap().emplace(name, pair(cl, ty)); |
255 | 0 | } |
256 | | |
257 | | static bool isUnknownType(const string& name) |
258 | 0 | { |
259 | 0 | return boost::starts_with(name, "TYPE") || boost::starts_with(name, "type"); |
260 | 0 | } |
261 | | |
262 | | static uint16_t TypeToNumber(const string& name) |
263 | 0 | { |
264 | 0 | n2typemap_t::const_iterator iter = getN2Typemap().find(toUpper(name)); |
265 | 0 | if(iter != getN2Typemap().end()) |
266 | 0 | return iter->second.second; |
267 | 0 |
|
268 | 0 | if (isUnknownType(name)) { |
269 | 0 | return pdns::checked_stoi<uint16_t>(name.substr(4)); |
270 | 0 | } |
271 | 0 |
|
272 | 0 | throw runtime_error("Unknown DNS type '"+name+"'"); |
273 | 0 | } |
274 | | |
275 | | static const string NumberToType(uint16_t num, uint16_t classnum = QClass::IN) |
276 | 0 | { |
277 | 0 | auto iter = getT2Namemap().find(pair(classnum, num)); |
278 | 0 | if(iter == getT2Namemap().end()) |
279 | 0 | return "TYPE" + std::to_string(num); |
280 | 0 | // throw runtime_error("Unknown DNS type with numerical id "+std::to_string(num)); |
281 | 0 | return iter->second; |
282 | 0 | } |
283 | | |
284 | | /** |
285 | | * \brief Return whether we have implemented a content representation for this type |
286 | | */ |
287 | | static bool isRegisteredType(uint16_t rtype, uint16_t rclass = QClass::IN); |
288 | | |
289 | | virtual uint16_t getType() const = 0; |
290 | | |
291 | | static void lock() |
292 | 0 | { |
293 | 0 | d_locked.store(true); |
294 | 0 | } |
295 | | |
296 | | [[nodiscard]] virtual size_t sizeEstimate() const = 0; |
297 | | |
298 | | protected: |
299 | | typedef std::map<std::pair<uint16_t, uint16_t>, makerfunc_t* > typemap_t; |
300 | | typedef std::map<std::pair<uint16_t, uint16_t>, zmakerfunc_t* > zmakermap_t; |
301 | | typedef std::map<std::pair<uint16_t, uint16_t>, string > t2namemap_t; |
302 | | typedef std::map<string, std::pair<uint16_t, uint16_t> > n2typemap_t; |
303 | | static typemap_t& getTypemap(); |
304 | | static t2namemap_t& getT2Namemap(); |
305 | | static n2typemap_t& getN2Typemap(); |
306 | | static zmakermap_t& getZmakermap(); |
307 | | static std::atomic<bool> d_locked; |
308 | | }; |
309 | | |
310 | | struct DNSRecord |
311 | | { |
312 | | DNSRecord() : |
313 | | d_class(QClass::IN) |
314 | 0 | {} |
315 | | explicit DNSRecord(const DNSResourceRecord& rr); |
316 | | DNSRecord(const std::string& name, |
317 | | std::shared_ptr<DNSRecordContent> content, |
318 | | const uint16_t type, |
319 | | const uint16_t qclass = QClass::IN, |
320 | | const uint32_t ttl = 86400, |
321 | | const uint16_t clen = 0, |
322 | | const DNSResourceRecord::Place place = DNSResourceRecord::ANSWER) : |
323 | | d_name(DNSName(name)), |
324 | | d_content(std::move(content)), |
325 | | d_type(type), |
326 | | d_class(qclass), |
327 | | d_ttl(ttl), |
328 | | d_clen(clen), |
329 | 0 | d_place(place) {} |
330 | | |
331 | | DNSName d_name; |
332 | | private: |
333 | | std::shared_ptr<const DNSRecordContent> d_content; |
334 | | public: |
335 | | uint16_t d_type{}; |
336 | | uint16_t d_class{}; |
337 | | uint32_t d_ttl{}; |
338 | | uint16_t d_clen{}; |
339 | | DNSResourceRecord::Place d_place{DNSResourceRecord::ANSWER}; |
340 | | |
341 | | [[nodiscard]] std::string print(const std::string& indent = "") const |
342 | 0 | { |
343 | 0 | std::stringstream s; |
344 | 0 | s << indent << "Content = " << d_content->getZoneRepresentation() << std::endl; |
345 | 0 | s << indent << "Type = " << d_type << std::endl; |
346 | 0 | s << indent << "Class = " << d_class << std::endl; |
347 | 0 | s << indent << "TTL = " << d_ttl << std::endl; |
348 | 0 | s << indent << "clen = " << d_clen << std::endl; |
349 | 0 | s << indent << "Place = " << std::to_string(d_place) << std::endl; |
350 | 0 | return s.str(); |
351 | 0 | } |
352 | | |
353 | | [[nodiscard]] std::string toString() const |
354 | 0 | { |
355 | 0 | std::string ret(d_name.toLogString()); |
356 | 0 | ret += '|'; |
357 | 0 | ret += QType(d_type).toString(); |
358 | 0 | ret += '|'; |
359 | 0 | ret += getContent()->getZoneRepresentation(); |
360 | 0 | return ret; |
361 | 0 | } |
362 | | |
363 | | void setContent(const std::shared_ptr<const DNSRecordContent>& content) |
364 | 0 | { |
365 | 0 | d_content = content; |
366 | 0 | } |
367 | | |
368 | | void setContent(std::shared_ptr<const DNSRecordContent>&& content) |
369 | 0 | { |
370 | 0 | d_content = std::move(content); |
371 | 0 | } |
372 | | |
373 | | [[nodiscard]] const std::shared_ptr<const DNSRecordContent>& getContent() const |
374 | 0 | { |
375 | 0 | return d_content; |
376 | 0 | } |
377 | | |
378 | | bool operator<(const DNSRecord& rhs) const |
379 | 0 | { |
380 | 0 | if(std::tie(d_name, d_type, d_class, d_ttl) < std::tie(rhs.d_name, rhs.d_type, rhs.d_class, rhs.d_ttl)) |
381 | 0 | return true; |
382 | 0 |
|
383 | 0 | if(std::tie(d_name, d_type, d_class, d_ttl) != std::tie(rhs.d_name, rhs.d_type, rhs.d_class, rhs.d_ttl)) |
384 | 0 | return false; |
385 | 0 |
|
386 | 0 | string lzrp, rzrp; |
387 | 0 | if(d_content) |
388 | 0 | lzrp=toLower(d_content->getZoneRepresentation()); |
389 | 0 | if(rhs.d_content) |
390 | 0 | rzrp=toLower(rhs.d_content->getZoneRepresentation()); |
391 | 0 |
|
392 | 0 | return lzrp < rzrp; |
393 | 0 | } |
394 | | |
395 | | // this orders in canonical order and keeps the SOA record on top |
396 | | static bool prettyCompare(const DNSRecord& a, const DNSRecord& b) |
397 | 0 | { |
398 | 0 | auto aType = (a.d_type == QType::SOA) ? 0 : a.d_type; |
399 | 0 | auto bType = (b.d_type == QType::SOA) ? 0 : b.d_type; |
400 | 0 |
|
401 | 0 | if(a.d_name.canonCompare(b.d_name)) |
402 | 0 | return true; |
403 | 0 | if(b.d_name.canonCompare(a.d_name)) |
404 | 0 | return false; |
405 | 0 |
|
406 | 0 | if(std::tie(aType, a.d_class, a.d_ttl) < std::tie(bType, b.d_class, b.d_ttl)) |
407 | 0 | return true; |
408 | 0 |
|
409 | 0 | if(std::tie(aType, a.d_class, a.d_ttl) != std::tie(bType, b.d_class, b.d_ttl)) |
410 | 0 | return false; |
411 | 0 |
|
412 | 0 | string lzrp, rzrp; |
413 | 0 | if(a.d_content) |
414 | 0 | lzrp = a.d_content->getZoneRepresentation(); |
415 | 0 | if(b.d_content) |
416 | 0 | rzrp = b.d_content->getZoneRepresentation(); |
417 | 0 |
|
418 | 0 | switch (a.d_type) { |
419 | 0 | case QType::TXT: |
420 | 0 | case QType::SPF: |
421 | 0 | #if !defined(RECURSOR) |
422 | 0 | case QType::LUA: |
423 | 0 | #endif |
424 | 0 | return lzrp < rzrp; |
425 | 0 | default: |
426 | 0 | return toLower(lzrp) < toLower(rzrp); |
427 | 0 | } |
428 | 0 | } |
429 | | |
430 | | bool operator==(const DNSRecord& rhs) const |
431 | 0 | { |
432 | 0 | if (d_type != rhs.d_type || d_class != rhs.d_class || d_name != rhs.d_name) { |
433 | 0 | return false; |
434 | 0 | } |
435 | 0 |
|
436 | 0 | return *d_content == *rhs.d_content; |
437 | 0 | } |
438 | | |
439 | | [[nodiscard]] size_t sizeEstimate() const |
440 | 0 | { |
441 | 0 | return sizeof(*this) + d_name.sizeEstimate() + (d_content ? d_content->sizeEstimate() : 0U); |
442 | 0 | } |
443 | | }; |
444 | | |
445 | | struct DNSZoneRecord |
446 | | { |
447 | | domainid_t domain_id{UnknownDomainID}; |
448 | | uint8_t scopeMask{0}; |
449 | | int signttl{0}; |
450 | | DNSName wildcardname; |
451 | | bool auth{true}; |
452 | | bool disabled{false}; |
453 | | DNSRecord dr; |
454 | | |
455 | 0 | bool operator<(const DNSZoneRecord& other) const { |
456 | 0 | return dr.d_ttl < other.dr.d_ttl; |
457 | 0 | } |
458 | | }; |
459 | | |
460 | | class UnknownRecordContent : public DNSRecordContent |
461 | | { |
462 | | public: |
463 | | UnknownRecordContent(const DNSRecord& dr, PacketReader& pr) |
464 | | : d_dr(dr) |
465 | 0 | { |
466 | 0 | pr.copyRecord(d_record, dr.d_clen); |
467 | 0 | } |
468 | | |
469 | | UnknownRecordContent(const string& zone); |
470 | | |
471 | | string getZoneRepresentation(bool noDot) const override; |
472 | | void toPacket(DNSPacketWriter& pw) const override; |
473 | | uint16_t getType() const override |
474 | 0 | { |
475 | 0 | return d_dr.d_type; |
476 | 0 | } |
477 | | |
478 | | const vector<uint8_t>& getRawContent() const |
479 | 0 | { |
480 | 0 | return d_record; |
481 | 0 | } |
482 | | |
483 | | [[nodiscard]] size_t sizeEstimate() const override |
484 | 0 | { |
485 | 0 | return sizeof(*this) + d_dr.sizeEstimate() + d_record.size(); |
486 | 0 | } |
487 | | |
488 | | private: |
489 | | DNSRecord d_dr; |
490 | | vector<uint8_t> d_record; |
491 | | }; |
492 | | |
493 | | //! This class can be used to parse incoming packets, and is copyable |
494 | | class MOADNSParser : public boost::noncopyable |
495 | | { |
496 | | public: |
497 | | //! Parse from a string |
498 | | MOADNSParser(bool query, const string& buffer): d_tsigPos(0) |
499 | 0 | { |
500 | 0 | init(query, buffer); |
501 | 0 | } |
502 | | |
503 | | //! Parse from a pointer and length |
504 | | MOADNSParser(bool query, const char *packet, unsigned int len) : d_tsigPos(0) |
505 | 0 | { |
506 | 0 | init(query, std::string_view(packet, len)); |
507 | 0 | } |
508 | | |
509 | | DNSName d_qname; |
510 | | uint16_t d_qclass, d_qtype; |
511 | | dnsheader d_header; |
512 | | |
513 | | using answers_t = vector<DNSRecord>; |
514 | | |
515 | | //! All answers contained in this packet (everything *but* the question section) |
516 | | answers_t d_answers; |
517 | | |
518 | | uint16_t getTSIGPos() const |
519 | 0 | { |
520 | 0 | return d_tsigPos; |
521 | 0 | } |
522 | | |
523 | | bool hasEDNS() const; |
524 | | |
525 | | private: |
526 | | void init(bool query, const std::string_view& packet); |
527 | | uint16_t d_tsigPos; |
528 | | }; |
529 | | |
530 | | string simpleCompress(const string& label, const string& root=""); |
531 | | void ageDNSPacket(char* packet, size_t length, uint32_t seconds, const dnsheader_aligned&); |
532 | | void ageDNSPacket(std::string& packet, uint32_t seconds, const dnsheader_aligned&); |
533 | | void editDNSPacketTTL(char* packet, size_t length, const std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)>& visitor); |
534 | | void clearDNSPacketRecordTypes(vector<uint8_t>& packet, const std::unordered_set<QType>& qtypes); |
535 | | void clearDNSPacketRecordTypes(PacketBuffer& packet, const std::unordered_set<QType>& qtypes); |
536 | | void clearDNSPacketRecordTypes(char* packet, size_t& length, const std::unordered_set<QType>& qtypes); |
537 | | uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA=nullptr); |
538 | | uint32_t getDNSPacketLength(const char* packet, size_t length); |
539 | | uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type); |
540 | | bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z); |
541 | | /* call the visitor for every records in the answer, authority and additional sections, passing the section, class, type, ttl, rdatalength and rdata |
542 | | to the visitor. Stops whenever the visitor returns false or at the end of the packet */ |
543 | | bool visitDNSPacket(const std::string_view& packet, const std::function<bool(uint8_t, uint16_t, uint16_t, uint32_t, uint16_t, const char*)>& visitor); |
544 | | |
545 | | template<typename T> |
546 | | std::shared_ptr<const T> getRR(const DNSRecord& dr) |
547 | | { |
548 | | return std::dynamic_pointer_cast<const T>(dr.getContent()); |
549 | | } |
550 | | |
551 | | /** Simple DNSPacketMangler. Ritual is: get a pointer into the packet and moveOffset() to beyond your needs |
552 | | * If you survive that, feel free to read from the pointer */ |
553 | | class DNSPacketMangler |
554 | | { |
555 | | public: |
556 | | explicit DNSPacketMangler(std::string& packet) |
557 | | : d_packet(packet.data()), d_length(packet.length()), d_notyouroffset(12), d_offset(d_notyouroffset) |
558 | 0 | {} |
559 | | DNSPacketMangler(char* packet, size_t length) |
560 | | : d_packet(packet), d_length(length), d_notyouroffset(12), d_offset(d_notyouroffset) |
561 | 0 | {} |
562 | | |
563 | | /*! Advances past a wire-format domain name |
564 | | * The name is not checked for adherence to length restrictions. |
565 | | * Compression pointers are not followed. |
566 | | */ |
567 | | void skipDomainName() |
568 | 0 | { |
569 | 0 | uint8_t len; |
570 | 0 | while((len=get8BitInt())) { |
571 | 0 | if(len >= 0xc0) { // extended label |
572 | 0 | get8BitInt(); |
573 | 0 | return; |
574 | 0 | } |
575 | 0 | skipBytes(len); |
576 | 0 | } |
577 | 0 | } |
578 | | |
579 | | void skipBytes(uint16_t bytes) |
580 | 0 | { |
581 | 0 | moveOffset(bytes); |
582 | 0 | } |
583 | | void rewindBytes(uint16_t by) |
584 | 0 | { |
585 | 0 | rewindOffset(by); |
586 | 0 | } |
587 | | uint32_t get32BitInt() |
588 | 0 | { |
589 | 0 | const char* p = d_packet + d_offset; |
590 | 0 | moveOffset(4); |
591 | 0 | uint32_t ret; |
592 | 0 | memcpy(&ret, p, sizeof(ret)); |
593 | 0 | return ntohl(ret); |
594 | 0 | } |
595 | | uint16_t get16BitInt() |
596 | 0 | { |
597 | 0 | const char* p = d_packet + d_offset; |
598 | 0 | moveOffset(2); |
599 | 0 | uint16_t ret; |
600 | 0 | memcpy(&ret, p, sizeof(ret)); |
601 | 0 | return ntohs(ret); |
602 | 0 | } |
603 | | |
604 | | uint8_t get8BitInt() |
605 | 0 | { |
606 | 0 | const char* p = d_packet + d_offset; |
607 | 0 | moveOffset(1); |
608 | 0 | return *p; |
609 | 0 | } |
610 | | |
611 | | void skipRData() |
612 | 0 | { |
613 | 0 | int toskip = get16BitInt(); |
614 | 0 | moveOffset(toskip); |
615 | 0 | } |
616 | | |
617 | | void decreaseAndSkip32BitInt(uint32_t decrease) |
618 | 0 | { |
619 | 0 | const char *p = d_packet + d_offset; |
620 | 0 | moveOffset(4); |
621 | 0 |
|
622 | 0 | uint32_t tmp; |
623 | 0 | memcpy(&tmp, p, sizeof(tmp)); |
624 | 0 | tmp = ntohl(tmp); |
625 | 0 | if (tmp > decrease) { |
626 | 0 | tmp -= decrease; |
627 | 0 | } else { |
628 | 0 | tmp = 0; |
629 | 0 | } |
630 | 0 | tmp = htonl(tmp); |
631 | 0 | memcpy(d_packet + d_offset-4, (const char*)&tmp, sizeof(tmp)); |
632 | 0 | } |
633 | | |
634 | | void setAndSkip32BitInt(uint32_t value) |
635 | 0 | { |
636 | 0 | moveOffset(4); |
637 | 0 |
|
638 | 0 | value = htonl(value); |
639 | 0 | memcpy(d_packet + d_offset-4, (const char*)&value, sizeof(value)); |
640 | 0 | } |
641 | | |
642 | | uint32_t getOffset() const |
643 | 0 | { |
644 | 0 | return d_offset; |
645 | 0 | } |
646 | | |
647 | | private: |
648 | | void moveOffset(uint16_t by) |
649 | 0 | { |
650 | 0 | d_notyouroffset += by; |
651 | 0 | if(d_notyouroffset > d_length) |
652 | 0 | throw std::out_of_range("dns packet out of range: "+std::to_string(d_notyouroffset) +" > " |
653 | 0 | + std::to_string(d_length) ); |
654 | 0 | } |
655 | | |
656 | | void rewindOffset(uint16_t by) |
657 | 0 | { |
658 | 0 | if(d_notyouroffset < by) |
659 | 0 | throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < " |
660 | 0 | + std::to_string(by)); |
661 | 0 | d_notyouroffset -= by; |
662 | 0 | if(d_notyouroffset < 12) |
663 | 0 | throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < " |
664 | 0 | + std::to_string(12)); |
665 | 0 | } |
666 | | |
667 | | char* d_packet; |
668 | | size_t d_length; |
669 | | |
670 | | uint32_t d_notyouroffset; // only 'moveOffset' can touch this |
671 | | const uint32_t& d_offset; // look.. but don't touch |
672 | | }; |