/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 <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 | | MOADNSException(const string& str) : runtime_error(str) |
61 | 0 | {} |
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 | | : d_pos(initialPos), d_startrecordpos(initialPos), d_content(content) |
72 | 0 | { |
73 | 0 | if(content.size() > std::numeric_limits<uint16_t>::max()) |
74 | 0 | throw std::out_of_range("packet too large"); |
75 | | |
76 | 0 | d_recordlen = (uint16_t) content.size(); |
77 | 0 | not_used = 0; |
78 | 0 | } |
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 | 0 | { |
89 | 0 | val=get32BitInt(); |
90 | 0 | } |
91 | | |
92 | | void xfrIP(uint32_t& val) |
93 | 0 | { |
94 | 0 | xfr32BitInt(val); |
95 | 0 | val=htonl(val); |
96 | 0 | } |
97 | | |
98 | 0 | void xfrIP6(std::string &val) { |
99 | 0 | xfrBlob(val, 16); |
100 | 0 | } |
101 | | |
102 | 0 | void xfrCAWithoutPort(uint8_t version, ComboAddress &val) { |
103 | 0 | string blob; |
104 | 0 | if (version == 4) xfrBlob(blob, 4); |
105 | 0 | else if (version == 6) xfrBlob(blob, 16); |
106 | 0 | else throw runtime_error("invalid IP protocol"); |
107 | 0 | val = makeComboAddressFromRaw(version, blob); |
108 | 0 | } |
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 | 0 | { |
118 | 0 | xfr32BitInt(val); |
119 | 0 | } |
120 | | |
121 | | |
122 | | void xfr16BitInt(uint16_t& val) |
123 | 0 | { |
124 | 0 | val=get16BitInt(); |
125 | 0 | } |
126 | | |
127 | | void xfrType(uint16_t& val) |
128 | 0 | { |
129 | 0 | xfr16BitInt(val); |
130 | 0 | } |
131 | | |
132 | | |
133 | | void xfr8BitInt(uint8_t& val) |
134 | 0 | { |
135 | 0 | val=get8BitInt(); |
136 | 0 | } |
137 | | |
138 | | void xfrName(DNSName& name, bool /* compress */ = false, bool /* noDot */ = false) |
139 | 0 | { |
140 | 0 | name = getName(); |
141 | 0 | } |
142 | | |
143 | | void xfrText(string &text, bool multi=false, bool lenField=true) |
144 | 0 | { |
145 | 0 | text=getText(multi, lenField); |
146 | 0 | } |
147 | | |
148 | 0 | void xfrUnquotedText(string &text, bool lenField){ |
149 | 0 | text=getUnquotedText(lenField); |
150 | 0 | } |
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 | 0 | bool eof() { return true; }; |
168 | 0 | const string getRemaining() const { |
169 | 0 | return ""; |
170 | 0 | }; |
171 | | |
172 | | uint16_t getPosition() const |
173 | 0 | { |
174 | 0 | return d_pos; |
175 | 0 | } |
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> 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); |
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 | 0 | virtual ~DNSRecordContent() {} |
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 | 0 | 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 | 110 | { |
237 | 110 | if(f) |
238 | 107 | getTypemap()[pair(cl,ty)]=f; |
239 | 110 | if(z) |
240 | 107 | getZmakermap()[pair(cl,ty)]=z; |
241 | | |
242 | 110 | getT2Namemap().emplace(pair(cl, ty), name); |
243 | 110 | getN2Typemap().emplace(name, pair(cl, ty)); |
244 | 110 | } |
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 | 2.78k | { |
255 | 2.78k | return boost::starts_with(name, "TYPE") || boost::starts_with(name, "type"); |
256 | 2.78k | } |
257 | | |
258 | | static uint16_t TypeToNumber(const string& name) |
259 | 153k | { |
260 | 153k | n2typemap_t::const_iterator iter = getN2Typemap().find(toUpper(name)); |
261 | 153k | if(iter != getN2Typemap().end()) |
262 | 150k | return iter->second.second; |
263 | | |
264 | 2.78k | if (isUnknownType(name)) { |
265 | 2.26k | return pdns::checked_stoi<uint16_t>(name.substr(4)); |
266 | 2.26k | } |
267 | | |
268 | 511 | throw runtime_error("Unknown DNS type '"+name+"'"); |
269 | 2.78k | } |
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 | | // 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 | | d_class(QClass::IN) |
302 | 0 | {} |
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 | | void setContent(const std::shared_ptr<const DNSRecordContent>& content) |
342 | 0 | { |
343 | 0 | d_content = content; |
344 | 0 | } |
345 | | |
346 | | void setContent(std::shared_ptr<const DNSRecordContent>&& content) |
347 | 0 | { |
348 | 0 | d_content = std::move(content); |
349 | 0 | } |
350 | | |
351 | | [[nodiscard]] const std::shared_ptr<const DNSRecordContent>& getContent() const |
352 | 0 | { |
353 | 0 | return d_content; |
354 | 0 | } |
355 | | |
356 | | bool operator<(const DNSRecord& rhs) const |
357 | 0 | { |
358 | 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)) |
359 | 0 | return true; |
360 | 0 |
|
361 | 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)) |
362 | 0 | return false; |
363 | 0 |
|
364 | 0 | string lzrp, rzrp; |
365 | 0 | if(d_content) |
366 | 0 | lzrp=toLower(d_content->getZoneRepresentation()); |
367 | 0 | if(rhs.d_content) |
368 | 0 | rzrp=toLower(rhs.d_content->getZoneRepresentation()); |
369 | 0 |
|
370 | 0 | return lzrp < rzrp; |
371 | 0 | } |
372 | | |
373 | | // this orders in canonical order and keeps the SOA record on top |
374 | | static bool prettyCompare(const DNSRecord& a, const DNSRecord& b) |
375 | 0 | { |
376 | 0 | auto aType = (a.d_type == QType::SOA) ? 0 : a.d_type; |
377 | 0 | auto bType = (b.d_type == QType::SOA) ? 0 : b.d_type; |
378 | 0 |
|
379 | 0 | if(a.d_name.canonCompare(b.d_name)) |
380 | 0 | return true; |
381 | 0 | if(b.d_name.canonCompare(a.d_name)) |
382 | 0 | return false; |
383 | 0 |
|
384 | 0 | if(std::tie(aType, a.d_class, a.d_ttl) < std::tie(bType, b.d_class, b.d_ttl)) |
385 | 0 | return true; |
386 | 0 |
|
387 | 0 | if(std::tie(aType, a.d_class, a.d_ttl) != std::tie(bType, b.d_class, b.d_ttl)) |
388 | 0 | return false; |
389 | 0 |
|
390 | 0 | string lzrp, rzrp; |
391 | 0 | if(a.d_content) |
392 | 0 | lzrp = a.d_content->getZoneRepresentation(); |
393 | 0 | if(b.d_content) |
394 | 0 | rzrp = b.d_content->getZoneRepresentation(); |
395 | 0 |
|
396 | 0 | switch (a.d_type) { |
397 | 0 | case QType::TXT: |
398 | 0 | case QType::SPF: |
399 | 0 | #if !defined(RECURSOR) |
400 | 0 | case QType::LUA: |
401 | 0 | #endif |
402 | 0 | return lzrp < rzrp; |
403 | 0 | default: |
404 | 0 | return toLower(lzrp) < toLower(rzrp); |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | | bool operator==(const DNSRecord& rhs) const |
409 | 0 | { |
410 | 0 | if (d_type != rhs.d_type || d_class != rhs.d_class || d_name != rhs.d_name) { |
411 | 0 | return false; |
412 | 0 | } |
413 | 0 |
|
414 | 0 | return *d_content == *rhs.d_content; |
415 | 0 | } |
416 | | }; |
417 | | |
418 | | struct DNSZoneRecord |
419 | | { |
420 | | int domain_id{-1}; |
421 | | uint8_t scopeMask{0}; |
422 | | int signttl{0}; |
423 | | DNSName wildcardname; |
424 | | bool auth{true}; |
425 | | bool disabled{false}; |
426 | | DNSRecord dr; |
427 | | }; |
428 | | |
429 | | class UnknownRecordContent : public DNSRecordContent |
430 | | { |
431 | | public: |
432 | | UnknownRecordContent(const DNSRecord& dr, PacketReader& pr) |
433 | | : d_dr(dr) |
434 | 0 | { |
435 | 0 | pr.copyRecord(d_record, dr.d_clen); |
436 | 0 | } |
437 | | |
438 | | UnknownRecordContent(const string& zone); |
439 | | |
440 | | string getZoneRepresentation(bool noDot) const override; |
441 | | void toPacket(DNSPacketWriter& pw) const override; |
442 | | uint16_t getType() const override |
443 | 0 | { |
444 | 0 | return d_dr.d_type; |
445 | 0 | } |
446 | | |
447 | | const vector<uint8_t>& getRawContent() const |
448 | 0 | { |
449 | 0 | return d_record; |
450 | 0 | } |
451 | | |
452 | | private: |
453 | | DNSRecord d_dr; |
454 | | vector<uint8_t> d_record; |
455 | | }; |
456 | | |
457 | | //! This class can be used to parse incoming packets, and is copyable |
458 | | class MOADNSParser : public boost::noncopyable |
459 | | { |
460 | | public: |
461 | | //! Parse from a string |
462 | | MOADNSParser(bool query, const string& buffer): d_tsigPos(0) |
463 | 0 | { |
464 | 0 | init(query, buffer); |
465 | 0 | } |
466 | | |
467 | | //! Parse from a pointer and length |
468 | | MOADNSParser(bool query, const char *packet, unsigned int len) : d_tsigPos(0) |
469 | 0 | { |
470 | 0 | init(query, std::string_view(packet, len)); |
471 | 0 | } |
472 | | |
473 | | DNSName d_qname; |
474 | | uint16_t d_qclass, d_qtype; |
475 | | //uint8_t d_rcode; |
476 | | dnsheader d_header; |
477 | | |
478 | | typedef vector<pair<DNSRecord, uint16_t > > answers_t; |
479 | | |
480 | | //! All answers contained in this packet (everything *but* the question section) |
481 | | answers_t d_answers; |
482 | | |
483 | | uint16_t getTSIGPos() const |
484 | 0 | { |
485 | 0 | return d_tsigPos; |
486 | 0 | } |
487 | | |
488 | | bool hasEDNS() const; |
489 | | |
490 | | private: |
491 | | void init(bool query, const std::string_view& packet); |
492 | | uint16_t d_tsigPos; |
493 | | }; |
494 | | |
495 | | string simpleCompress(const string& label, const string& root=""); |
496 | | void ageDNSPacket(char* packet, size_t length, uint32_t seconds, const dnsheader_aligned&); |
497 | | void ageDNSPacket(std::string& packet, uint32_t seconds, const dnsheader_aligned&); |
498 | | void editDNSPacketTTL(char* packet, size_t length, const std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)>& visitor); |
499 | | void clearDNSPacketRecordTypes(vector<uint8_t>& packet, const std::unordered_set<QType>& qtypes); |
500 | | void clearDNSPacketRecordTypes(PacketBuffer& packet, const std::unordered_set<QType>& qtypes); |
501 | | void clearDNSPacketRecordTypes(char* packet, size_t& length, const std::unordered_set<QType>& qtypes); |
502 | | uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA=nullptr); |
503 | | uint32_t getDNSPacketLength(const char* packet, size_t length); |
504 | | uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type); |
505 | | bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z); |
506 | | /* call the visitor for every records in the answer, authority and additional sections, passing the section, class, type, ttl, rdatalength and rdata |
507 | | to the visitor. Stops whenever the visitor returns false or at the end of the packet */ |
508 | | bool visitDNSPacket(const std::string_view& packet, const std::function<bool(uint8_t, uint16_t, uint16_t, uint32_t, uint16_t, const char*)>& visitor); |
509 | | |
510 | | template<typename T> |
511 | | std::shared_ptr<const T> getRR(const DNSRecord& dr) |
512 | 0 | { |
513 | 0 | return std::dynamic_pointer_cast<const T>(dr.getContent()); |
514 | 0 | } Unexecuted instantiation: std::__1::shared_ptr<OPTRecordContent const> getRR<OPTRecordContent>(DNSRecord const&) Unexecuted instantiation: std::__1::shared_ptr<ARecordContent const> getRR<ARecordContent>(DNSRecord const&) Unexecuted instantiation: std::__1::shared_ptr<AAAARecordContent const> getRR<AAAARecordContent>(DNSRecord const&) |
515 | | |
516 | | /** Simple DNSPacketMangler. Ritual is: get a pointer into the packet and moveOffset() to beyond your needs |
517 | | * If you survive that, feel free to read from the pointer */ |
518 | | class DNSPacketMangler |
519 | | { |
520 | | public: |
521 | | explicit DNSPacketMangler(std::string& packet) |
522 | | : d_packet(packet.data()), d_length(packet.length()), d_notyouroffset(12), d_offset(d_notyouroffset) |
523 | 0 | {} |
524 | | DNSPacketMangler(char* packet, size_t length) |
525 | | : d_packet(packet), d_length(length), d_notyouroffset(12), d_offset(d_notyouroffset) |
526 | 0 | {} |
527 | | |
528 | | /*! Advances past a wire-format domain name |
529 | | * The name is not checked for adherence to length restrictions. |
530 | | * Compression pointers are not followed. |
531 | | */ |
532 | | void skipDomainName() |
533 | 0 | { |
534 | 0 | uint8_t len; |
535 | 0 | while((len=get8BitInt())) { |
536 | 0 | if(len >= 0xc0) { // extended label |
537 | 0 | get8BitInt(); |
538 | 0 | return; |
539 | 0 | } |
540 | 0 | skipBytes(len); |
541 | 0 | } |
542 | 0 | } |
543 | | |
544 | | void skipBytes(uint16_t bytes) |
545 | 0 | { |
546 | 0 | moveOffset(bytes); |
547 | 0 | } |
548 | | void rewindBytes(uint16_t by) |
549 | 0 | { |
550 | 0 | rewindOffset(by); |
551 | 0 | } |
552 | | uint32_t get32BitInt() |
553 | 0 | { |
554 | 0 | const char* p = d_packet + d_offset; |
555 | 0 | moveOffset(4); |
556 | 0 | uint32_t ret; |
557 | 0 | memcpy(&ret, p, sizeof(ret)); |
558 | 0 | return ntohl(ret); |
559 | 0 | } |
560 | | uint16_t get16BitInt() |
561 | 0 | { |
562 | 0 | const char* p = d_packet + d_offset; |
563 | 0 | moveOffset(2); |
564 | 0 | uint16_t ret; |
565 | 0 | memcpy(&ret, p, sizeof(ret)); |
566 | 0 | return ntohs(ret); |
567 | 0 | } |
568 | | |
569 | | uint8_t get8BitInt() |
570 | 0 | { |
571 | 0 | const char* p = d_packet + d_offset; |
572 | 0 | moveOffset(1); |
573 | 0 | return *p; |
574 | 0 | } |
575 | | |
576 | | void skipRData() |
577 | 0 | { |
578 | 0 | int toskip = get16BitInt(); |
579 | 0 | moveOffset(toskip); |
580 | 0 | } |
581 | | |
582 | | void decreaseAndSkip32BitInt(uint32_t decrease) |
583 | 0 | { |
584 | 0 | const char *p = d_packet + d_offset; |
585 | 0 | moveOffset(4); |
586 | |
|
587 | 0 | uint32_t tmp; |
588 | 0 | memcpy(&tmp, p, sizeof(tmp)); |
589 | 0 | tmp = ntohl(tmp); |
590 | 0 | if (tmp > decrease) { |
591 | 0 | tmp -= decrease; |
592 | 0 | } else { |
593 | 0 | tmp = 0; |
594 | 0 | } |
595 | 0 | tmp = htonl(tmp); |
596 | 0 | memcpy(d_packet + d_offset-4, (const char*)&tmp, sizeof(tmp)); |
597 | 0 | } |
598 | | |
599 | | void setAndSkip32BitInt(uint32_t value) |
600 | 0 | { |
601 | 0 | moveOffset(4); |
602 | |
|
603 | 0 | value = htonl(value); |
604 | 0 | memcpy(d_packet + d_offset-4, (const char*)&value, sizeof(value)); |
605 | 0 | } |
606 | | |
607 | | uint32_t getOffset() const |
608 | 0 | { |
609 | 0 | return d_offset; |
610 | 0 | } |
611 | | |
612 | | private: |
613 | | void moveOffset(uint16_t by) |
614 | 0 | { |
615 | 0 | d_notyouroffset += by; |
616 | 0 | if(d_notyouroffset > d_length) |
617 | 0 | throw std::out_of_range("dns packet out of range: "+std::to_string(d_notyouroffset) +" > " |
618 | 0 | + std::to_string(d_length) ); |
619 | 0 | } |
620 | | |
621 | | void rewindOffset(uint16_t by) |
622 | 0 | { |
623 | 0 | if(d_notyouroffset < by) |
624 | 0 | throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < " |
625 | 0 | + std::to_string(by)); |
626 | 0 | d_notyouroffset -= by; |
627 | 0 | if(d_notyouroffset < 12) |
628 | 0 | throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < " |
629 | 0 | + std::to_string(12)); |
630 | 0 | } |
631 | | |
632 | | char* d_packet; |
633 | | size_t d_length; |
634 | | |
635 | | uint32_t d_notyouroffset; // only 'moveOffset' can touch this |
636 | | const uint32_t& d_offset; // look.. but don't touch |
637 | | }; |