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