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