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