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