]>
Commit | Line | Data |
---|---|---|
12c86877 BH |
1 | /* |
2 | PowerDNS Versatile Database Driven Nameserver | |
657e9124 | 3 | Copyright (C) 2001 - 2008 PowerDNS.COM BV |
12c86877 BH |
4 | |
5 | This program is free software; you can redistribute it and/or modify | |
feccc9fc | 6 | it under the terms of the GNU General Public License version 2 as |
f28307ad | 7 | published by the Free Software Foundation |
12c86877 BH |
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 |
12c86877 | 17 | */ |
ff6a1e7b | 18 | |
12c86877 BH |
19 | #include "utility.hh" |
20 | #include <cstdio> | |
21 | ||
22 | #include <cstdlib> | |
23 | #include <sys/types.h> | |
24 | ||
25 | #include <iostream> | |
26 | ||
27 | #include <string> | |
28 | #include <errno.h> | |
b8e0f341 BH |
29 | #include <boost/tokenizer.hpp> |
30 | #include <boost/algorithm/string.hpp> | |
12c86877 BH |
31 | #include <algorithm> |
32 | ||
33 | #include "dns.hh" | |
34 | #include "dnsbackend.hh" | |
35 | #include "ahuexception.hh" | |
36 | #include "dnspacket.hh" | |
37 | #include "logger.hh" | |
38 | #include "arguments.hh" | |
b8e0f341 BH |
39 | #include "dnswriter.hh" |
40 | #include "dnsparser.hh" | |
7f7b8d55 | 41 | #include "dnsrecords.hh" |
12c86877 BH |
42 | |
43 | DNSPacket::DNSPacket() | |
44 | { | |
45 | d_wrapped=false; | |
46 | d_compress=true; | |
e9dd48f9 | 47 | d_tcp=false; |
7f7b8d55 | 48 | d_wantsnsid=false; |
12c86877 BH |
49 | } |
50 | ||
12c86877 BH |
51 | string DNSPacket::getString() |
52 | { | |
53 | return stringbuffer; | |
54 | } | |
55 | ||
b8e0f341 BH |
56 | const char *DNSPacket::getData(void) |
57 | { | |
58 | if(!d_wrapped) | |
59 | wrapup(); | |
60 | ||
61 | return stringbuffer.data(); | |
62 | } | |
63 | ||
64 | const char *DNSPacket::getRaw(void) | |
65 | { | |
66 | return stringbuffer.data(); | |
67 | } | |
68 | ||
12c86877 BH |
69 | string DNSPacket::getRemote() const |
70 | { | |
809fe23f | 71 | return remote.toString(); |
12c86877 BH |
72 | } |
73 | ||
092f210a | 74 | uint16_t DNSPacket::getRemotePort() const |
288f4aa9 | 75 | { |
809fe23f | 76 | return remote.sin4.sin_port; |
288f4aa9 | 77 | } |
12c86877 | 78 | |
12c86877 BH |
79 | DNSPacket::DNSPacket(const DNSPacket &orig) |
80 | { | |
81 | DLOG(L<<"DNSPacket copy constructor called!"<<endl); | |
82 | d_socket=orig.d_socket; | |
809fe23f | 83 | remote=orig.remote; |
12c86877 BH |
84 | len=orig.len; |
85 | d_qlen=orig.d_qlen; | |
86 | d_dt=orig.d_dt; | |
12c86877 | 87 | d_compress=orig.d_compress; |
f28307ad | 88 | d_tcp=orig.d_tcp; |
12c86877 BH |
89 | qtype=orig.qtype; |
90 | qclass=orig.qclass; | |
91 | qdomain=orig.qdomain; | |
657e9124 | 92 | d_maxreplylen = orig.d_maxreplylen; |
7f7b8d55 BH |
93 | d_ednsping = orig.d_ednsping; |
94 | d_wantsnsid = orig.d_wantsnsid; | |
12c86877 BH |
95 | rrs=orig.rrs; |
96 | ||
97 | d_wrapped=orig.d_wrapped; | |
98 | ||
99 | stringbuffer=orig.stringbuffer; | |
100 | d=orig.d; | |
12c86877 BH |
101 | } |
102 | ||
12c86877 BH |
103 | void DNSPacket::setRcode(int v) |
104 | { | |
105 | d.rcode=v; | |
106 | } | |
107 | ||
108 | void DNSPacket::setAnswer(bool b) | |
109 | { | |
110 | if(b) { | |
111 | stringbuffer.assign(12,(char)0); | |
112 | memset((void *)&d,0,sizeof(d)); | |
113 | ||
114 | d.qr=b; | |
115 | } | |
116 | } | |
117 | ||
118 | void DNSPacket::setA(bool b) | |
119 | { | |
120 | d.aa=b; | |
121 | } | |
122 | ||
092f210a | 123 | void DNSPacket::setID(uint16_t id) |
12c86877 BH |
124 | { |
125 | d.id=id; | |
126 | } | |
127 | ||
128 | void DNSPacket::setRA(bool b) | |
129 | { | |
130 | d.ra=b; | |
131 | } | |
132 | ||
133 | void DNSPacket::setRD(bool b) | |
134 | { | |
135 | d.rd=b; | |
136 | } | |
137 | ||
092f210a | 138 | void DNSPacket::setOpcode(uint16_t opcode) |
12c86877 BH |
139 | { |
140 | d.opcode=opcode; | |
141 | } | |
142 | ||
12c86877 | 143 | |
77235722 BH |
144 | void DNSPacket::clearRecords() |
145 | { | |
146 | rrs.clear(); | |
147 | } | |
148 | ||
12c86877 BH |
149 | void DNSPacket::addRecord(const DNSResourceRecord &rr) |
150 | { | |
151 | if(d_compress) | |
152 | for(vector<DNSResourceRecord>::const_iterator i=rrs.begin();i!=rrs.end();++i) | |
ab2249ae BH |
153 | if(rr.qname==i->qname && rr.qtype==i->qtype && rr.content==i->content) { |
154 | if(rr.qtype.getCode()!=QType::MX && rr.qtype.getCode()!=QType::SRV) | |
0b0ec1e7 | 155 | return; |
ab2249ae BH |
156 | if(rr.priority==i->priority) |
157 | return; | |
158 | } | |
12c86877 BH |
159 | |
160 | rrs.push_back(rr); | |
161 | } | |
162 | ||
b8e0f341 | 163 | // the functions below update the 'arcount' and 'ancount', plus they serialize themselves to the stringbuffer |
d3491d5a | 164 | |
34b37bbb | 165 | string& attodot(string &str) |
12c86877 BH |
166 | { |
167 | if(str.find_first_of("@")==string::npos) | |
168 | return str; | |
169 | ||
170 | for (unsigned int i = 0; i < str.length(); i++) | |
171 | { | |
172 | if (str[i] == '@') { | |
173 | str[i] = '.'; | |
174 | break; | |
175 | } else if (str[i] == '.') { | |
176 | str.insert(i++, "\\"); | |
177 | } | |
178 | } | |
179 | ||
180 | return str; | |
181 | } | |
182 | ||
34b37bbb | 183 | void fillSOAData(const string &content, SOAData &data) |
12c86877 BH |
184 | { |
185 | // content consists of fields separated by spaces: | |
186 | // nameservername hostmaster serial-number [refresh [retry [expire [ minimum] ] ] ] | |
187 | ||
188 | // fill out data with some plausible defaults: | |
189 | // 10800 3600 604800 3600 | |
190 | data.serial=0; | |
43d347d3 BH |
191 | data.refresh=::arg().asNum("soa-refresh-default"); |
192 | data.retry=::arg().asNum("soa-retry-default"); | |
193 | data.expire=::arg().asNum("soa-expire-default"); | |
194 | data.default_ttl=::arg().asNum("soa-minimum-ttl"); | |
12c86877 BH |
195 | |
196 | vector<string>parts; | |
197 | stringtok(parts,content); | |
198 | int pleft=parts.size(); | |
199 | ||
200 | // cout<<"'"<<content<<"'"<<endl; | |
201 | ||
202 | if(pleft) | |
203 | data.nameserver=parts[0]; | |
204 | ||
205 | if(pleft>1) | |
206 | data.hostmaster=attodot(parts[1]); // ahu@ds9a.nl -> ahu.ds9a.nl, piet.puk@ds9a.nl -> piet\.puk.ds9a.nl | |
207 | ||
208 | if(pleft>2) | |
090802f9 | 209 | data.serial=strtoul(parts[2].c_str(), NULL, 10); |
12c86877 BH |
210 | |
211 | if(pleft>3) | |
212 | data.refresh=atoi(parts[3].c_str()); | |
213 | ||
214 | if(pleft>4) | |
215 | data.retry=atoi(parts[4].c_str()); | |
216 | ||
12c86877 BH |
217 | if(pleft>5) |
218 | data.expire=atoi(parts[5].c_str()); | |
219 | ||
220 | if(pleft>6) | |
221 | data.default_ttl=atoi(parts[6].c_str()); | |
222 | ||
223 | } | |
224 | ||
34b37bbb | 225 | string serializeSOAData(const SOAData &d) |
12c86877 BH |
226 | { |
227 | ostringstream o; | |
12c86877 | 228 | // nameservername hostmaster serial-number [refresh [retry [expire [ minimum] ] ] ] |
12c86877 BH |
229 | o<<d.nameserver<<" "<< d.hostmaster <<" "<< d.serial <<" "<< d.refresh << " "<< d.retry << " "<< d.expire << " "<< d.default_ttl; |
230 | ||
231 | return o.str(); | |
12c86877 BH |
232 | } |
233 | ||
12c86877 BH |
234 | |
235 | static int rrcomp(const DNSResourceRecord &A, const DNSResourceRecord &B) | |
236 | { | |
237 | if(A.d_place<B.d_place) | |
238 | return 1; | |
239 | ||
240 | return 0; | |
241 | } | |
242 | ||
f6ba332a | 243 | vector<DNSResourceRecord*> DNSPacket::getAPRecords() |
12c86877 | 244 | { |
f6ba332a | 245 | vector<DNSResourceRecord*> arrs; |
12c86877 | 246 | |
f6ba332a | 247 | for(vector<DNSResourceRecord>::iterator i=rrs.begin(); |
12c86877 BH |
248 | i!=rrs.end(); |
249 | ++i) | |
250 | { | |
251 | if(i->d_place!=DNSResourceRecord::ADDITIONAL && | |
252 | (i->qtype.getCode()==15 || | |
253 | i->qtype.getCode()==2 )) // CNAME or MX or NS | |
254 | { | |
f6ba332a | 255 | arrs.push_back(&*i); |
12c86877 BH |
256 | } |
257 | } | |
258 | ||
259 | return arrs; | |
260 | ||
261 | } | |
262 | ||
263 | void DNSPacket::setCompress(bool compress) | |
264 | { | |
265 | d_compress=compress; | |
266 | stringbuffer.reserve(65000); | |
267 | rrs.reserve(200); | |
268 | } | |
269 | ||
7f7b8d55 BH |
270 | bool DNSPacket::couldBeCached() |
271 | { | |
272 | return d_ednsping.empty() && !d_wantsnsid; | |
273 | } | |
274 | ||
12c86877 BH |
275 | /** Must be called before attempting to access getData(). This function stuffs all resource |
276 | * records found in rrs into the data buffer. It also frees resource records queued for us. | |
277 | */ | |
278 | void DNSPacket::wrapup(void) | |
279 | { | |
280 | if(d_wrapped) { | |
281 | return; | |
282 | } | |
283 | ||
284 | // do embedded-additional processing decapsulation | |
285 | DNSResourceRecord rr; | |
286 | vector<DNSResourceRecord>::iterator pos; | |
287 | ||
288 | vector<DNSResourceRecord> additional; | |
c1d02c0d | 289 | |
12c86877 BH |
290 | int ipos=rrs.size(); |
291 | rrs.resize(rrs.size()+additional.size()); | |
292 | copy(additional.begin(), additional.end(), rrs.begin()+ipos); | |
293 | ||
294 | // we now need to order rrs so that the different sections come at the right place | |
295 | // we want a stable sort, based on the d_place field | |
296 | ||
297 | stable_sort(rrs.begin(),rrs.end(),rrcomp); | |
298 | ||
43d347d3 | 299 | static bool mustShuffle =::arg().mustDo("no-shuffle"); |
da73ef3c BH |
300 | |
301 | if(!d_tcp && !mustShuffle) { | |
e67e250f | 302 | shuffle(rrs); |
ff6a1e7b | 303 | } |
12c86877 BH |
304 | d_wrapped=true; |
305 | ||
b8e0f341 BH |
306 | vector<uint8_t> packet; |
307 | DNSPacketWriter pw(packet, qdomain, qtype.getCode(), 1); | |
12c86877 | 308 | |
6f966e0b | 309 | pw.getHeader()->rcode=d.rcode; |
b8e0f341 BH |
310 | pw.getHeader()->aa=d.aa; |
311 | pw.getHeader()->ra=d.ra; | |
312 | pw.getHeader()->qr=d.qr; | |
313 | pw.getHeader()->id=d.id; | |
314 | pw.getHeader()->rd=d.rd; | |
12c86877 | 315 | |
7f7b8d55 BH |
316 | DNSPacketWriter::optvect_t opts; |
317 | if(d_wantsnsid) { | |
318 | opts.push_back(make_pair(3, ::arg()["server-id"])); | |
319 | } | |
320 | ||
321 | if(!d_ednsping.empty()) { | |
322 | opts.push_back(make_pair(4, d_ednsping)); | |
323 | } | |
324 | ||
325 | if(!rrs.empty() || !opts.empty()) { | |
6f966e0b | 326 | try { |
c1d02c0d | 327 | for(pos=rrs.begin(); pos < rrs.end(); ++pos) { |
347427bd BH |
328 | // this needs to deal with the 'prio' mismatch! |
329 | if(pos->qtype.getCode()==QType::MX || pos->qtype.getCode() == QType::SRV) { | |
330 | pos->content = lexical_cast<string>(pos->priority) + " " + pos->content; | |
331 | } | |
332 | pw.startRecord(pos->qname, pos->qtype.getCode(), pos->ttl, 1, (DNSPacketWriter::Place)pos->d_place); | |
0bcb4ced | 333 | if(!pos->content.empty() && pos->qtype.getCode()==QType::TXT && pos->content[0]!='"') { |
347427bd BH |
334 | pos->content="\""+pos->content+"\""; |
335 | } | |
c1d02c0d BH |
336 | if(pos->content.empty()) // empty contents confuse the MOADNS setup |
337 | pos->content="."; | |
347427bd BH |
338 | shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(pos->qtype.getCode(), 1, pos->content)); |
339 | drc->toPacket(pw); | |
340 | } | |
7f7b8d55 BH |
341 | if(!opts.empty()) |
342 | pw.addOpt(2800, 0, 0, opts); | |
343 | ||
6f966e0b BH |
344 | pw.commit(); |
345 | } | |
5172cb78 | 346 | catch(std::exception& e) { |
347427bd | 347 | L<<Logger::Error<<"Exception: "<<e.what()<<endl; |
6f966e0b | 348 | throw; |
12c86877 | 349 | } |
b8e0f341 BH |
350 | } |
351 | stringbuffer.assign((char*)&packet[0], packet.size()); | |
352 | len=packet.size(); | |
12c86877 BH |
353 | } |
354 | ||
355 | ||
356 | /** Truncates a packet that has already been wrapup()-ed, possibly via a call to getData(). Do not call this function | |
357 | before having done this - it will possibly break your packet, or crash your program. | |
358 | ||
359 | This method sets the 'TC' bit in the stringbuffer, and caps the len attributed to new_length. | |
360 | */ | |
361 | ||
362 | void DNSPacket::truncate(int new_length) | |
363 | { | |
364 | if(new_length>len || !d_wrapped) | |
365 | return; | |
366 | ||
b1b00117 | 367 | DLOG(L<<Logger::Warning<<"Truncating a packet to "<< remote.toString() <<endl); |
12c86877 BH |
368 | |
369 | len=new_length; | |
370 | stringbuffer[2]|=2; // set TC | |
371 | } | |
372 | ||
12c86877 | 373 | |
288f4aa9 | 374 | void DNSPacket::setQuestion(int op, const string &qd, int newqtype) |
12c86877 BH |
375 | { |
376 | memset(&d,0,sizeof(d)); | |
377 | d.id=Utility::random(); | |
378 | d.rd=d.tc=d.aa=false; | |
379 | d.qr=false; | |
380 | d.qdcount=1; // is htons'ed later on | |
381 | d.ancount=d.arcount=d.nscount=0; | |
382 | d.opcode=op; | |
288f4aa9 BH |
383 | qdomain=qd; |
384 | qtype=newqtype; | |
12c86877 BH |
385 | } |
386 | ||
12c86877 BH |
387 | /** convenience function for creating a reply packet from a question packet. Do not forget to delete it after use! */ |
388 | DNSPacket *DNSPacket::replyPacket() const | |
389 | { | |
390 | DNSPacket *r=new DNSPacket; | |
391 | r->setSocket(d_socket); | |
392 | ||
809fe23f | 393 | r->setRemote(&remote); |
12c86877 BH |
394 | r->setAnswer(true); // this implies the allocation of the header |
395 | r->setA(true); // and we are authoritative | |
396 | r->setRA(0); // no recursion available | |
397 | r->setRD(d.rd); // if you wanted to recurse, answer will say you wanted it (we don't do it) | |
398 | r->setID(d.id); | |
399 | r->setOpcode(d.opcode); | |
400 | ||
12c86877 | 401 | r->d_dt=d_dt; |
092c9cc4 | 402 | r->d.qdcount=1; |
f28307ad | 403 | r->d_tcp = d_tcp; |
b8e0f341 BH |
404 | r->qdomain = qdomain; |
405 | r->qtype = qtype; | |
657e9124 | 406 | r->d_maxreplylen = d_maxreplylen; |
7f7b8d55 BH |
407 | r->d_ednsping = d_ednsping; |
408 | r->d_wantsnsid = d_wantsnsid; | |
12c86877 BH |
409 | return r; |
410 | } | |
411 | ||
b8e0f341 | 412 | void DNSPacket::spoofQuestion(const string &qd) |
12c86877 | 413 | { |
b8e0f341 BH |
414 | string label=simpleCompress(qd); |
415 | for(string::size_type i=0;i<label.size();++i) | |
416 | stringbuffer[i+sizeof(d)]=label[i]; | |
417 | d_wrapped=true; // if we do this, don't later on wrapup | |
418 | } | |
12c86877 | 419 | |
b8e0f341 BH |
420 | /** This function takes data from the network, possibly received with recvfrom, and parses |
421 | it into our class. Results of calling this function multiple times on one packet are | |
422 | unknown. Returns -1 if the packet cannot be parsed. | |
423 | */ | |
424 | int DNSPacket::parse(const char *mesg, int length) | |
9f064e90 | 425 | try |
b8e0f341 BH |
426 | { |
427 | stringbuffer.assign(mesg,length); | |
7f7b8d55 | 428 | |
b8e0f341 BH |
429 | len=length; |
430 | if(length < 12) { | |
431 | L << Logger::Warning << "Ignoring packet: too short from " | |
432 | << getRemote() << endl; | |
433 | return -1; | |
434 | } | |
7f7b8d55 | 435 | |
657e9124 | 436 | MOADNSParser mdp(stringbuffer); |
7f7b8d55 BH |
437 | EDNSOpts edo; |
438 | ||
439 | // ANY OPTION WHICH *MIGHT* BE SET DOWN BELOW SHOULD BE CLEARED FIRST! | |
440 | ||
441 | d_wantsnsid=false; | |
442 | d_ednsping.clear(); | |
443 | ||
444 | if(getEDNSOpts(mdp, &edo)) { | |
54cf6f3e | 445 | d_maxreplylen=max(edo.d_packetsize, (uint16_t)1280); |
7f7b8d55 BH |
446 | |
447 | for(vector<pair<uint16_t, string> >::const_iterator iter = edo.d_options.begin(); | |
448 | iter != edo.d_options.end(); | |
449 | ++iter) { | |
450 | if(iter->first == 3) {// 'EDNS NSID' | |
451 | d_wantsnsid=1; | |
452 | } | |
deff621d | 453 | else if(iter->first == 5) {// 'EDNS PING' |
7f7b8d55 BH |
454 | d_ednsping = iter->second; |
455 | } | |
456 | else | |
457 | ; // cerr<<"Have an option #"<<iter->first<<endl; | |
458 | } | |
657e9124 | 459 | } |
7f7b8d55 | 460 | else { |
657e9124 | 461 | d_maxreplylen=512; |
7f7b8d55 | 462 | } |
12c86877 | 463 | |
b8e0f341 BH |
464 | memcpy((void *)&d,(const void *)stringbuffer.c_str(),12); |
465 | qdomain=mdp.d_qname; | |
466 | if(!qdomain.empty()) // strip dot | |
467 | erase_tail(qdomain, 1); | |
12c86877 | 468 | |
b8e0f341 BH |
469 | if(!ntohs(d.qdcount)) { |
470 | if(!d_tcp) { | |
471 | L << Logger::Warning << "No question section in packet from " << getRemote() <<", rcode="<<(int)d.rcode<<endl; | |
472 | return -1; | |
12c86877 BH |
473 | } |
474 | } | |
475 | ||
b8e0f341 BH |
476 | qtype=mdp.d_qtype; |
477 | qclass=mdp.d_qclass; | |
478 | return 0; | |
12c86877 | 479 | } |
5172cb78 | 480 | catch(std::exception& e) { |
9f064e90 BH |
481 | return -1; |
482 | } | |
12c86877 | 483 | |
657e9124 BH |
484 | int DNSPacket::getMaxReplyLen() |
485 | { | |
486 | return d_maxreplylen; | |
487 | } | |
488 | ||
b8e0f341 BH |
489 | //! Use this to set where this packet was received from or should be sent to |
490 | void DNSPacket::setRemote(const ComboAddress *s) | |
12c86877 | 491 | { |
b8e0f341 BH |
492 | remote=*s; |
493 | } | |
12c86877 | 494 | |
b8e0f341 BH |
495 | void DNSPacket::spoofID(uint16_t id) |
496 | { | |
497 | stringbuffer[1]=(id>>8)&0xff; | |
498 | stringbuffer[0]=id&0xff; | |
499 | d.id=id; | |
12c86877 BH |
500 | } |
501 | ||
b8e0f341 | 502 | void DNSPacket::setSocket(Utility::sock_t sock) |
12c86877 | 503 | { |
b8e0f341 | 504 | d_socket=sock; |
12c86877 BH |
505 | } |
506 | ||
b8e0f341 | 507 | void DNSPacket::commitD() |
12c86877 | 508 | { |
b8e0f341 | 509 | stringbuffer.replace(0,12,(char *)&d,12); // copy in d |
12c86877 BH |
510 | } |
511 |