]>
Commit | Line | Data |
---|---|---|
4192ca66 | 1 | /* |
12471842 PL |
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 | */ | |
ff6a1e7b | 22 | #include "dnsparser.hh" |
6c0670c3 | 23 | #include "dnswriter.hh" |
4cf74e6f | 24 | #include <boost/algorithm/string.hpp> |
5dba9d25 | 25 | #include <boost/format.hpp> |
ff6a1e7b | 26 | |
61b26744 | 27 | #include "namespaces.hh" |
ff6a1e7b BH |
28 | |
29 | class UnknownRecordContent : public DNSRecordContent | |
30 | { | |
31 | public: | |
7fc69fd0 | 32 | UnknownRecordContent(const DNSRecord& dr, PacketReader& pr) |
5a1f298f | 33 | : d_dr(dr) |
ff6a1e7b | 34 | { |
7fc69fd0 | 35 | pr.copyRecord(d_record, dr.d_clen); |
ff6a1e7b BH |
36 | } |
37 | ||
5a1f298f | 38 | UnknownRecordContent(const string& zone) |
6c0670c3 | 39 | { |
95a61c6f AT |
40 | // parse the input |
41 | vector<string> parts; | |
42 | stringtok(parts, zone); | |
43 | if(parts.size()!=3 && !(parts.size()==2 && equals(parts[1],"0")) ) | |
335da0ba | 44 | throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got "+std::to_string(parts.size())+": "+zone ); |
95a61c6f | 45 | const string& relevant=(parts.size() > 2) ? parts[2] : ""; |
335da0ba | 46 | unsigned int total=pdns_stou(parts[1]); |
e007fb02 RG |
47 | if(relevant.size() % 2 || relevant.size() / 2 != total) |
48 | throw MOADNSException((boost::format("invalid unknown record length: size not equal to length field (%d != 2 * %d)") % relevant.size() % total).str()); | |
95a61c6f AT |
49 | string out; |
50 | out.reserve(total+1); | |
51 | for(unsigned int n=0; n < total; ++n) { | |
52 | int c; | |
53 | sscanf(relevant.c_str()+2*n, "%02x", &c); | |
54 | out.append(1, (char)c); | |
55 | } | |
56 | ||
57 | d_record.insert(d_record.end(), out.begin(), out.end()); | |
6c0670c3 | 58 | } |
cff3e04d | 59 | |
f21fc0aa | 60 | string getZoneRepresentation(bool noDot) const override |
ff6a1e7b BH |
61 | { |
62 | ostringstream str; | |
705f31ae | 63 | str<<"\\# "<<(unsigned int)d_record.size()<<" "; |
ff6a1e7b BH |
64 | char hex[4]; |
65 | for(size_t n=0; n<d_record.size(); ++n) { | |
66 | snprintf(hex,sizeof(hex)-1, "%02x", d_record.at(n)); | |
67 | str << hex; | |
68 | } | |
ff6a1e7b BH |
69 | return str.str(); |
70 | } | |
cff3e04d CH |
71 | |
72 | void toPacket(DNSPacketWriter& pw) override | |
6c0670c3 | 73 | { |
95a61c6f | 74 | pw.xfrBlob(string(d_record.begin(),d_record.end())); |
6c0670c3 | 75 | } |
5a1f298f | 76 | |
cff3e04d | 77 | uint16_t getType() const override |
5a1f298f | 78 | { |
79 | return d_dr.d_type; | |
80 | } | |
ff6a1e7b | 81 | private: |
6c0670c3 BH |
82 | DNSRecord d_dr; |
83 | vector<uint8_t> d_record; | |
ff6a1e7b BH |
84 | }; |
85 | ||
561434a6 | 86 | shared_ptr<DNSRecordContent> DNSRecordContent::unserialize(const DNSName& qname, uint16_t qtype, const string& serialized) |
ea634573 BH |
87 | { |
88 | dnsheader dnsheader; | |
89 | memset(&dnsheader, 0, sizeof(dnsheader)); | |
90 | dnsheader.qdcount=htons(1); | |
91 | dnsheader.ancount=htons(1); | |
92 | ||
93 | vector<uint8_t> packet; // build pseudo packet | |
678ce973 BH |
94 | |
95 | /* will look like: dnsheader, 5 bytes, encoded qname, dns record header, serialized data */ | |
ea634573 | 96 | |
98584698 | 97 | string encoded=qname.toDNSString(); |
678ce973 BH |
98 | |
99 | packet.resize(sizeof(dnsheader) + 5 + encoded.size() + sizeof(struct dnsrecordheader) + serialized.size()); | |
100 | ||
101 | uint16_t pos=0; | |
102 | ||
103 | memcpy(&packet[0], &dnsheader, sizeof(dnsheader)); pos+=sizeof(dnsheader); | |
104 | ||
105 | char tmp[6]="\x0" "\x0\x1" "\x0\x1"; // root question for ns_t_a | |
106 | memcpy(&packet[pos], &tmp, 5); pos+=5; | |
107 | ||
705f31ae | 108 | memcpy(&packet[pos], encoded.c_str(), encoded.size()); pos+=(uint16_t)encoded.size(); |
ea634573 BH |
109 | |
110 | struct dnsrecordheader drh; | |
111 | drh.d_type=htons(qtype); | |
78f56b38 | 112 | drh.d_class=htons(QClass::IN); |
ea634573 BH |
113 | drh.d_ttl=0; |
114 | drh.d_clen=htons(serialized.size()); | |
115 | ||
678ce973 | 116 | memcpy(&packet[pos], &drh, sizeof(drh)); pos+=sizeof(drh); |
705f31ae | 117 | memcpy(&packet[pos], serialized.c_str(), serialized.size()); pos+=(uint16_t)serialized.size(); |
ea634573 | 118 | |
27c0050c | 119 | MOADNSParser mdp(false, (char*)&*packet.begin(), (unsigned int)packet.size()); |
ea634573 | 120 | shared_ptr<DNSRecordContent> ret= mdp.d_answers.begin()->first.d_content; |
ea634573 BH |
121 | return ret; |
122 | } | |
ff6a1e7b | 123 | |
6177a176 | 124 | std::shared_ptr<DNSRecordContent> DNSRecordContent::mastermake(const DNSRecord &dr, |
232f0877 | 125 | PacketReader& pr) |
ff6a1e7b | 126 | { |
7f7b8d55 BH |
127 | uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT |
128 | ||
129 | typemap_t::const_iterator i=getTypemap().find(make_pair(searchclass, dr.d_type)); | |
49a06471 | 130 | if(i==getTypemap().end() || !i->second) { |
6177a176 | 131 | return std::make_shared<UnknownRecordContent>(dr, pr); |
ff6a1e7b | 132 | } |
945a9ad4 | 133 | |
6177a176 | 134 | return std::shared_ptr<DNSRecordContent>(i->second(dr, pr)); |
ff6a1e7b BH |
135 | } |
136 | ||
6177a176 | 137 | std::shared_ptr<DNSRecordContent> DNSRecordContent::mastermake(uint16_t qtype, uint16_t qclass, |
232f0877 | 138 | const string& content) |
6c0670c3 | 139 | { |
49a06471 BH |
140 | zmakermap_t::const_iterator i=getZmakermap().find(make_pair(qclass, qtype)); |
141 | if(i==getZmakermap().end()) { | |
6177a176 | 142 | return std::make_shared<UnknownRecordContent>(content); |
6c0670c3 BH |
143 | } |
144 | ||
6177a176 | 145 | return std::shared_ptr<DNSRecordContent>(i->second(content)); |
6c0670c3 BH |
146 | } |
147 | ||
a957f3ee | 148 | std::unique_ptr<DNSRecordContent> DNSRecordContent::makeunique(uint16_t qtype, uint16_t qclass, |
149 | const string& content) | |
150 | { | |
151 | zmakermap_t::const_iterator i=getZmakermap().find(make_pair(qclass, qtype)); | |
152 | if(i==getZmakermap().end()) { | |
153 | return std::unique_ptr<DNSRecordContent>(new UnknownRecordContent(content)); | |
154 | } | |
155 | ||
156 | return std::unique_ptr<DNSRecordContent>(i->second(content)); | |
157 | } | |
158 | ||
159 | ||
6177a176 | 160 | std::shared_ptr<DNSRecordContent> DNSRecordContent::mastermake(const DNSRecord &dr, PacketReader& pr, uint16_t oc) { |
7945d472 RA |
161 | // For opcode UPDATE and where the DNSRecord is an answer record, we don't care about content, because this is |
162 | // not used within the prerequisite section of RFC2136, so - we can simply use unknownrecordcontent. | |
89c46d18 | 163 | // For section 3.2.3, we do need content so we need to get it properly. But only for the correct QClasses. |
e693ff5a | 164 | if (oc == Opcode::Update && dr.d_place == DNSResourceRecord::ANSWER && dr.d_class != 1) |
6177a176 | 165 | return std::make_shared<UnknownRecordContent>(dr, pr); |
914353ca | 166 | |
7945d472 RA |
167 | uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT |
168 | ||
169 | typemap_t::const_iterator i=getTypemap().find(make_pair(searchclass, dr.d_type)); | |
170 | if(i==getTypemap().end() || !i->second) { | |
6177a176 | 171 | return std::make_shared<UnknownRecordContent>(dr, pr); |
7945d472 RA |
172 | } |
173 | ||
6177a176 | 174 | return std::shared_ptr<DNSRecordContent>(i->second(dr, pr)); |
7945d472 RA |
175 | } |
176 | ||
177 | ||
49a06471 BH |
178 | DNSRecordContent::typemap_t& DNSRecordContent::getTypemap() |
179 | { | |
180 | static DNSRecordContent::typemap_t typemap; | |
181 | return typemap; | |
182 | } | |
183 | ||
108c321e | 184 | DNSRecordContent::n2typemap_t& DNSRecordContent::getN2Typemap() |
49a06471 | 185 | { |
93e4cb97 BH |
186 | static DNSRecordContent::n2typemap_t n2typemap; |
187 | return n2typemap; | |
49a06471 BH |
188 | } |
189 | ||
108c321e BH |
190 | DNSRecordContent::t2namemap_t& DNSRecordContent::getT2Namemap() |
191 | { | |
93e4cb97 BH |
192 | static DNSRecordContent::t2namemap_t t2namemap; |
193 | return t2namemap; | |
108c321e BH |
194 | } |
195 | ||
49a06471 BH |
196 | DNSRecordContent::zmakermap_t& DNSRecordContent::getZmakermap() |
197 | { | |
198 | static DNSRecordContent::zmakermap_t zmakermap; | |
199 | return zmakermap; | |
200 | } | |
201 | ||
5708a729 | 202 | DNSRecord::DNSRecord(const DNSResourceRecord& rr): d_name(rr.qname) |
fbe23591 | 203 | { |
fbe23591 | 204 | d_type = rr.qtype.getCode(); |
205 | d_ttl = rr.ttl; | |
206 | d_class = rr.qclass; | |
4950b140 | 207 | d_place = DNSResourceRecord::ANSWER; |
e24c18b0 | 208 | d_clen = 0; |
6177a176 | 209 | d_content = DNSRecordContent::mastermake(d_type, rr.qclass, rr.content); |
fbe23591 | 210 | } |
211 | ||
4950b140 CH |
212 | // If you call this and you are not parsing a packet coming from a socket, you are doing it wrong. |
213 | DNSResourceRecord DNSResourceRecord::fromWire(const DNSRecord& d) { | |
214 | DNSResourceRecord rr; | |
215 | rr.qname = d.d_name; | |
216 | rr.qtype = QType(d.d_type); | |
217 | rr.ttl = d.d_ttl; | |
bb7123d3 | 218 | rr.content = d.d_content->getZoneRepresentation(true); |
4950b140 CH |
219 | rr.auth = false; |
220 | rr.qclass = d.d_class; | |
221 | return rr; | |
222 | } | |
223 | ||
78f56b38 | 224 | void MOADNSParser::init(bool query, const std::string& packet) |
ff6a1e7b | 225 | { |
78f56b38 | 226 | if (packet.size() < sizeof(dnsheader)) |
ff6a1e7b BH |
227 | throw MOADNSException("Packet shorter than minimal header"); |
228 | ||
78f56b38 | 229 | memcpy(&d_header, packet.data(), sizeof(dnsheader)); |
ff6a1e7b | 230 | |
7945d472 | 231 | if(d_header.opcode != Opcode::Query && d_header.opcode != Opcode::Notify && d_header.opcode != Opcode::Update) |
335da0ba | 232 | throw MOADNSException("Can't parse non-query packet with opcode="+ std::to_string(d_header.opcode)); |
f27e6356 | 233 | |
ff6a1e7b BH |
234 | d_header.qdcount=ntohs(d_header.qdcount); |
235 | d_header.ancount=ntohs(d_header.ancount); | |
236 | d_header.nscount=ntohs(d_header.nscount); | |
237 | d_header.arcount=ntohs(d_header.arcount); | |
27c0050c RG |
238 | |
239 | if (query && (d_header.qdcount > 1)) | |
240 | throw MOADNSException("Query with QD > 1 ("+std::to_string(d_header.qdcount)+")"); | |
ff6a1e7b | 241 | |
629eaf49 | 242 | unsigned int n=0; |
ff6a1e7b | 243 | |
78f56b38 | 244 | PacketReader pr(packet); |
7b1469bb | 245 | bool validPacket=false; |
512c8492 | 246 | try { |
00bf10d3 BH |
247 | d_qtype = d_qclass = 0; // sometimes replies come in with no question, don't present garbage then |
248 | ||
40207e64 | 249 | for(n=0;n < d_header.qdcount; ++n) { |
8ad443ea | 250 | d_qname=pr.getName(); |
40207e64 BH |
251 | d_qtype=pr.get16BitInt(); |
252 | d_qclass=pr.get16BitInt(); | |
253 | } | |
254 | ||
512c8492 BH |
255 | struct dnsrecordheader ah; |
256 | vector<unsigned char> record; | |
04151caa | 257 | bool seenTSIG = false; |
7b1469bb | 258 | validPacket=true; |
756e82cf | 259 | d_answers.reserve((unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount)); |
c4ac5865 | 260 | for(n=0;n < (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount); ++n) { |
512c8492 BH |
261 | DNSRecord dr; |
262 | ||
263 | if(n < d_header.ancount) | |
e693ff5a | 264 | dr.d_place=DNSResourceRecord::ANSWER; |
512c8492 | 265 | else if(n < d_header.ancount + d_header.nscount) |
e693ff5a | 266 | dr.d_place=DNSResourceRecord::AUTHORITY; |
512c8492 | 267 | else |
e693ff5a | 268 | dr.d_place=DNSResourceRecord::ADDITIONAL; |
04151caa | 269 | |
78f56b38 | 270 | unsigned int recordStartPos=pr.getPosition(); |
57e5f5f7 | 271 | |
f809c028 | 272 | DNSName name=pr.getName(); |
04151caa | 273 | |
512c8492 BH |
274 | pr.getDnsrecordheader(ah); |
275 | dr.d_ttl=ah.d_ttl; | |
276 | dr.d_type=ah.d_type; | |
277 | dr.d_class=ah.d_class; | |
04151caa | 278 | |
f809c028 | 279 | dr.d_name=name; |
512c8492 | 280 | dr.d_clen=ah.d_clen; |
7b1469bb | 281 | |
c2a1b24d PL |
282 | if (query && |
283 | !(d_qtype == QType::IXFR && dr.d_place == DNSResourceRecord::AUTHORITY && dr.d_type == QType::SOA) && // IXFR queries have a SOA in their AUTHORITY section | |
284 | (dr.d_place == DNSResourceRecord::ANSWER || dr.d_place == DNSResourceRecord::AUTHORITY || (dr.d_type != QType::OPT && dr.d_type != QType::TSIG && dr.d_type != QType::SIG && dr.d_type != QType::TKEY) || ((dr.d_type == QType::TSIG || dr.d_type == QType::SIG || dr.d_type == QType::TKEY) && dr.d_class != QClass::ANY))) { | |
27c0050c | 285 | // cerr<<"discarding RR, query is "<<query<<", place is "<<dr.d_place<<", type is "<<dr.d_type<<", class is "<<dr.d_class<<endl; |
63fc7705 | 286 | dr.d_content=std::make_shared<UnknownRecordContent>(dr, pr); |
27c0050c RG |
287 | } |
288 | else { | |
289 | // cerr<<"parsing RR, query is "<<query<<", place is "<<dr.d_place<<", type is "<<dr.d_type<<", class is "<<dr.d_class<<endl; | |
6177a176 | 290 | dr.d_content=DNSRecordContent::mastermake(dr, pr, d_header.opcode); |
27c0050c RG |
291 | } |
292 | ||
78f56b38 | 293 | d_answers.push_back(make_pair(dr, pr.getPosition() - sizeof(dnsheader))); |
57e5f5f7 | 294 | |
18f707fa RG |
295 | /* XXX: XPF records should be allowed after TSIG as soon as the actual XPF option code has been assigned: |
296 | if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG && dr.d_type != QType::XPF) | |
297 | */ | |
298 | if (dr.d_place == DNSResourceRecord::ADDITIONAL && seenTSIG) { | |
04151caa RG |
299 | /* only XPF records are allowed after a TSIG */ |
300 | throw MOADNSException("Packet ("+d_qname.toString()+"|#"+std::to_string(d_qtype)+") has an unexpected record ("+std::to_string(dr.d_type)+") after a TSIG one."); | |
301 | } | |
302 | ||
60a1c204 | 303 | if(dr.d_type == QType::TSIG && dr.d_class == QClass::ANY) { |
18f707fa | 304 | if(seenTSIG || dr.d_place != DNSResourceRecord::ADDITIONAL) { |
86f1af1c | 305 | throw MOADNSException("Packet ("+d_qname.toLogString()+"|#"+std::to_string(d_qtype)+") has a TSIG record in an invalid position."); |
60a1c204 | 306 | } |
04151caa | 307 | seenTSIG = true; |
78f56b38 | 308 | d_tsigPos = recordStartPos; |
60a1c204 | 309 | } |
512c8492 | 310 | } |
e5986c84 | 311 | |
04151caa | 312 | #if 0 |
78f56b38 RG |
313 | if(pr.getPosition()!=packet.size()) { |
314 | throw MOADNSException("Packet ("+d_qname+"|#"+std::to_string(d_qtype)+") has trailing garbage ("+ std::to_string(pr.getPosition()) + " < " + | |
315 | std::to_string(packet.size()) + ")"); | |
ff6a1e7b | 316 | } |
04151caa | 317 | #endif |
512c8492 | 318 | } |
78f56b38 | 319 | catch(const std::out_of_range &re) { |
629eaf49 BH |
320 | if(validPacket && d_header.tc) { // don't sweat it over truncated packets, but do adjust an, ns and arcount |
321 | if(n < d_header.ancount) { | |
4957a608 | 322 | d_header.ancount=n; d_header.nscount = d_header.arcount = 0; |
629eaf49 BH |
323 | } |
324 | else if(n < d_header.ancount + d_header.nscount) { | |
4957a608 | 325 | d_header.nscount = n - d_header.ancount; d_header.arcount=0; |
629eaf49 BH |
326 | } |
327 | else { | |
4957a608 | 328 | d_header.arcount = n - d_header.ancount - d_header.nscount; |
629eaf49 BH |
329 | } |
330 | } | |
331 | else { | |
78f56b38 | 332 | throw MOADNSException("Error parsing packet of "+std::to_string(packet.size())+" bytes (rd="+ |
335da0ba | 333 | std::to_string(d_header.rd)+ |
232f0877 | 334 | "), out of bounds: "+string(re.what())); |
629eaf49 | 335 | } |
ff6a1e7b | 336 | } |
ff6a1e7b BH |
337 | } |
338 | ||
10321a98 | 339 | |
ff6a1e7b BH |
340 | void PacketReader::getDnsrecordheader(struct dnsrecordheader &ah) |
341 | { | |
342 | unsigned int n; | |
343 | unsigned char *p=reinterpret_cast<unsigned char*>(&ah); | |
344 | ||
345 | for(n=0; n < sizeof(dnsrecordheader); ++n) | |
346 | p[n]=d_content.at(d_pos++); | |
347 | ||
348 | ah.d_type=ntohs(ah.d_type); | |
349 | ah.d_class=ntohs(ah.d_class); | |
350 | ah.d_clen=ntohs(ah.d_clen); | |
351 | ah.d_ttl=ntohl(ah.d_ttl); | |
8c1c9170 BH |
352 | |
353 | d_startrecordpos=d_pos; // needed for getBlob later on | |
354 | d_recordlen=ah.d_clen; | |
ff6a1e7b BH |
355 | } |
356 | ||
357 | ||
092f210a | 358 | void PacketReader::copyRecord(vector<unsigned char>& dest, uint16_t len) |
ff6a1e7b BH |
359 | { |
360 | dest.resize(len); | |
bff744a8 BH |
361 | if(!len) |
362 | return; | |
363 | ||
092f210a | 364 | for(uint16_t n=0;n<len;++n) { |
ff6a1e7b BH |
365 | dest.at(n)=d_content.at(d_pos++); |
366 | } | |
367 | } | |
368 | ||
092f210a | 369 | void PacketReader::copyRecord(unsigned char* dest, uint16_t len) |
ff6a1e7b BH |
370 | { |
371 | if(d_pos + len > d_content.size()) | |
10f4eea8 | 372 | throw std::out_of_range("Attempt to copy outside of packet"); |
ff6a1e7b | 373 | |
bff744a8 | 374 | memcpy(dest, &d_content.at(d_pos), len); |
ff6a1e7b BH |
375 | d_pos+=len; |
376 | } | |
377 | ||
341930bb BH |
378 | void PacketReader::xfr48BitInt(uint64_t& ret) |
379 | { | |
380 | ret=0; | |
78f56b38 | 381 | ret+=static_cast<uint8_t>(d_content.at(d_pos++)); |
341930bb | 382 | ret<<=8; |
78f56b38 | 383 | ret+=static_cast<uint8_t>(d_content.at(d_pos++)); |
341930bb | 384 | ret<<=8; |
78f56b38 | 385 | ret+=static_cast<uint8_t>(d_content.at(d_pos++)); |
341930bb | 386 | ret<<=8; |
78f56b38 | 387 | ret+=static_cast<uint8_t>(d_content.at(d_pos++)); |
341930bb | 388 | ret<<=8; |
78f56b38 | 389 | ret+=static_cast<uint8_t>(d_content.at(d_pos++)); |
341930bb | 390 | ret<<=8; |
78f56b38 | 391 | ret+=static_cast<uint8_t>(d_content.at(d_pos++)); |
341930bb | 392 | } |
ff6a1e7b | 393 | |
092f210a | 394 | uint32_t PacketReader::get32BitInt() |
ff6a1e7b | 395 | { |
092f210a | 396 | uint32_t ret=0; |
78f56b38 | 397 | ret+=static_cast<uint8_t>(d_content.at(d_pos++)); |
ff6a1e7b | 398 | ret<<=8; |
78f56b38 | 399 | ret+=static_cast<uint8_t>(d_content.at(d_pos++)); |
ff6a1e7b | 400 | ret<<=8; |
78f56b38 | 401 | ret+=static_cast<uint8_t>(d_content.at(d_pos++)); |
ff6a1e7b | 402 | ret<<=8; |
78f56b38 | 403 | ret+=static_cast<uint8_t>(d_content.at(d_pos++)); |
ff6a1e7b BH |
404 | |
405 | return ret; | |
406 | } | |
407 | ||
408 | ||
092f210a | 409 | uint16_t PacketReader::get16BitInt() |
ff6a1e7b | 410 | { |
092f210a | 411 | uint16_t ret=0; |
78f56b38 | 412 | ret+=static_cast<uint8_t>(d_content.at(d_pos++)); |
ff6a1e7b | 413 | ret<<=8; |
78f56b38 | 414 | ret+=static_cast<uint8_t>(d_content.at(d_pos++)); |
ff6a1e7b BH |
415 | |
416 | return ret; | |
417 | } | |
418 | ||
49a06471 | 419 | uint8_t PacketReader::get8BitInt() |
ff6a1e7b BH |
420 | { |
421 | return d_content.at(d_pos++); | |
422 | } | |
423 | ||
8171ab83 | 424 | DNSName PacketReader::getName() |
ff6a1e7b | 425 | { |
d926c0da | 426 | unsigned int consumed; |
c1229531 | 427 | try { |
78f56b38 | 428 | DNSName dn((const char*) d_content.data(), d_content.size(), d_pos, true /* uncompress */, 0 /* qtype */, 0 /* qclass */, &consumed, sizeof(dnsheader)); |
c1229531 | 429 | |
430 | d_pos+=consumed; | |
8171ab83 | 431 | return dn; |
c1229531 | 432 | } |
6177a176 RG |
433 | catch(const std::range_error& re) { |
434 | throw std::out_of_range(string("dnsname issue: ")+re.what()); | |
435 | } | |
436 | catch(...) { | |
437 | throw std::out_of_range("dnsname issue"); | |
438 | } | |
fb38d90b | 439 | throw PDNSException("PacketReader::getName(): name is empty"); |
ff6a1e7b BH |
440 | } |
441 | ||
ef6a78d5 BH |
442 | static string txtEscape(const string &name) |
443 | { | |
444 | string ret; | |
5dba9d25 | 445 | char ebuf[5]; |
ef6a78d5 | 446 | |
5dba9d25 | 447 | for(string::const_iterator i=name.begin();i!=name.end();++i) { |
fa1dc694 | 448 | if((unsigned char) *i >= 127 || (unsigned char) *i < 32) { |
5dba9d25 PD |
449 | snprintf(ebuf, sizeof(ebuf), "\\%03u", (unsigned char)*i); |
450 | ret += ebuf; | |
451 | } | |
66b40966 | 452 | else if(*i=='"' || *i=='\\'){ |
ef6a78d5 BH |
453 | ret += '\\'; |
454 | ret += *i; | |
455 | } | |
456 | else | |
457 | ret += *i; | |
5dba9d25 | 458 | } |
ef6a78d5 BH |
459 | return ret; |
460 | } | |
461 | ||
462 | // exceptions thrown here do not result in logging in the main pdns auth server - just so you know! | |
84e1142d | 463 | string PacketReader::getText(bool multi, bool lenField) |
9d9c52ef BH |
464 | { |
465 | string ret; | |
466 | ret.reserve(40); | |
ef6a78d5 BH |
467 | while(d_pos < d_startrecordpos + d_recordlen ) { |
468 | if(!ret.empty()) { | |
469 | ret.append(1,' '); | |
470 | } | |
84e1142d PL |
471 | uint16_t labellen; |
472 | if(lenField) | |
78f56b38 | 473 | labellen=static_cast<uint8_t>(d_content.at(d_pos++)); |
84e1142d PL |
474 | else |
475 | labellen=d_recordlen - (d_pos - d_startrecordpos); | |
ef6a78d5 BH |
476 | |
477 | ret.append(1,'"'); | |
950f78df BH |
478 | if(labellen) { // no need to do anything for an empty string |
479 | string val(&d_content.at(d_pos), &d_content.at(d_pos+labellen-1)+1); | |
480 | ret.append(txtEscape(val)); // the end is one beyond the packet | |
481 | } | |
ef6a78d5 BH |
482 | ret.append(1,'"'); |
483 | d_pos+=labellen; | |
484 | if(!multi) | |
485 | break; | |
486 | } | |
9d9c52ef | 487 | |
9d9c52ef BH |
488 | return ret; |
489 | } | |
ff6a1e7b | 490 | |
948a927f PL |
491 | string PacketReader::getUnquotedText(bool lenField) |
492 | { | |
a94e5580 | 493 | uint16_t stop_at; |
948a927f | 494 | if(lenField) |
78f56b38 | 495 | stop_at = static_cast<uint8_t>(d_content.at(d_pos)) + d_pos + 1; |
948a927f PL |
496 | else |
497 | stop_at = d_recordlen; | |
498 | ||
499 | if(stop_at == d_pos) | |
500 | return ""; | |
501 | ||
502 | d_pos++; | |
503 | string ret(&d_content.at(d_pos), &d_content.at(stop_at)); | |
504 | d_pos = stop_at; | |
505 | return ret; | |
506 | } | |
ef6a78d5 | 507 | |
8c1c9170 | 508 | void PacketReader::xfrBlob(string& blob) |
2bc83940 | 509 | try |
8c1c9170 | 510 | { |
e2c162d3 | 511 | if(d_recordlen && !(d_pos == (d_startrecordpos + d_recordlen))) |
7f7b8d55 BH |
512 | blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1); |
513 | else | |
514 | blob.clear(); | |
8c1c9170 BH |
515 | |
516 | d_pos = d_startrecordpos + d_recordlen; | |
517 | } | |
2bc83940 | 518 | catch(...) |
519 | { | |
520 | throw std::out_of_range("xfrBlob out of range"); | |
521 | } | |
59a0f653 | 522 | |
2fe9d6f7 AT |
523 | void PacketReader::xfrBlobNoSpaces(string& blob, int length) { |
524 | xfrBlob(blob, length); | |
525 | } | |
526 | ||
06ffdc52 BH |
527 | void PacketReader::xfrBlob(string& blob, int length) |
528 | { | |
2617464d | 529 | if(length) { |
0407751c | 530 | blob.assign(&d_content.at(d_pos), &d_content.at(d_pos + length - 1 ) + 1 ); |
2617464d BH |
531 | |
532 | d_pos += length; | |
533 | } | |
534 | else | |
535 | blob.clear(); | |
06ffdc52 BH |
536 | } |
537 | ||
538 | ||
e4090157 | 539 | void PacketReader::xfrHexBlob(string& blob, bool keepReading) |
59a0f653 BH |
540 | { |
541 | xfrBlob(blob); | |
542 | } | |
b8e0f341 | 543 | |
3343ad1f | 544 | //FIXME400 remove this method completely |
27ff60a3 | 545 | string simpleCompress(const string& elabel, const string& root) |
b8e0f341 | 546 | { |
27ff60a3 | 547 | string label=elabel; |
3343ad1f | 548 | // FIXME400: this relies on the semi-canonical escaped output from getName |
83b746fd | 549 | if(strchr(label.c_str(), '\\')) { |
550 | boost::replace_all(label, "\\.", "."); | |
551 | boost::replace_all(label, "\\032", " "); | |
552 | boost::replace_all(label, "\\\\", "\\"); | |
553 | } | |
b8e0f341 BH |
554 | typedef vector<pair<unsigned int, unsigned int> > parts_t; |
555 | parts_t parts; | |
556 | vstringtok(parts, label, "."); | |
557 | string ret; | |
558 | ret.reserve(label.size()+4); | |
559 | for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) { | |
05a38bfa | 560 | if(!root.empty() && !strncasecmp(root.c_str(), label.c_str() + i->first, 1 + label.length() - i->first)) { // also match trailing 0, hence '1 +' |
015db425 PD |
561 | const unsigned char rootptr[2]={0xc0,0x11}; |
562 | ret.append((const char *) rootptr, 2); | |
7127879f BH |
563 | return ret; |
564 | } | |
b8e0f341 BH |
565 | ret.append(1, (char)(i->second - i->first)); |
566 | ret.append(label.c_str() + i->first, i->second - i->first); | |
567 | } | |
568 | ret.append(1, (char)0); | |
569 | return ret; | |
570 | } | |
571 | ||
7127879f | 572 | |
2c73e580 BH |
573 | /** Simple DNSPacketMangler. Ritual is: get a pointer into the packet and moveOffset() to beyond your needs |
574 | * If you survive that, feel free to read from the pointer */ | |
575 | class DNSPacketMangler | |
576 | { | |
577 | public: | |
578 | explicit DNSPacketMangler(std::string& packet) | |
886e2cf2 RG |
579 | : d_packet((char*) packet.c_str()), d_length(packet.length()), d_notyouroffset(12), d_offset(d_notyouroffset) |
580 | {} | |
581 | DNSPacketMangler(char* packet, size_t length) | |
582 | : d_packet(packet), d_length(length), d_notyouroffset(12), d_offset(d_notyouroffset) | |
2c73e580 BH |
583 | {} |
584 | ||
585 | void skipLabel() | |
586 | { | |
587 | uint8_t len; | |
588 | while((len=get8BitInt())) { | |
589 | if(len >= 0xc0) { // extended label | |
232f0877 CH |
590 | get8BitInt(); |
591 | return; | |
2c73e580 BH |
592 | } |
593 | skipBytes(len); | |
594 | } | |
595 | } | |
596 | void skipBytes(uint16_t bytes) | |
597 | { | |
153d5065 RG |
598 | moveOffset(bytes); |
599 | } | |
600 | void rewindBytes(uint16_t by) | |
601 | { | |
602 | rewindOffset(by); | |
2c73e580 | 603 | } |
0766890a RG |
604 | uint32_t get32BitInt() |
605 | { | |
606 | const char* p = d_packet + d_offset; | |
607 | moveOffset(4); | |
608 | uint32_t ret; | |
a683e8bd | 609 | memcpy(&ret, (void*)p, sizeof(ret)); |
0766890a RG |
610 | return ntohl(ret); |
611 | } | |
2c73e580 BH |
612 | uint16_t get16BitInt() |
613 | { | |
886e2cf2 | 614 | const char* p = d_packet + d_offset; |
2c73e580 BH |
615 | moveOffset(2); |
616 | uint16_t ret; | |
a683e8bd | 617 | memcpy(&ret, (void*)p, sizeof(ret)); |
2c73e580 BH |
618 | return ntohs(ret); |
619 | } | |
620 | ||
621 | uint8_t get8BitInt() | |
622 | { | |
886e2cf2 | 623 | const char* p = d_packet + d_offset; |
2c73e580 BH |
624 | moveOffset(1); |
625 | return *p; | |
626 | } | |
627 | ||
628 | void skipRData() | |
629 | { | |
630 | int toskip = get16BitInt(); | |
631 | moveOffset(toskip); | |
632 | } | |
153d5065 | 633 | |
2c73e580 BH |
634 | void decreaseAndSkip32BitInt(uint32_t decrease) |
635 | { | |
886e2cf2 | 636 | const char *p = d_packet + d_offset; |
2c73e580 | 637 | moveOffset(4); |
153d5065 | 638 | |
2c73e580 BH |
639 | uint32_t tmp; |
640 | memcpy(&tmp, (void*) p, sizeof(tmp)); | |
641 | tmp = ntohl(tmp); | |
642 | tmp-=decrease; | |
643 | tmp = htonl(tmp); | |
886e2cf2 | 644 | memcpy(d_packet + d_offset-4, (const char*)&tmp, sizeof(tmp)); |
2c73e580 | 645 | } |
153d5065 RG |
646 | void setAndSkip32BitInt(uint32_t value) |
647 | { | |
648 | moveOffset(4); | |
649 | ||
650 | value = htonl(value); | |
651 | memcpy(d_packet + d_offset-4, (const char*)&value, sizeof(value)); | |
652 | } | |
55baa1f2 RG |
653 | uint32_t getOffset() const |
654 | { | |
655 | return d_offset; | |
656 | } | |
2c73e580 BH |
657 | private: |
658 | void moveOffset(uint16_t by) | |
659 | { | |
660 | d_notyouroffset += by; | |
886e2cf2 | 661 | if(d_notyouroffset > d_length) |
335da0ba | 662 | throw std::out_of_range("dns packet out of range: "+std::to_string(d_notyouroffset) +" > " |
886e2cf2 | 663 | + std::to_string(d_length) ); |
2c73e580 | 664 | } |
153d5065 RG |
665 | void rewindOffset(uint16_t by) |
666 | { | |
667 | if(d_notyouroffset < by) | |
668 | throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < " | |
669 | + std::to_string(by)); | |
670 | d_notyouroffset -= by; | |
671 | if(d_notyouroffset < 12) | |
672 | throw std::out_of_range("Rewinding dns packet out of range: "+std::to_string(d_notyouroffset) +" < " | |
673 | + std::to_string(12)); | |
674 | } | |
886e2cf2 RG |
675 | char* d_packet; |
676 | size_t d_length; | |
2c73e580 BH |
677 | |
678 | uint32_t d_notyouroffset; // only 'moveOffset' can touch this | |
679 | const uint32_t& d_offset; // look.. but don't touch | |
680 | ||
681 | }; | |
682 | ||
153d5065 RG |
683 | // method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it |
684 | void editDNSPacketTTL(char* packet, size_t length, std::function<uint32_t(uint8_t, uint16_t, uint16_t, uint32_t)> visitor) | |
685 | { | |
686 | if(length < sizeof(dnsheader)) | |
687 | return; | |
688 | try | |
689 | { | |
690 | dnsheader dh; | |
691 | memcpy((void*)&dh, (const dnsheader*)packet, sizeof(dh)); | |
692 | uint64_t numrecords = ntohs(dh.ancount) + ntohs(dh.nscount) + ntohs(dh.arcount); | |
693 | DNSPacketMangler dpm(packet, length); | |
694 | ||
695 | uint64_t n; | |
696 | for(n=0; n < ntohs(dh.qdcount) ; ++n) { | |
697 | dpm.skipLabel(); | |
698 | /* type and class */ | |
699 | dpm.skipBytes(4); | |
700 | } | |
701 | ||
702 | for(n=0; n < numrecords; ++n) { | |
703 | dpm.skipLabel(); | |
704 | ||
d7ce446c | 705 | uint8_t section = n < ntohs(dh.ancount) ? 1 : (n < (ntohs(dh.ancount) + ntohs(dh.nscount)) ? 2 : 3); |
153d5065 RG |
706 | uint16_t dnstype = dpm.get16BitInt(); |
707 | uint16_t dnsclass = dpm.get16BitInt(); | |
708 | ||
709 | if(dnstype == QType::OPT) // not getting near that one with a stick | |
710 | break; | |
711 | ||
712 | uint32_t dnsttl = dpm.get32BitInt(); | |
713 | uint32_t newttl = visitor(section, dnsclass, dnstype, dnsttl); | |
714 | if (newttl) { | |
715 | dpm.rewindBytes(sizeof(newttl)); | |
716 | dpm.setAndSkip32BitInt(newttl); | |
717 | } | |
718 | dpm.skipRData(); | |
719 | } | |
720 | } | |
721 | catch(...) | |
722 | { | |
723 | return; | |
724 | } | |
725 | } | |
726 | ||
2c73e580 | 727 | // method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it |
886e2cf2 | 728 | void ageDNSPacket(char* packet, size_t length, uint32_t seconds) |
2c73e580 | 729 | { |
886e2cf2 | 730 | if(length < sizeof(dnsheader)) |
2c73e580 BH |
731 | return; |
732 | try | |
733 | { | |
7def3a4b RG |
734 | const dnsheader* dh = reinterpret_cast<const dnsheader*>(packet); |
735 | const uint64_t dqcount = ntohs(dh->qdcount); | |
736 | const uint64_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount); | |
886e2cf2 | 737 | DNSPacketMangler dpm(packet, length); |
a683e8bd RG |
738 | |
739 | uint64_t n; | |
7def3a4b | 740 | for(n=0; n < dqcount; ++n) { |
2c73e580 | 741 | dpm.skipLabel(); |
1819b930 RG |
742 | /* type and class */ |
743 | dpm.skipBytes(4); | |
2c73e580 BH |
744 | } |
745 | // cerr<<"Skipped "<<n<<" questions, now parsing "<<numrecords<<" records"<<endl; | |
746 | for(n=0; n < numrecords; ++n) { | |
747 | dpm.skipLabel(); | |
748 | ||
749 | uint16_t dnstype = dpm.get16BitInt(); | |
1819b930 RG |
750 | /* class */ |
751 | dpm.skipBytes(2); | |
2c73e580 BH |
752 | |
753 | if(dnstype == QType::OPT) // not aging that one with a stick | |
232f0877 | 754 | break; |
2c73e580 BH |
755 | |
756 | dpm.decreaseAndSkip32BitInt(seconds); | |
757 | dpm.skipRData(); | |
758 | } | |
759 | } | |
760 | catch(...) | |
761 | { | |
762 | return; | |
763 | } | |
764 | } | |
886e2cf2 RG |
765 | |
766 | void ageDNSPacket(std::string& packet, uint32_t seconds) | |
767 | { | |
768 | ageDNSPacket((char*)packet.c_str(), packet.length(), seconds); | |
769 | } | |
0766890a | 770 | |
47698274 | 771 | uint32_t getDNSPacketMinTTL(const char* packet, size_t length, bool* seenAuthSOA) |
0766890a RG |
772 | { |
773 | uint32_t result = std::numeric_limits<uint32_t>::max(); | |
774 | if(length < sizeof(dnsheader)) { | |
775 | return result; | |
776 | } | |
777 | try | |
778 | { | |
779 | const dnsheader* dh = (const dnsheader*) packet; | |
780 | DNSPacketMangler dpm(const_cast<char*>(packet), length); | |
781 | ||
782 | const uint16_t qdcount = ntohs(dh->qdcount); | |
783 | for(size_t n = 0; n < qdcount; ++n) { | |
784 | dpm.skipLabel(); | |
1819b930 RG |
785 | /* type and class */ |
786 | dpm.skipBytes(4); | |
0766890a RG |
787 | } |
788 | const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount); | |
789 | for(size_t n = 0; n < numrecords; ++n) { | |
790 | dpm.skipLabel(); | |
0766890a | 791 | const uint16_t dnstype = dpm.get16BitInt(); |
1819b930 | 792 | /* class */ |
47698274 | 793 | const uint16_t dnsclass = dpm.get16BitInt(); |
0766890a | 794 | |
d7ce446c | 795 | if(dnstype == QType::OPT) { |
0766890a | 796 | break; |
d7ce446c RG |
797 | } |
798 | ||
799 | /* report it if we see a SOA record in the AUTHORITY section */ | |
cfd669b4 | 800 | if(dnstype == QType::SOA && dnsclass == QClass::IN && seenAuthSOA != nullptr && n >= ntohs(dh->ancount) && n < (ntohs(dh->ancount) + ntohs(dh->nscount))) { |
d7ce446c RG |
801 | *seenAuthSOA = true; |
802 | } | |
0766890a RG |
803 | |
804 | const uint32_t ttl = dpm.get32BitInt(); | |
47698274 | 805 | if (result > ttl) { |
0766890a | 806 | result = ttl; |
47698274 | 807 | } |
0766890a RG |
808 | |
809 | dpm.skipRData(); | |
810 | } | |
811 | } | |
812 | catch(...) | |
813 | { | |
814 | } | |
815 | return result; | |
816 | } | |
55baa1f2 RG |
817 | |
818 | uint32_t getDNSPacketLength(const char* packet, size_t length) | |
819 | { | |
820 | uint32_t result = length; | |
821 | if(length < sizeof(dnsheader)) { | |
822 | return result; | |
823 | } | |
824 | try | |
825 | { | |
826 | const dnsheader* dh = (const dnsheader*) packet; | |
827 | DNSPacketMangler dpm(const_cast<char*>(packet), length); | |
828 | ||
829 | const uint16_t qdcount = ntohs(dh->qdcount); | |
830 | for(size_t n = 0; n < qdcount; ++n) { | |
831 | dpm.skipLabel(); | |
1819b930 RG |
832 | /* type and class */ |
833 | dpm.skipBytes(4); | |
55baa1f2 RG |
834 | } |
835 | const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount); | |
836 | for(size_t n = 0; n < numrecords; ++n) { | |
837 | dpm.skipLabel(); | |
1819b930 RG |
838 | /* type (2), class (2) and ttl (4) */ |
839 | dpm.skipBytes(8); | |
55baa1f2 RG |
840 | dpm.skipRData(); |
841 | } | |
842 | result = dpm.getOffset(); | |
843 | } | |
844 | catch(...) | |
845 | { | |
846 | } | |
847 | return result; | |
848 | } | |
849 | ||
850 | uint16_t getRecordsOfTypeCount(const char* packet, size_t length, uint8_t section, uint16_t type) | |
851 | { | |
852 | uint16_t result = 0; | |
853 | if(length < sizeof(dnsheader)) { | |
854 | return result; | |
855 | } | |
856 | try | |
857 | { | |
858 | const dnsheader* dh = (const dnsheader*) packet; | |
859 | DNSPacketMangler dpm(const_cast<char*>(packet), length); | |
860 | ||
861 | const uint16_t qdcount = ntohs(dh->qdcount); | |
862 | for(size_t n = 0; n < qdcount; ++n) { | |
863 | dpm.skipLabel(); | |
864 | if (section == 0) { | |
865 | uint16_t dnstype = dpm.get16BitInt(); | |
866 | if (dnstype == type) { | |
867 | result++; | |
868 | } | |
1819b930 RG |
869 | /* class */ |
870 | dpm.skipBytes(2); | |
55baa1f2 | 871 | } else { |
1819b930 RG |
872 | /* type and class */ |
873 | dpm.skipBytes(4); | |
55baa1f2 RG |
874 | } |
875 | } | |
876 | const uint16_t ancount = ntohs(dh->ancount); | |
877 | for(size_t n = 0; n < ancount; ++n) { | |
878 | dpm.skipLabel(); | |
879 | if (section == 1) { | |
880 | uint16_t dnstype = dpm.get16BitInt(); | |
881 | if (dnstype == type) { | |
882 | result++; | |
883 | } | |
1819b930 RG |
884 | /* class */ |
885 | dpm.skipBytes(2); | |
55baa1f2 | 886 | } else { |
1819b930 RG |
887 | /* type and class */ |
888 | dpm.skipBytes(4); | |
55baa1f2 | 889 | } |
1819b930 RG |
890 | /* ttl */ |
891 | dpm.skipBytes(4); | |
55baa1f2 RG |
892 | dpm.skipRData(); |
893 | } | |
894 | const uint16_t nscount = ntohs(dh->nscount); | |
895 | for(size_t n = 0; n < nscount; ++n) { | |
896 | dpm.skipLabel(); | |
897 | if (section == 2) { | |
898 | uint16_t dnstype = dpm.get16BitInt(); | |
899 | if (dnstype == type) { | |
900 | result++; | |
901 | } | |
1819b930 RG |
902 | /* class */ |
903 | dpm.skipBytes(2); | |
55baa1f2 | 904 | } else { |
1819b930 RG |
905 | /* type and class */ |
906 | dpm.skipBytes(4); | |
55baa1f2 | 907 | } |
1819b930 RG |
908 | /* ttl */ |
909 | dpm.skipBytes(4); | |
55baa1f2 RG |
910 | dpm.skipRData(); |
911 | } | |
912 | const uint16_t arcount = ntohs(dh->arcount); | |
913 | for(size_t n = 0; n < arcount; ++n) { | |
914 | dpm.skipLabel(); | |
915 | if (section == 3) { | |
916 | uint16_t dnstype = dpm.get16BitInt(); | |
917 | if (dnstype == type) { | |
918 | result++; | |
919 | } | |
1819b930 RG |
920 | /* class */ |
921 | dpm.skipBytes(2); | |
55baa1f2 | 922 | } else { |
1819b930 RG |
923 | /* type and class */ |
924 | dpm.skipBytes(4); | |
55baa1f2 | 925 | } |
1819b930 RG |
926 | /* ttl */ |
927 | dpm.skipBytes(4); | |
55baa1f2 RG |
928 | dpm.skipRData(); |
929 | } | |
930 | } | |
931 | catch(...) | |
932 | { | |
933 | } | |
934 | return result; | |
935 | } | |
e7c732b8 | 936 | |
e0fd37ec | 937 | bool getEDNSUDPPayloadSizeAndZ(const char* packet, size_t length, uint16_t* payloadSize, uint16_t* z) |
e7c732b8 RG |
938 | { |
939 | if (length < sizeof(dnsheader)) { | |
e0fd37ec | 940 | return false; |
e7c732b8 RG |
941 | } |
942 | ||
e0fd37ec RG |
943 | *payloadSize = 0; |
944 | *z = 0; | |
945 | ||
e7c732b8 RG |
946 | try |
947 | { | |
948 | const dnsheader* dh = (const dnsheader*) packet; | |
949 | DNSPacketMangler dpm(const_cast<char*>(packet), length); | |
950 | ||
951 | const uint16_t qdcount = ntohs(dh->qdcount); | |
952 | for(size_t n = 0; n < qdcount; ++n) { | |
953 | dpm.skipLabel(); | |
954 | /* type and class */ | |
955 | dpm.skipBytes(4); | |
956 | } | |
957 | const size_t numrecords = ntohs(dh->ancount) + ntohs(dh->nscount) + ntohs(dh->arcount); | |
958 | for(size_t n = 0; n < numrecords; ++n) { | |
959 | dpm.skipLabel(); | |
960 | const uint16_t dnstype = dpm.get16BitInt(); | |
961 | const uint16_t dnsclass = dpm.get16BitInt(); | |
962 | ||
963 | if(dnstype == QType::OPT) { | |
e0fd37ec RG |
964 | /* skip extended rcode and version */ |
965 | dpm.skipBytes(2); | |
966 | *z = dpm.get16BitInt(); | |
967 | *payloadSize = dnsclass; | |
968 | return true; | |
e7c732b8 RG |
969 | } |
970 | ||
971 | /* TTL */ | |
972 | dpm.skipBytes(4); | |
973 | dpm.skipRData(); | |
974 | } | |
975 | } | |
976 | catch(...) | |
977 | { | |
978 | } | |
e0fd37ec RG |
979 | |
980 | return false; | |
e7c732b8 | 981 | } |