]>
Commit | Line | Data |
---|---|---|
4192ca66 BH |
1 | /* |
2 | PowerDNS Versatile Database Driven Nameserver | |
93e4cb97 | 3 | Copyright (C) 2005 - 2011 PowerDNS.COM BV |
4192ca66 BH |
4 | |
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License version 2 as | |
7 | published by the Free Software Foundation | |
8 | ||
f782fe38 MH |
9 | Additionally, the license of this program contains a special |
10 | exception which allows to distribute the program in binary form when | |
11 | it is linked against OpenSSL. | |
12 | ||
4192ca66 BH |
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 | |
06bd9ccf | 20 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
4192ca66 BH |
21 | */ |
22 | ||
ff6a1e7b | 23 | #include "dnsparser.hh" |
6c0670c3 | 24 | #include "dnswriter.hh" |
ff6a1e7b | 25 | #include <boost/lexical_cast.hpp> |
4cf74e6f | 26 | #include <boost/algorithm/string.hpp> |
5dba9d25 | 27 | #include <boost/format.hpp> |
ff6a1e7b | 28 | |
61b26744 | 29 | #include "namespaces.hh" |
ff6a1e7b BH |
30 | |
31 | class UnknownRecordContent : public DNSRecordContent | |
32 | { | |
33 | public: | |
7fc69fd0 | 34 | UnknownRecordContent(const DNSRecord& dr, PacketReader& pr) |
ea634573 | 35 | : DNSRecordContent(dr.d_type), d_dr(dr) |
ff6a1e7b | 36 | { |
7fc69fd0 | 37 | pr.copyRecord(d_record, dr.d_clen); |
ff6a1e7b BH |
38 | } |
39 | ||
ea634573 | 40 | UnknownRecordContent(const string& zone) : DNSRecordContent(0) |
6c0670c3 BH |
41 | { |
42 | d_record.insert(d_record.end(), zone.begin(), zone.end()); | |
43 | } | |
44 | ||
ff6a1e7b BH |
45 | string getZoneRepresentation() const |
46 | { | |
47 | ostringstream str; | |
705f31ae | 48 | str<<"\\# "<<(unsigned int)d_record.size()<<" "; |
ff6a1e7b BH |
49 | char hex[4]; |
50 | for(size_t n=0; n<d_record.size(); ++n) { | |
51 | snprintf(hex,sizeof(hex)-1, "%02x", d_record.at(n)); | |
52 | str << hex; | |
53 | } | |
ff6a1e7b BH |
54 | return str.str(); |
55 | } | |
56 | ||
6c0670c3 BH |
57 | void toPacket(DNSPacketWriter& pw) |
58 | { | |
d0b471d3 | 59 | string tmp((char*)&*d_record.begin(), d_record.size()); |
6c0670c3 BH |
60 | vector<string> parts; |
61 | stringtok(parts, tmp); | |
4cf74e6f | 62 | if(parts.size()!=3 && !(parts.size()==2 && equals(parts[1],"0")) ) |
a9af3782 | 63 | throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got "+lexical_cast<string>(parts.size())+": "+tmp ); |
4cf74e6f | 64 | const string& relevant=(parts.size() > 2) ? parts[2] : ""; |
6c0670c3 BH |
65 | unsigned int total=atoi(parts[1].c_str()); |
66 | if(relevant.size()!=2*total) | |
70d5a663 | 67 | throw MOADNSException((boost::format("invalid unknown record length for label %s: size not equal to length field (%d != %d)") % d_dr.d_label.c_str() % relevant.size() % (2*total)).str()); |
6c0670c3 | 68 | string out; |
cc835f89 | 69 | out.reserve(total+1); |
6c0670c3 BH |
70 | for(unsigned int n=0; n < total; ++n) { |
71 | int c; | |
72 | sscanf(relevant.c_str()+2*n, "%02x", &c); | |
73 | out.append(1, (char)c); | |
74 | } | |
75 | pw.xfrBlob(out); | |
76 | } | |
ff6a1e7b | 77 | private: |
6c0670c3 BH |
78 | DNSRecord d_dr; |
79 | vector<uint8_t> d_record; | |
ff6a1e7b BH |
80 | }; |
81 | ||
9bd9588f | 82 | static const string EncodeDNSLabel(const string& input) |
ea634573 | 83 | { |
9bd9588f BH |
84 | if(input.length() == 1 && input[0]=='.') // otherwise we encode .. (long story) |
85 | return string (1, 0); | |
86 | ||
213f6de6 BH |
87 | labelparts_t parts; |
88 | bool unescapedSomething = labeltokUnescape(parts, input); | |
89 | string ret; | |
9bd9588f | 90 | |
213f6de6 BH |
91 | if(!unescapedSomething) { |
92 | for(labelparts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) { | |
9bd9588f BH |
93 | ret.append(1, i->second - i->first); |
94 | ret.append(input.c_str() + i->first, i->second - i->first); | |
213f6de6 BH |
95 | } |
96 | ||
97 | } else { | |
98 | for(labelparts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) { | |
9bd9588f BH |
99 | string part(input.c_str() + i->first, i->second - i->first); |
100 | boost::replace_all(part, "\\\\", "\\"); | |
101 | boost::replace_all(part, "\\.", "."); | |
213f6de6 BH |
102 | |
103 | ret.append(1, part.length()); | |
104 | ret.append(part); | |
105 | } | |
106 | } | |
107 | ret.append(1, 0); | |
9bd9588f BH |
108 | return ret; |
109 | } | |
ff6a1e7b | 110 | |
213f6de6 | 111 | |
ea634573 BH |
112 | shared_ptr<DNSRecordContent> DNSRecordContent::unserialize(const string& qname, uint16_t qtype, const string& serialized) |
113 | { | |
114 | dnsheader dnsheader; | |
115 | memset(&dnsheader, 0, sizeof(dnsheader)); | |
116 | dnsheader.qdcount=htons(1); | |
117 | dnsheader.ancount=htons(1); | |
118 | ||
119 | vector<uint8_t> packet; // build pseudo packet | |
678ce973 BH |
120 | |
121 | /* will look like: dnsheader, 5 bytes, encoded qname, dns record header, serialized data */ | |
ea634573 BH |
122 | |
123 | string encoded=EncodeDNSLabel(qname); | |
678ce973 BH |
124 | |
125 | packet.resize(sizeof(dnsheader) + 5 + encoded.size() + sizeof(struct dnsrecordheader) + serialized.size()); | |
126 | ||
127 | uint16_t pos=0; | |
128 | ||
129 | memcpy(&packet[0], &dnsheader, sizeof(dnsheader)); pos+=sizeof(dnsheader); | |
130 | ||
131 | char tmp[6]="\x0" "\x0\x1" "\x0\x1"; // root question for ns_t_a | |
132 | memcpy(&packet[pos], &tmp, 5); pos+=5; | |
133 | ||
705f31ae | 134 | memcpy(&packet[pos], encoded.c_str(), encoded.size()); pos+=(uint16_t)encoded.size(); |
ea634573 BH |
135 | |
136 | struct dnsrecordheader drh; | |
137 | drh.d_type=htons(qtype); | |
138 | drh.d_class=htons(1); | |
139 | drh.d_ttl=0; | |
140 | drh.d_clen=htons(serialized.size()); | |
141 | ||
678ce973 | 142 | memcpy(&packet[pos], &drh, sizeof(drh)); pos+=sizeof(drh); |
705f31ae | 143 | memcpy(&packet[pos], serialized.c_str(), serialized.size()); pos+=(uint16_t)serialized.size(); |
ea634573 | 144 | |
705f31ae | 145 | MOADNSParser mdp((char*)&*packet.begin(), (unsigned int)packet.size()); |
ea634573 BH |
146 | shared_ptr<DNSRecordContent> ret= mdp.d_answers.begin()->first.d_content; |
147 | ret->header.d_type=ret->d_qtype; | |
148 | ret->label=mdp.d_answers.begin()->first.d_label; | |
149 | ret->header.d_ttl=mdp.d_answers.begin()->first.d_ttl; | |
150 | return ret; | |
151 | } | |
ff6a1e7b | 152 | |
7fc69fd0 | 153 | DNSRecordContent* DNSRecordContent::mastermake(const DNSRecord &dr, |
232f0877 | 154 | PacketReader& pr) |
ff6a1e7b | 155 | { |
7f7b8d55 BH |
156 | uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT |
157 | ||
158 | typemap_t::const_iterator i=getTypemap().find(make_pair(searchclass, dr.d_type)); | |
49a06471 | 159 | if(i==getTypemap().end() || !i->second) { |
7fc69fd0 | 160 | return new UnknownRecordContent(dr, pr); |
ff6a1e7b | 161 | } |
945a9ad4 | 162 | |
7fc69fd0 | 163 | return i->second(dr, pr); |
ff6a1e7b BH |
164 | } |
165 | ||
6c0670c3 | 166 | DNSRecordContent* DNSRecordContent::mastermake(uint16_t qtype, uint16_t qclass, |
232f0877 | 167 | const string& content) |
6c0670c3 | 168 | { |
49a06471 BH |
169 | zmakermap_t::const_iterator i=getZmakermap().find(make_pair(qclass, qtype)); |
170 | if(i==getZmakermap().end()) { | |
6c0670c3 BH |
171 | return new UnknownRecordContent(content); |
172 | } | |
173 | ||
174 | return i->second(content); | |
175 | } | |
176 | ||
7945d472 RA |
177 | DNSRecordContent* DNSRecordContent::mastermake(const DNSRecord &dr, PacketReader& pr, uint16_t oc) { |
178 | // For opcode UPDATE and where the DNSRecord is an answer record, we don't care about content, because this is | |
179 | // not used within the prerequisite section of RFC2136, so - we can simply use unknownrecordcontent. | |
180 | // For section 3.2.3, we do need content so we need to get it properly. But only for the correct Qclasses. | |
181 | if (oc == Opcode::Update && dr.d_place == DNSRecord::Answer && dr.d_class != 1) | |
182 | return new UnknownRecordContent(dr, pr); | |
914353ca | 183 | |
7945d472 RA |
184 | uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT |
185 | ||
186 | typemap_t::const_iterator i=getTypemap().find(make_pair(searchclass, dr.d_type)); | |
187 | if(i==getTypemap().end() || !i->second) { | |
188 | return new UnknownRecordContent(dr, pr); | |
189 | } | |
190 | ||
191 | return i->second(dr, pr); | |
192 | } | |
193 | ||
194 | ||
49a06471 BH |
195 | DNSRecordContent::typemap_t& DNSRecordContent::getTypemap() |
196 | { | |
197 | static DNSRecordContent::typemap_t typemap; | |
198 | return typemap; | |
199 | } | |
200 | ||
108c321e | 201 | DNSRecordContent::n2typemap_t& DNSRecordContent::getN2Typemap() |
49a06471 | 202 | { |
93e4cb97 BH |
203 | static DNSRecordContent::n2typemap_t n2typemap; |
204 | return n2typemap; | |
49a06471 BH |
205 | } |
206 | ||
108c321e BH |
207 | DNSRecordContent::t2namemap_t& DNSRecordContent::getT2Namemap() |
208 | { | |
93e4cb97 BH |
209 | static DNSRecordContent::t2namemap_t t2namemap; |
210 | return t2namemap; | |
108c321e BH |
211 | } |
212 | ||
213 | ||
49a06471 BH |
214 | DNSRecordContent::zmakermap_t& DNSRecordContent::getZmakermap() |
215 | { | |
216 | static DNSRecordContent::zmakermap_t zmakermap; | |
217 | return zmakermap; | |
218 | } | |
219 | ||
ff6a1e7b BH |
220 | void MOADNSParser::init(const char *packet, unsigned int len) |
221 | { | |
222 | if(len < sizeof(dnsheader)) | |
223 | throw MOADNSException("Packet shorter than minimal header"); | |
224 | ||
225 | memcpy(&d_header, packet, sizeof(dnsheader)); | |
226 | ||
7945d472 | 227 | if(d_header.opcode != Opcode::Query && d_header.opcode != Opcode::Notify && d_header.opcode != Opcode::Update) |
f27e6356 BH |
228 | throw MOADNSException("Can't parse non-query packet with opcode="+ lexical_cast<string>(d_header.opcode)); |
229 | ||
ff6a1e7b BH |
230 | d_header.qdcount=ntohs(d_header.qdcount); |
231 | d_header.ancount=ntohs(d_header.ancount); | |
232 | d_header.nscount=ntohs(d_header.nscount); | |
233 | d_header.arcount=ntohs(d_header.arcount); | |
234 | ||
092f210a | 235 | uint16_t contentlen=len-sizeof(dnsheader); |
ff6a1e7b BH |
236 | |
237 | d_content.resize(contentlen); | |
238 | copy(packet+sizeof(dnsheader), packet+len, d_content.begin()); | |
239 | ||
629eaf49 | 240 | unsigned int n=0; |
ff6a1e7b BH |
241 | |
242 | PacketReader pr(d_content); | |
7b1469bb | 243 | bool validPacket=false; |
512c8492 | 244 | try { |
00bf10d3 BH |
245 | d_qtype = d_qclass = 0; // sometimes replies come in with no question, don't present garbage then |
246 | ||
40207e64 BH |
247 | for(n=0;n < d_header.qdcount; ++n) { |
248 | d_qname=pr.getLabel(); | |
249 | d_qtype=pr.get16BitInt(); | |
250 | d_qclass=pr.get16BitInt(); | |
251 | } | |
252 | ||
512c8492 BH |
253 | struct dnsrecordheader ah; |
254 | vector<unsigned char> record; | |
7b1469bb | 255 | validPacket=true; |
c4ac5865 | 256 | for(n=0;n < (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount); ++n) { |
512c8492 BH |
257 | DNSRecord dr; |
258 | ||
259 | if(n < d_header.ancount) | |
4957a608 | 260 | dr.d_place=DNSRecord::Answer; |
512c8492 | 261 | else if(n < d_header.ancount + d_header.nscount) |
4957a608 | 262 | dr.d_place=DNSRecord::Nameserver; |
512c8492 | 263 | else |
4957a608 | 264 | dr.d_place=DNSRecord::Additional; |
512c8492 | 265 | |
57e5f5f7 BH |
266 | unsigned int recordStartPos=pr.d_pos; |
267 | ||
512c8492 BH |
268 | string label=pr.getLabel(); |
269 | ||
270 | pr.getDnsrecordheader(ah); | |
271 | dr.d_ttl=ah.d_ttl; | |
272 | dr.d_type=ah.d_type; | |
273 | dr.d_class=ah.d_class; | |
274 | ||
275 | dr.d_label=label; | |
276 | dr.d_clen=ah.d_clen; | |
7b1469bb | 277 | |
7945d472 | 278 | dr.d_content=boost::shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(dr, pr, d_header.opcode)); |
bc9b5203 | 279 | d_answers.push_back(make_pair(dr, pr.d_pos)); |
57e5f5f7 BH |
280 | |
281 | if(dr.d_type == QType::TSIG && dr.d_class == 0xff) | |
4957a608 | 282 | d_tsigPos = recordStartPos + sizeof(struct dnsheader); |
512c8492 | 283 | } |
e5986c84 BH |
284 | |
285 | #if 0 | |
512c8492 | 286 | if(pr.d_pos!=contentlen) { |
29a14b24 | 287 | throw MOADNSException("Packet ("+d_qname+"|#"+lexical_cast<string>(d_qtype)+") has trailing garbage ("+ lexical_cast<string>(pr.d_pos) + " < " + |
232f0877 | 288 | lexical_cast<string>(contentlen) + ")"); |
ff6a1e7b | 289 | } |
e5986c84 | 290 | #endif |
512c8492 | 291 | } |
10f4eea8 | 292 | catch(std::out_of_range &re) { |
629eaf49 BH |
293 | if(validPacket && d_header.tc) { // don't sweat it over truncated packets, but do adjust an, ns and arcount |
294 | if(n < d_header.ancount) { | |
4957a608 | 295 | d_header.ancount=n; d_header.nscount = d_header.arcount = 0; |
629eaf49 BH |
296 | } |
297 | else if(n < d_header.ancount + d_header.nscount) { | |
4957a608 | 298 | d_header.nscount = n - d_header.ancount; d_header.arcount=0; |
629eaf49 BH |
299 | } |
300 | else { | |
4957a608 | 301 | d_header.arcount = n - d_header.ancount - d_header.nscount; |
629eaf49 BH |
302 | } |
303 | } | |
304 | else { | |
7b1469bb | 305 | throw MOADNSException("Error parsing packet of "+lexical_cast<string>(len)+" bytes (rd="+ |
232f0877 CH |
306 | lexical_cast<string>(d_header.rd)+ |
307 | "), out of bounds: "+string(re.what())); | |
629eaf49 | 308 | } |
ff6a1e7b | 309 | } |
ff6a1e7b BH |
310 | } |
311 | ||
10321a98 | 312 | |
ff6a1e7b BH |
313 | void PacketReader::getDnsrecordheader(struct dnsrecordheader &ah) |
314 | { | |
315 | unsigned int n; | |
316 | unsigned char *p=reinterpret_cast<unsigned char*>(&ah); | |
317 | ||
318 | for(n=0; n < sizeof(dnsrecordheader); ++n) | |
319 | p[n]=d_content.at(d_pos++); | |
320 | ||
321 | ah.d_type=ntohs(ah.d_type); | |
322 | ah.d_class=ntohs(ah.d_class); | |
323 | ah.d_clen=ntohs(ah.d_clen); | |
324 | ah.d_ttl=ntohl(ah.d_ttl); | |
8c1c9170 BH |
325 | |
326 | d_startrecordpos=d_pos; // needed for getBlob later on | |
327 | d_recordlen=ah.d_clen; | |
ff6a1e7b BH |
328 | } |
329 | ||
330 | ||
092f210a | 331 | void PacketReader::copyRecord(vector<unsigned char>& dest, uint16_t len) |
ff6a1e7b BH |
332 | { |
333 | dest.resize(len); | |
bff744a8 BH |
334 | if(!len) |
335 | return; | |
336 | ||
092f210a | 337 | for(uint16_t n=0;n<len;++n) { |
ff6a1e7b BH |
338 | dest.at(n)=d_content.at(d_pos++); |
339 | } | |
340 | } | |
341 | ||
092f210a | 342 | void PacketReader::copyRecord(unsigned char* dest, uint16_t len) |
ff6a1e7b BH |
343 | { |
344 | if(d_pos + len > d_content.size()) | |
10f4eea8 | 345 | throw std::out_of_range("Attempt to copy outside of packet"); |
ff6a1e7b | 346 | |
bff744a8 | 347 | memcpy(dest, &d_content.at(d_pos), len); |
ff6a1e7b BH |
348 | d_pos+=len; |
349 | } | |
350 | ||
341930bb BH |
351 | void PacketReader::xfr48BitInt(uint64_t& ret) |
352 | { | |
353 | ret=0; | |
354 | ret+=d_content.at(d_pos++); | |
355 | ret<<=8; | |
356 | ret+=d_content.at(d_pos++); | |
357 | ret<<=8; | |
358 | ret+=d_content.at(d_pos++); | |
359 | ret<<=8; | |
360 | ret+=d_content.at(d_pos++); | |
361 | ret<<=8; | |
362 | ret+=d_content.at(d_pos++); | |
363 | ret<<=8; | |
364 | ret+=d_content.at(d_pos++); | |
365 | } | |
ff6a1e7b | 366 | |
092f210a | 367 | uint32_t PacketReader::get32BitInt() |
ff6a1e7b | 368 | { |
092f210a | 369 | uint32_t ret=0; |
ff6a1e7b BH |
370 | ret+=d_content.at(d_pos++); |
371 | ret<<=8; | |
372 | ret+=d_content.at(d_pos++); | |
373 | ret<<=8; | |
374 | ret+=d_content.at(d_pos++); | |
375 | ret<<=8; | |
376 | ret+=d_content.at(d_pos++); | |
377 | ||
378 | return ret; | |
379 | } | |
380 | ||
381 | ||
092f210a | 382 | uint16_t PacketReader::get16BitInt() |
ff6a1e7b BH |
383 | { |
384 | return get16BitInt(d_content, d_pos); | |
385 | } | |
386 | ||
092f210a | 387 | uint16_t PacketReader::get16BitInt(const vector<unsigned char>&content, uint16_t& pos) |
ff6a1e7b | 388 | { |
092f210a | 389 | uint16_t ret=0; |
ff6a1e7b BH |
390 | ret+=content.at(pos++); |
391 | ret<<=8; | |
392 | ret+=content.at(pos++); | |
393 | ||
394 | return ret; | |
395 | } | |
396 | ||
49a06471 | 397 | uint8_t PacketReader::get8BitInt() |
ff6a1e7b BH |
398 | { |
399 | return d_content.at(d_pos++); | |
400 | } | |
401 | ||
ff6a1e7b BH |
402 | string PacketReader::getLabel(unsigned int recurs) |
403 | { | |
1ab67463 BH |
404 | string ret; |
405 | ret.reserve(40); | |
406 | getLabelFromContent(d_content, d_pos, ret, recurs++); | |
407 | return ret; | |
ff6a1e7b BH |
408 | } |
409 | ||
ef6a78d5 BH |
410 | static string txtEscape(const string &name) |
411 | { | |
412 | string ret; | |
5dba9d25 | 413 | char ebuf[5]; |
ef6a78d5 | 414 | |
5dba9d25 | 415 | for(string::const_iterator i=name.begin();i!=name.end();++i) { |
837f4b49 | 416 | if((unsigned char) *i > 127 || (unsigned char) *i < 32) { |
5dba9d25 PD |
417 | snprintf(ebuf, sizeof(ebuf), "\\%03u", (unsigned char)*i); |
418 | ret += ebuf; | |
419 | } | |
66b40966 | 420 | else if(*i=='"' || *i=='\\'){ |
ef6a78d5 BH |
421 | ret += '\\'; |
422 | ret += *i; | |
423 | } | |
424 | else | |
425 | ret += *i; | |
5dba9d25 | 426 | } |
ef6a78d5 BH |
427 | return ret; |
428 | } | |
429 | ||
430 | // exceptions thrown here do not result in logging in the main pdns auth server - just so you know! | |
431 | string PacketReader::getText(bool multi) | |
9d9c52ef BH |
432 | { |
433 | string ret; | |
434 | ret.reserve(40); | |
ef6a78d5 BH |
435 | while(d_pos < d_startrecordpos + d_recordlen ) { |
436 | if(!ret.empty()) { | |
437 | ret.append(1,' '); | |
438 | } | |
439 | unsigned char labellen=d_content.at(d_pos++); | |
440 | ||
441 | ret.append(1,'"'); | |
950f78df BH |
442 | if(labellen) { // no need to do anything for an empty string |
443 | string val(&d_content.at(d_pos), &d_content.at(d_pos+labellen-1)+1); | |
444 | ret.append(txtEscape(val)); // the end is one beyond the packet | |
445 | } | |
ef6a78d5 BH |
446 | ret.append(1,'"'); |
447 | d_pos+=labellen; | |
448 | if(!multi) | |
449 | break; | |
450 | } | |
9d9c52ef | 451 | |
9d9c52ef BH |
452 | return ret; |
453 | } | |
ff6a1e7b | 454 | |
ef6a78d5 | 455 | |
49a06471 | 456 | void PacketReader::getLabelFromContent(const vector<uint8_t>& content, uint16_t& frompos, string& ret, int recurs) |
ff6a1e7b | 457 | { |
7c923cc0 | 458 | if(recurs > 1000) // the forward reference-check below should make this test 100% obsolete |
ff6a1e7b | 459 | throw MOADNSException("Loop"); |
8682c32b | 460 | // it is tempting to call reserve on ret, but it turns out it creates a malloc/free storm in the loop |
ff6a1e7b BH |
461 | for(;;) { |
462 | unsigned char labellen=content.at(frompos++); | |
1ab67463 | 463 | |
ff6a1e7b | 464 | if(!labellen) { |
7738a23f | 465 | if(ret.empty()) |
4957a608 | 466 | ret.append(1,'.'); |
ff6a1e7b BH |
467 | break; |
468 | } | |
0ffd51d5 | 469 | else if((labellen & 0xc0) == 0xc0) { |
092f210a | 470 | uint16_t offset=256*(labellen & ~0xc0) + (unsigned int)content.at(frompos++) - sizeof(dnsheader); |
4957a608 | 471 | // cout<<"This is an offset, need to go to: "<<offset<<endl; |
7c923cc0 PD |
472 | |
473 | if(offset >= frompos-2) | |
474 | throw MOADNSException("forward reference during label decompression"); | |
1ab67463 | 475 | return getLabelFromContent(content, offset, ret, ++recurs); |
ff6a1e7b | 476 | } |
0ffd51d5 | 477 | else if(labellen > 63) |
478 | throw MOADNSException("Overly long label during label decompression ("+lexical_cast<string>((unsigned int)labellen)+")"); | |
ff6a1e7b | 479 | else { |
38e655b6 | 480 | // XXX FIXME THIS MIGHT BE VERY SLOW! |
8682c32b | 481 | |
38e655b6 | 482 | for(string::size_type n = 0 ; n < labellen; ++n, frompos++) { |
7ecd3576 | 483 | if(content.at(frompos)=='.' || content.at(frompos)=='\\') { |
4957a608 | 484 | ret.append(1, '\\'); |
7ecd3576 BH |
485 | ret.append(1, content[frompos]); |
486 | } | |
487 | else if(content.at(frompos)==' ') { | |
488 | ret+="\\032"; | |
489 | } | |
490 | else | |
491 | ret.append(1, content[frompos]); | |
38e655b6 | 492 | } |
7738a23f | 493 | ret.append(1,'.'); |
ff6a1e7b BH |
494 | } |
495 | } | |
ff6a1e7b | 496 | } |
8c1c9170 BH |
497 | |
498 | void PacketReader::xfrBlob(string& blob) | |
2bc83940 | 499 | try |
8c1c9170 | 500 | { |
e2c162d3 | 501 | if(d_recordlen && !(d_pos == (d_startrecordpos + d_recordlen))) |
7f7b8d55 BH |
502 | blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1); |
503 | else | |
504 | blob.clear(); | |
8c1c9170 BH |
505 | |
506 | d_pos = d_startrecordpos + d_recordlen; | |
507 | } | |
2bc83940 | 508 | catch(...) |
509 | { | |
510 | throw std::out_of_range("xfrBlob out of range"); | |
511 | } | |
59a0f653 | 512 | |
06ffdc52 BH |
513 | void PacketReader::xfrBlob(string& blob, int length) |
514 | { | |
2617464d | 515 | if(length) { |
0407751c | 516 | blob.assign(&d_content.at(d_pos), &d_content.at(d_pos + length - 1 ) + 1 ); |
2617464d BH |
517 | |
518 | d_pos += length; | |
519 | } | |
520 | else | |
521 | blob.clear(); | |
06ffdc52 BH |
522 | } |
523 | ||
524 | ||
e4090157 | 525 | void PacketReader::xfrHexBlob(string& blob, bool keepReading) |
59a0f653 BH |
526 | { |
527 | xfrBlob(blob); | |
528 | } | |
b8e0f341 | 529 | |
27ff60a3 | 530 | string simpleCompress(const string& elabel, const string& root) |
b8e0f341 | 531 | { |
27ff60a3 PD |
532 | string label=elabel; |
533 | // FIXME: this relies on the semi-canonical escaped output from getLabelFromContent | |
83b746fd | 534 | if(strchr(label.c_str(), '\\')) { |
535 | boost::replace_all(label, "\\.", "."); | |
536 | boost::replace_all(label, "\\032", " "); | |
537 | boost::replace_all(label, "\\\\", "\\"); | |
538 | } | |
b8e0f341 BH |
539 | typedef vector<pair<unsigned int, unsigned int> > parts_t; |
540 | parts_t parts; | |
541 | vstringtok(parts, label, "."); | |
542 | string ret; | |
543 | ret.reserve(label.size()+4); | |
544 | for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) { | |
05a38bfa | 545 | 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 |
546 | const unsigned char rootptr[2]={0xc0,0x11}; |
547 | ret.append((const char *) rootptr, 2); | |
7127879f BH |
548 | return ret; |
549 | } | |
b8e0f341 BH |
550 | ret.append(1, (char)(i->second - i->first)); |
551 | ret.append(label.c_str() + i->first, i->second - i->first); | |
552 | } | |
553 | ret.append(1, (char)0); | |
554 | return ret; | |
555 | } | |
556 | ||
7127879f | 557 | |
b8e0f341 BH |
558 | void simpleExpandTo(const string& label, unsigned int frompos, string& ret) |
559 | { | |
560 | unsigned int labellen=0; | |
53edfc45 | 561 | while((labellen=(unsigned char)label.at(frompos++))) { |
b8e0f341 BH |
562 | ret.append(label.c_str()+frompos, labellen); |
563 | ret.append(1,'.'); | |
564 | frompos+=labellen; | |
565 | } | |
566 | } | |
2c73e580 BH |
567 | |
568 | /** Simple DNSPacketMangler. Ritual is: get a pointer into the packet and moveOffset() to beyond your needs | |
569 | * If you survive that, feel free to read from the pointer */ | |
570 | class DNSPacketMangler | |
571 | { | |
572 | public: | |
573 | explicit DNSPacketMangler(std::string& packet) | |
574 | : d_packet(packet), d_notyouroffset(12), d_offset(d_notyouroffset) | |
575 | {} | |
576 | ||
577 | void skipLabel() | |
578 | { | |
579 | uint8_t len; | |
580 | while((len=get8BitInt())) { | |
581 | if(len >= 0xc0) { // extended label | |
232f0877 CH |
582 | get8BitInt(); |
583 | return; | |
2c73e580 BH |
584 | } |
585 | skipBytes(len); | |
586 | } | |
587 | } | |
588 | void skipBytes(uint16_t bytes) | |
589 | { | |
590 | moveOffset(bytes); | |
591 | } | |
592 | uint16_t get16BitInt() | |
593 | { | |
594 | const char* p = d_packet.c_str() + d_offset; | |
595 | moveOffset(2); | |
596 | uint16_t ret; | |
597 | memcpy(&ret, (void*)p, 2); | |
598 | return ntohs(ret); | |
599 | } | |
600 | ||
601 | uint8_t get8BitInt() | |
602 | { | |
603 | const char* p = d_packet.c_str() + d_offset; | |
604 | moveOffset(1); | |
605 | return *p; | |
606 | } | |
607 | ||
608 | void skipRData() | |
609 | { | |
610 | int toskip = get16BitInt(); | |
611 | moveOffset(toskip); | |
612 | } | |
613 | void decreaseAndSkip32BitInt(uint32_t decrease) | |
614 | { | |
615 | const char *p = (const char*)d_packet.c_str() + d_offset; | |
616 | moveOffset(4); | |
617 | ||
618 | uint32_t tmp; | |
619 | memcpy(&tmp, (void*) p, sizeof(tmp)); | |
620 | tmp = ntohl(tmp); | |
621 | tmp-=decrease; | |
622 | tmp = htonl(tmp); | |
623 | d_packet.replace(d_offset-4, sizeof(tmp), (const char*)&tmp, sizeof(tmp)); | |
624 | } | |
625 | private: | |
626 | void moveOffset(uint16_t by) | |
627 | { | |
628 | d_notyouroffset += by; | |
629 | if(d_notyouroffset > d_packet.length()) | |
10f4eea8 | 630 | throw std::out_of_range("dns packet out of range: "+lexical_cast<string>(d_notyouroffset) +" > " |
2c73e580 BH |
631 | + lexical_cast<string>(d_packet.length()) ); |
632 | } | |
633 | std::string& d_packet; | |
634 | ||
635 | uint32_t d_notyouroffset; // only 'moveOffset' can touch this | |
636 | const uint32_t& d_offset; // look.. but don't touch | |
637 | ||
638 | }; | |
639 | ||
640 | // method of operation: silently fail if it doesn't work - we're only trying to be nice, don't fall over on it | |
641 | void ageDNSPacket(std::string& packet, uint32_t seconds) | |
642 | { | |
44b67752 | 643 | if(packet.length() < sizeof(dnsheader)) |
2c73e580 BH |
644 | return; |
645 | try | |
646 | { | |
44b67752 BH |
647 | dnsheader dh; |
648 | memcpy((void*)&dh, (const dnsheader*)packet.c_str(), sizeof(dh)); | |
649 | int numrecords = ntohs(dh.ancount) + ntohs(dh.nscount) + ntohs(dh.arcount); | |
2c73e580 BH |
650 | DNSPacketMangler dpm(packet); |
651 | ||
652 | int n; | |
44b67752 | 653 | for(n=0; n < ntohs(dh.qdcount) ; ++n) { |
2c73e580 BH |
654 | dpm.skipLabel(); |
655 | dpm.skipBytes(4); // qtype, qclass | |
656 | } | |
657 | // cerr<<"Skipped "<<n<<" questions, now parsing "<<numrecords<<" records"<<endl; | |
658 | for(n=0; n < numrecords; ++n) { | |
659 | dpm.skipLabel(); | |
660 | ||
661 | uint16_t dnstype = dpm.get16BitInt(); | |
88a754aa | 662 | /* uint16_t dnsclass = */ dpm.get16BitInt(); |
2c73e580 BH |
663 | |
664 | if(dnstype == QType::OPT) // not aging that one with a stick | |
232f0877 | 665 | break; |
2c73e580 BH |
666 | |
667 | dpm.decreaseAndSkip32BitInt(seconds); | |
668 | dpm.skipRData(); | |
669 | } | |
670 | } | |
671 | catch(...) | |
672 | { | |
673 | return; | |
674 | } | |
675 | } |