]>
Commit | Line | Data |
---|---|---|
4192ca66 BH |
1 | /* |
2 | PowerDNS Versatile Database Driven Nameserver | |
4cf74e6f | 3 | Copyright (C) 2005 - 2008 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 | ||
9 | This program is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | GNU General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU General Public License | |
15 | along with this program; if not, write to the Free Software | |
06bd9ccf | 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
4192ca66 BH |
17 | */ |
18 | ||
ff6a1e7b | 19 | #include "dnsparser.hh" |
6c0670c3 | 20 | #include "dnswriter.hh" |
ff6a1e7b | 21 | #include <boost/lexical_cast.hpp> |
4cf74e6f | 22 | #include <boost/algorithm/string.hpp> |
ff6a1e7b BH |
23 | |
24 | using namespace boost; | |
25 | ||
26 | class UnknownRecordContent : public DNSRecordContent | |
27 | { | |
28 | public: | |
7fc69fd0 | 29 | UnknownRecordContent(const DNSRecord& dr, PacketReader& pr) |
ea634573 | 30 | : DNSRecordContent(dr.d_type), d_dr(dr) |
ff6a1e7b | 31 | { |
7fc69fd0 | 32 | pr.copyRecord(d_record, dr.d_clen); |
ff6a1e7b BH |
33 | } |
34 | ||
ea634573 | 35 | UnknownRecordContent(const string& zone) : DNSRecordContent(0) |
6c0670c3 BH |
36 | { |
37 | d_record.insert(d_record.end(), zone.begin(), zone.end()); | |
38 | } | |
39 | ||
ff6a1e7b BH |
40 | string getZoneRepresentation() const |
41 | { | |
42 | ostringstream str; | |
705f31ae | 43 | str<<"\\# "<<(unsigned int)d_record.size()<<" "; |
ff6a1e7b BH |
44 | char hex[4]; |
45 | for(size_t n=0; n<d_record.size(); ++n) { | |
46 | snprintf(hex,sizeof(hex)-1, "%02x", d_record.at(n)); | |
47 | str << hex; | |
48 | } | |
ff6a1e7b BH |
49 | return str.str(); |
50 | } | |
51 | ||
6c0670c3 BH |
52 | void toPacket(DNSPacketWriter& pw) |
53 | { | |
d0b471d3 | 54 | string tmp((char*)&*d_record.begin(), d_record.size()); |
6c0670c3 BH |
55 | vector<string> parts; |
56 | stringtok(parts, tmp); | |
4cf74e6f | 57 | if(parts.size()!=3 && !(parts.size()==2 && equals(parts[1],"0")) ) |
a9af3782 | 58 | throw MOADNSException("Unknown record was stored incorrectly, need 3 fields, got "+lexical_cast<string>(parts.size())+": "+tmp ); |
4cf74e6f | 59 | const string& relevant=(parts.size() > 2) ? parts[2] : ""; |
6c0670c3 BH |
60 | unsigned int total=atoi(parts[1].c_str()); |
61 | if(relevant.size()!=2*total) | |
62 | throw runtime_error("invalid unknown record"); | |
63 | string out; | |
cc835f89 | 64 | out.reserve(total+1); |
6c0670c3 BH |
65 | for(unsigned int n=0; n < total; ++n) { |
66 | int c; | |
67 | sscanf(relevant.c_str()+2*n, "%02x", &c); | |
68 | out.append(1, (char)c); | |
69 | } | |
70 | pw.xfrBlob(out); | |
71 | } | |
ff6a1e7b | 72 | private: |
6c0670c3 BH |
73 | DNSRecord d_dr; |
74 | vector<uint8_t> d_record; | |
ff6a1e7b BH |
75 | }; |
76 | ||
ea634573 BH |
77 | static const string EncodeDNSLabel(const string& input) |
78 | { | |
79 | typedef vector<string> parts_t; | |
80 | parts_t parts; | |
81 | stringtok(parts,input,"."); | |
82 | string ret; | |
83 | for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) { | |
84 | ret.append(1,(char)i->length()); | |
85 | ret.append(*i); | |
86 | } | |
87 | ret.append(1,(char)0); | |
88 | return ret; | |
89 | } | |
ff6a1e7b | 90 | |
ea634573 BH |
91 | shared_ptr<DNSRecordContent> DNSRecordContent::unserialize(const string& qname, uint16_t qtype, const string& serialized) |
92 | { | |
93 | dnsheader dnsheader; | |
94 | memset(&dnsheader, 0, sizeof(dnsheader)); | |
95 | dnsheader.qdcount=htons(1); | |
96 | dnsheader.ancount=htons(1); | |
97 | ||
98 | vector<uint8_t> packet; // build pseudo packet | |
678ce973 BH |
99 | |
100 | /* will look like: dnsheader, 5 bytes, encoded qname, dns record header, serialized data */ | |
ea634573 BH |
101 | |
102 | string encoded=EncodeDNSLabel(qname); | |
678ce973 BH |
103 | |
104 | packet.resize(sizeof(dnsheader) + 5 + encoded.size() + sizeof(struct dnsrecordheader) + serialized.size()); | |
105 | ||
106 | uint16_t pos=0; | |
107 | ||
108 | memcpy(&packet[0], &dnsheader, sizeof(dnsheader)); pos+=sizeof(dnsheader); | |
109 | ||
110 | char tmp[6]="\x0" "\x0\x1" "\x0\x1"; // root question for ns_t_a | |
111 | memcpy(&packet[pos], &tmp, 5); pos+=5; | |
112 | ||
705f31ae | 113 | memcpy(&packet[pos], encoded.c_str(), encoded.size()); pos+=(uint16_t)encoded.size(); |
ea634573 BH |
114 | |
115 | struct dnsrecordheader drh; | |
116 | drh.d_type=htons(qtype); | |
117 | drh.d_class=htons(1); | |
118 | drh.d_ttl=0; | |
119 | drh.d_clen=htons(serialized.size()); | |
120 | ||
678ce973 | 121 | memcpy(&packet[pos], &drh, sizeof(drh)); pos+=sizeof(drh); |
705f31ae | 122 | memcpy(&packet[pos], serialized.c_str(), serialized.size()); pos+=(uint16_t)serialized.size(); |
ea634573 | 123 | |
705f31ae | 124 | MOADNSParser mdp((char*)&*packet.begin(), (unsigned int)packet.size()); |
ea634573 BH |
125 | shared_ptr<DNSRecordContent> ret= mdp.d_answers.begin()->first.d_content; |
126 | ret->header.d_type=ret->d_qtype; | |
127 | ret->label=mdp.d_answers.begin()->first.d_label; | |
128 | ret->header.d_ttl=mdp.d_answers.begin()->first.d_ttl; | |
129 | return ret; | |
130 | } | |
ff6a1e7b | 131 | |
7fc69fd0 | 132 | DNSRecordContent* DNSRecordContent::mastermake(const DNSRecord &dr, |
ff6a1e7b BH |
133 | PacketReader& pr) |
134 | { | |
7f7b8d55 BH |
135 | uint16_t searchclass = (dr.d_type == QType::OPT) ? 1 : dr.d_class; // class is invalid for OPT |
136 | ||
137 | typemap_t::const_iterator i=getTypemap().find(make_pair(searchclass, dr.d_type)); | |
49a06471 | 138 | if(i==getTypemap().end() || !i->second) { |
7fc69fd0 | 139 | return new UnknownRecordContent(dr, pr); |
ff6a1e7b | 140 | } |
945a9ad4 | 141 | |
7fc69fd0 | 142 | return i->second(dr, pr); |
ff6a1e7b BH |
143 | } |
144 | ||
6c0670c3 BH |
145 | DNSRecordContent* DNSRecordContent::mastermake(uint16_t qtype, uint16_t qclass, |
146 | const string& content) | |
147 | { | |
49a06471 BH |
148 | zmakermap_t::const_iterator i=getZmakermap().find(make_pair(qclass, qtype)); |
149 | if(i==getZmakermap().end()) { | |
6c0670c3 BH |
150 | return new UnknownRecordContent(content); |
151 | } | |
152 | ||
153 | return i->second(content); | |
154 | } | |
155 | ||
ff6a1e7b | 156 | |
49a06471 BH |
157 | DNSRecordContent::typemap_t& DNSRecordContent::getTypemap() |
158 | { | |
159 | static DNSRecordContent::typemap_t typemap; | |
160 | return typemap; | |
161 | } | |
162 | ||
163 | DNSRecordContent::namemap_t& DNSRecordContent::getNamemap() | |
164 | { | |
165 | static DNSRecordContent::namemap_t namemap; | |
166 | return namemap; | |
167 | } | |
168 | ||
169 | DNSRecordContent::zmakermap_t& DNSRecordContent::getZmakermap() | |
170 | { | |
171 | static DNSRecordContent::zmakermap_t zmakermap; | |
172 | return zmakermap; | |
173 | } | |
174 | ||
175 | ||
ff6a1e7b BH |
176 | |
177 | void MOADNSParser::init(const char *packet, unsigned int len) | |
178 | { | |
179 | if(len < sizeof(dnsheader)) | |
180 | throw MOADNSException("Packet shorter than minimal header"); | |
181 | ||
182 | memcpy(&d_header, packet, sizeof(dnsheader)); | |
183 | ||
94dd1374 | 184 | if(d_header.opcode!=0 && d_header.opcode != 4) // notification |
f27e6356 BH |
185 | throw MOADNSException("Can't parse non-query packet with opcode="+ lexical_cast<string>(d_header.opcode)); |
186 | ||
ff6a1e7b BH |
187 | d_header.qdcount=ntohs(d_header.qdcount); |
188 | d_header.ancount=ntohs(d_header.ancount); | |
189 | d_header.nscount=ntohs(d_header.nscount); | |
190 | d_header.arcount=ntohs(d_header.arcount); | |
191 | ||
092f210a | 192 | uint16_t contentlen=len-sizeof(dnsheader); |
ff6a1e7b BH |
193 | |
194 | d_content.resize(contentlen); | |
195 | copy(packet+sizeof(dnsheader), packet+len, d_content.begin()); | |
196 | ||
629eaf49 | 197 | unsigned int n=0; |
ff6a1e7b BH |
198 | |
199 | PacketReader pr(d_content); | |
7b1469bb | 200 | bool validPacket=false; |
512c8492 | 201 | try { |
00bf10d3 BH |
202 | d_qtype = d_qclass = 0; // sometimes replies come in with no question, don't present garbage then |
203 | ||
40207e64 BH |
204 | for(n=0;n < d_header.qdcount; ++n) { |
205 | d_qname=pr.getLabel(); | |
206 | d_qtype=pr.get16BitInt(); | |
207 | d_qclass=pr.get16BitInt(); | |
208 | } | |
209 | ||
512c8492 BH |
210 | struct dnsrecordheader ah; |
211 | vector<unsigned char> record; | |
7b1469bb | 212 | validPacket=true; |
c4ac5865 | 213 | for(n=0;n < (unsigned int)(d_header.ancount + d_header.nscount + d_header.arcount); ++n) { |
512c8492 BH |
214 | DNSRecord dr; |
215 | ||
216 | if(n < d_header.ancount) | |
217 | dr.d_place=DNSRecord::Answer; | |
218 | else if(n < d_header.ancount + d_header.nscount) | |
219 | dr.d_place=DNSRecord::Nameserver; | |
220 | else | |
221 | dr.d_place=DNSRecord::Additional; | |
222 | ||
57e5f5f7 BH |
223 | unsigned int recordStartPos=pr.d_pos; |
224 | ||
512c8492 BH |
225 | string label=pr.getLabel(); |
226 | ||
227 | pr.getDnsrecordheader(ah); | |
228 | dr.d_ttl=ah.d_ttl; | |
229 | dr.d_type=ah.d_type; | |
230 | dr.d_class=ah.d_class; | |
231 | ||
232 | dr.d_label=label; | |
233 | dr.d_clen=ah.d_clen; | |
7b1469bb | 234 | |
512c8492 | 235 | dr.d_content=boost::shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(dr, pr)); |
bc9b5203 | 236 | d_answers.push_back(make_pair(dr, pr.d_pos)); |
57e5f5f7 BH |
237 | |
238 | if(dr.d_type == QType::TSIG && dr.d_class == 0xff) | |
239 | d_tsigPos = recordStartPos + sizeof(struct dnsheader); | |
512c8492 | 240 | } |
e5986c84 BH |
241 | |
242 | #if 0 | |
512c8492 | 243 | if(pr.d_pos!=contentlen) { |
29a14b24 BH |
244 | throw MOADNSException("Packet ("+d_qname+"|#"+lexical_cast<string>(d_qtype)+") has trailing garbage ("+ lexical_cast<string>(pr.d_pos) + " < " + |
245 | lexical_cast<string>(contentlen) + ")"); | |
ff6a1e7b | 246 | } |
e5986c84 | 247 | #endif |
512c8492 BH |
248 | } |
249 | catch(out_of_range &re) { | |
629eaf49 BH |
250 | if(validPacket && d_header.tc) { // don't sweat it over truncated packets, but do adjust an, ns and arcount |
251 | if(n < d_header.ancount) { | |
252 | d_header.ancount=n; d_header.nscount = d_header.arcount = 0; | |
253 | } | |
254 | else if(n < d_header.ancount + d_header.nscount) { | |
255 | d_header.nscount = n - d_header.ancount; d_header.arcount=0; | |
256 | } | |
257 | else { | |
258 | d_header.arcount = n - d_header.ancount - d_header.nscount; | |
259 | } | |
260 | } | |
261 | else { | |
7b1469bb BH |
262 | throw MOADNSException("Error parsing packet of "+lexical_cast<string>(len)+" bytes (rd="+ |
263 | lexical_cast<string>(d_header.rd)+ | |
264 | "), out of bounds: "+string(re.what())); | |
629eaf49 | 265 | } |
ff6a1e7b | 266 | } |
ff6a1e7b BH |
267 | } |
268 | ||
10321a98 | 269 | |
ff6a1e7b BH |
270 | void PacketReader::getDnsrecordheader(struct dnsrecordheader &ah) |
271 | { | |
272 | unsigned int n; | |
273 | unsigned char *p=reinterpret_cast<unsigned char*>(&ah); | |
274 | ||
275 | for(n=0; n < sizeof(dnsrecordheader); ++n) | |
276 | p[n]=d_content.at(d_pos++); | |
277 | ||
278 | ah.d_type=ntohs(ah.d_type); | |
279 | ah.d_class=ntohs(ah.d_class); | |
280 | ah.d_clen=ntohs(ah.d_clen); | |
281 | ah.d_ttl=ntohl(ah.d_ttl); | |
8c1c9170 BH |
282 | |
283 | d_startrecordpos=d_pos; // needed for getBlob later on | |
284 | d_recordlen=ah.d_clen; | |
ff6a1e7b BH |
285 | } |
286 | ||
287 | ||
092f210a | 288 | void PacketReader::copyRecord(vector<unsigned char>& dest, uint16_t len) |
ff6a1e7b BH |
289 | { |
290 | dest.resize(len); | |
bff744a8 BH |
291 | if(!len) |
292 | return; | |
293 | ||
092f210a | 294 | for(uint16_t n=0;n<len;++n) { |
ff6a1e7b BH |
295 | dest.at(n)=d_content.at(d_pos++); |
296 | } | |
297 | } | |
298 | ||
092f210a | 299 | void PacketReader::copyRecord(unsigned char* dest, uint16_t len) |
ff6a1e7b BH |
300 | { |
301 | if(d_pos + len > d_content.size()) | |
302 | throw MOADNSException("Attempt to copy outside of packet"); | |
303 | ||
bff744a8 | 304 | memcpy(dest, &d_content.at(d_pos), len); |
ff6a1e7b BH |
305 | d_pos+=len; |
306 | } | |
307 | ||
341930bb BH |
308 | void PacketReader::xfr48BitInt(uint64_t& ret) |
309 | { | |
310 | ret=0; | |
311 | ret+=d_content.at(d_pos++); | |
312 | ret<<=8; | |
313 | ret+=d_content.at(d_pos++); | |
314 | ret<<=8; | |
315 | ret+=d_content.at(d_pos++); | |
316 | ret<<=8; | |
317 | ret+=d_content.at(d_pos++); | |
318 | ret<<=8; | |
319 | ret+=d_content.at(d_pos++); | |
320 | ret<<=8; | |
321 | ret+=d_content.at(d_pos++); | |
322 | } | |
ff6a1e7b | 323 | |
092f210a | 324 | uint32_t PacketReader::get32BitInt() |
ff6a1e7b | 325 | { |
092f210a | 326 | uint32_t ret=0; |
ff6a1e7b BH |
327 | ret+=d_content.at(d_pos++); |
328 | ret<<=8; | |
329 | ret+=d_content.at(d_pos++); | |
330 | ret<<=8; | |
331 | ret+=d_content.at(d_pos++); | |
332 | ret<<=8; | |
333 | ret+=d_content.at(d_pos++); | |
334 | ||
335 | return ret; | |
336 | } | |
337 | ||
338 | ||
092f210a | 339 | uint16_t PacketReader::get16BitInt() |
ff6a1e7b BH |
340 | { |
341 | return get16BitInt(d_content, d_pos); | |
342 | } | |
343 | ||
092f210a | 344 | uint16_t PacketReader::get16BitInt(const vector<unsigned char>&content, uint16_t& pos) |
ff6a1e7b | 345 | { |
092f210a | 346 | uint16_t ret=0; |
ff6a1e7b BH |
347 | ret+=content.at(pos++); |
348 | ret<<=8; | |
349 | ret+=content.at(pos++); | |
350 | ||
351 | return ret; | |
352 | } | |
353 | ||
49a06471 | 354 | uint8_t PacketReader::get8BitInt() |
ff6a1e7b BH |
355 | { |
356 | return d_content.at(d_pos++); | |
357 | } | |
358 | ||
359 | ||
360 | string PacketReader::getLabel(unsigned int recurs) | |
361 | { | |
1ab67463 BH |
362 | string ret; |
363 | ret.reserve(40); | |
364 | getLabelFromContent(d_content, d_pos, ret, recurs++); | |
365 | return ret; | |
ff6a1e7b BH |
366 | } |
367 | ||
ef6a78d5 BH |
368 | static string txtEscape(const string &name) |
369 | { | |
370 | string ret; | |
371 | ||
372 | for(string::const_iterator i=name.begin();i!=name.end();++i) | |
373 | if(*i=='"' || *i=='\\'){ | |
374 | ret += '\\'; | |
375 | ret += *i; | |
376 | } | |
377 | else | |
378 | ret += *i; | |
379 | return ret; | |
380 | } | |
381 | ||
382 | // exceptions thrown here do not result in logging in the main pdns auth server - just so you know! | |
383 | string PacketReader::getText(bool multi) | |
9d9c52ef BH |
384 | { |
385 | string ret; | |
386 | ret.reserve(40); | |
ef6a78d5 BH |
387 | while(d_pos < d_startrecordpos + d_recordlen ) { |
388 | if(!ret.empty()) { | |
389 | ret.append(1,' '); | |
390 | } | |
391 | unsigned char labellen=d_content.at(d_pos++); | |
392 | ||
393 | ret.append(1,'"'); | |
950f78df BH |
394 | if(labellen) { // no need to do anything for an empty string |
395 | string val(&d_content.at(d_pos), &d_content.at(d_pos+labellen-1)+1); | |
396 | ret.append(txtEscape(val)); // the end is one beyond the packet | |
397 | } | |
ef6a78d5 BH |
398 | ret.append(1,'"'); |
399 | d_pos+=labellen; | |
400 | if(!multi) | |
401 | break; | |
402 | } | |
9d9c52ef | 403 | |
9d9c52ef BH |
404 | return ret; |
405 | } | |
ff6a1e7b | 406 | |
ef6a78d5 | 407 | |
49a06471 | 408 | void PacketReader::getLabelFromContent(const vector<uint8_t>& content, uint16_t& frompos, string& ret, int recurs) |
ff6a1e7b BH |
409 | { |
410 | if(recurs > 10) | |
411 | throw MOADNSException("Loop"); | |
1ab67463 | 412 | |
ff6a1e7b BH |
413 | for(;;) { |
414 | unsigned char labellen=content.at(frompos++); | |
1ab67463 | 415 | |
ff6a1e7b | 416 | if(!labellen) { |
7738a23f BH |
417 | if(ret.empty()) |
418 | ret.append(1,'.'); | |
ff6a1e7b BH |
419 | break; |
420 | } | |
421 | if((labellen & 0xc0) == 0xc0) { | |
092f210a | 422 | uint16_t offset=256*(labellen & ~0xc0) + (unsigned int)content.at(frompos++) - sizeof(dnsheader); |
ff6a1e7b | 423 | // cout<<"This is an offset, need to go to: "<<offset<<endl; |
1ab67463 | 424 | return getLabelFromContent(content, offset, ret, ++recurs); |
ff6a1e7b BH |
425 | } |
426 | else { | |
38e655b6 BH |
427 | // XXX FIXME THIS MIGHT BE VERY SLOW! |
428 | ret.reserve(ret.size() + labellen + 2); | |
429 | for(string::size_type n = 0 ; n < labellen; ++n, frompos++) { | |
430 | if(content.at(frompos)=='.') | |
431 | ret.append(1, '\\'); | |
432 | ret.append(1, content[frompos]); | |
433 | } | |
7738a23f | 434 | ret.append(1,'.'); |
ff6a1e7b BH |
435 | } |
436 | } | |
ff6a1e7b | 437 | } |
8c1c9170 BH |
438 | |
439 | void PacketReader::xfrBlob(string& blob) | |
440 | { | |
7f7b8d55 BH |
441 | if(d_recordlen) |
442 | blob.assign(&d_content.at(d_pos), &d_content.at(d_startrecordpos + d_recordlen - 1 ) + 1); | |
443 | else | |
444 | blob.clear(); | |
8c1c9170 BH |
445 | |
446 | d_pos = d_startrecordpos + d_recordlen; | |
447 | } | |
59a0f653 | 448 | |
06ffdc52 BH |
449 | void PacketReader::xfrBlob(string& blob, int length) |
450 | { | |
2617464d | 451 | if(length) { |
0407751c | 452 | blob.assign(&d_content.at(d_pos), &d_content.at(d_pos + length - 1 ) + 1 ); |
2617464d BH |
453 | |
454 | d_pos += length; | |
455 | } | |
456 | else | |
457 | blob.clear(); | |
06ffdc52 BH |
458 | } |
459 | ||
460 | ||
59a0f653 BH |
461 | void PacketReader::xfrHexBlob(string& blob) |
462 | { | |
463 | xfrBlob(blob); | |
464 | } | |
b8e0f341 | 465 | |
7127879f | 466 | string simpleCompress(const string& label, const string& root) |
b8e0f341 BH |
467 | { |
468 | typedef vector<pair<unsigned int, unsigned int> > parts_t; | |
469 | parts_t parts; | |
470 | vstringtok(parts, label, "."); | |
471 | string ret; | |
472 | ret.reserve(label.size()+4); | |
473 | for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) { | |
05a38bfa | 474 | if(!root.empty() && !strncasecmp(root.c_str(), label.c_str() + i->first, 1 + label.length() - i->first)) { // also match trailing 0, hence '1 +' |
7127879f BH |
475 | const char rootptr[2]={0xc0,0x11}; |
476 | ret.append(rootptr, 2); | |
477 | return ret; | |
478 | } | |
b8e0f341 BH |
479 | ret.append(1, (char)(i->second - i->first)); |
480 | ret.append(label.c_str() + i->first, i->second - i->first); | |
481 | } | |
482 | ret.append(1, (char)0); | |
483 | return ret; | |
484 | } | |
485 | ||
7127879f | 486 | |
b8e0f341 BH |
487 | void simpleExpandTo(const string& label, unsigned int frompos, string& ret) |
488 | { | |
489 | unsigned int labellen=0; | |
490 | while((labellen=label.at(frompos++))) { | |
491 | ret.append(label.c_str()+frompos, labellen); | |
492 | ret.append(1,'.'); | |
493 | frompos+=labellen; | |
494 | } | |
495 | } |