]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnspacket.cc
spoofID fix - equivalent to t222
[thirdparty/pdns.git] / pdns / dnspacket.cc
1 /*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2001 - 2008 PowerDNS.COM BV
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
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
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>
29 #include <boost/tokenizer.hpp>
30 #include <boost/algorithm/string.hpp>
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"
39 #include "dnswriter.hh"
40 #include "dnsparser.hh"
41 #include "dnsrecords.hh"
42
43 DNSPacket::DNSPacket()
44 {
45 d_wrapped=false;
46 d_compress=true;
47 d_tcp=false;
48 d_wantsnsid=false;
49 }
50
51 string DNSPacket::getString()
52 {
53 return stringbuffer;
54 }
55
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
69 string DNSPacket::getRemote() const
70 {
71 return remote.toString();
72 }
73
74 uint16_t DNSPacket::getRemotePort() const
75 {
76 return remote.sin4.sin_port;
77 }
78
79 DNSPacket::DNSPacket(const DNSPacket &orig)
80 {
81 DLOG(L<<"DNSPacket copy constructor called!"<<endl);
82 d_socket=orig.d_socket;
83 remote=orig.remote;
84 len=orig.len;
85 d_qlen=orig.d_qlen;
86 d_dt=orig.d_dt;
87 d_compress=orig.d_compress;
88 d_tcp=orig.d_tcp;
89 qtype=orig.qtype;
90 qclass=orig.qclass;
91 qdomain=orig.qdomain;
92 d_maxreplylen = orig.d_maxreplylen;
93 d_ednsping = orig.d_ednsping;
94 d_wantsnsid = orig.d_wantsnsid;
95 rrs=orig.rrs;
96
97 d_wrapped=orig.d_wrapped;
98
99 stringbuffer=orig.stringbuffer;
100 d=orig.d;
101 }
102
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
123 void DNSPacket::setID(uint16_t id)
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
138 void DNSPacket::setOpcode(uint16_t opcode)
139 {
140 d.opcode=opcode;
141 }
142
143
144 void DNSPacket::clearRecords()
145 {
146 rrs.clear();
147 }
148
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)
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)
155 return;
156 if(rr.priority==i->priority)
157 return;
158 }
159
160 rrs.push_back(rr);
161 }
162
163 // the functions below update the 'arcount' and 'ancount', plus they serialize themselves to the stringbuffer
164
165 string& attodot(string &str)
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
183 void fillSOAData(const string &content, SOAData &data)
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;
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");
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)
209 data.serial=strtoul(parts[2].c_str(), NULL, 10);
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
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
225 string serializeSOAData(const SOAData &d)
226 {
227 ostringstream o;
228 // nameservername hostmaster serial-number [refresh [retry [expire [ minimum] ] ] ]
229 o<<d.nameserver<<" "<< d.hostmaster <<" "<< d.serial <<" "<< d.refresh << " "<< d.retry << " "<< d.expire << " "<< d.default_ttl;
230
231 return o.str();
232 }
233
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
243 vector<DNSResourceRecord*> DNSPacket::getAPRecords()
244 {
245 vector<DNSResourceRecord*> arrs;
246
247 for(vector<DNSResourceRecord>::iterator i=rrs.begin();
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 {
255 arrs.push_back(&*i);
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
270 bool DNSPacket::couldBeCached()
271 {
272 return d_ednsping.empty() && !d_wantsnsid;
273 }
274
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;
289
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
299 static bool mustShuffle =::arg().mustDo("no-shuffle");
300
301 if(!d_tcp && !mustShuffle) {
302 shuffle(rrs);
303 }
304 d_wrapped=true;
305
306 vector<uint8_t> packet;
307 DNSPacketWriter pw(packet, qdomain, qtype.getCode(), 1);
308
309 pw.getHeader()->rcode=d.rcode;
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;
315
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()) {
326 try {
327 for(pos=rrs.begin(); pos < rrs.end(); ++pos) {
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);
333 if(!pos->content.empty() && pos->qtype.getCode()==QType::TXT && pos->content[0]!='"') {
334 pos->content="\""+pos->content+"\"";
335 }
336 if(pos->content.empty()) // empty contents confuse the MOADNS setup
337 pos->content=".";
338 shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(pos->qtype.getCode(), 1, pos->content));
339 drc->toPacket(pw);
340 }
341 if(!opts.empty())
342 pw.addOpt(2800, 0, 0, opts);
343
344 pw.commit();
345 }
346 catch(std::exception& e) {
347 L<<Logger::Warning<<"Exception: "<<e.what()<<endl;
348 throw;
349 }
350 }
351 stringbuffer.assign((char*)&packet[0], packet.size());
352 len=packet.size();
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
367 DLOG(L<<Logger::Warning<<"Truncating a packet to "<< remote.toString() <<endl);
368
369 len=new_length;
370 stringbuffer[2]|=2; // set TC
371 }
372
373
374 void DNSPacket::setQuestion(int op, const string &qd, int newqtype)
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;
383 qdomain=qd;
384 qtype=newqtype;
385 }
386
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
393 r->setRemote(&remote);
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
401 r->d_dt=d_dt;
402 r->d.qdcount=1;
403 r->d_tcp = d_tcp;
404 r->qdomain = qdomain;
405 r->qtype = qtype;
406 r->d_maxreplylen = d_maxreplylen;
407 r->d_ednsping = d_ednsping;
408 r->d_wantsnsid = d_wantsnsid;
409 return r;
410 }
411
412 void DNSPacket::spoofQuestion(const string &qd)
413 {
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 }
419
420 int DNSPacket::noparse(const char *mesg, int length)
421 {
422 stringbuffer.assign(mesg,length);
423
424 len=length;
425 if(length < 12) {
426 L << Logger::Warning << "Ignoring packet: too short from "
427 << getRemote() << endl;
428 return -1;
429 }
430 d_wantsnsid=false;
431 d_ednsping.clear();
432 d_maxreplylen=512;
433 memcpy((void *)&d,(const void *)stringbuffer.c_str(),12);
434 return 0;
435 }
436
437 /** This function takes data from the network, possibly received with recvfrom, and parses
438 it into our class. Results of calling this function multiple times on one packet are
439 unknown. Returns -1 if the packet cannot be parsed.
440 */
441 int DNSPacket::parse(const char *mesg, int length)
442 try
443 {
444 stringbuffer.assign(mesg,length);
445
446 len=length;
447 if(length < 12) {
448 L << Logger::Warning << "Ignoring packet: too short from "
449 << getRemote() << endl;
450 return -1;
451 }
452
453 MOADNSParser mdp(stringbuffer);
454 EDNSOpts edo;
455
456 // ANY OPTION WHICH *MIGHT* BE SET DOWN BELOW SHOULD BE CLEARED FIRST!
457
458 d_wantsnsid=false;
459 d_ednsping.clear();
460
461 if(getEDNSOpts(mdp, &edo)) {
462 d_maxreplylen=max(edo.d_packetsize, (uint16_t)1280);
463
464 for(vector<pair<uint16_t, string> >::const_iterator iter = edo.d_options.begin();
465 iter != edo.d_options.end();
466 ++iter) {
467 if(iter->first == 3) {// 'EDNS NSID'
468 d_wantsnsid=1;
469 }
470 else if(iter->first == 5) {// 'EDNS PING'
471 d_ednsping = iter->second;
472 }
473 else
474 ; // cerr<<"Have an option #"<<iter->first<<endl;
475 }
476 }
477 else {
478 d_maxreplylen=512;
479 }
480
481 memcpy((void *)&d,(const void *)stringbuffer.c_str(),12);
482 qdomain=mdp.d_qname;
483 if(!qdomain.empty()) // strip dot
484 erase_tail(qdomain, 1);
485
486 if(!ntohs(d.qdcount)) {
487 if(!d_tcp) {
488 L << Logger::Warning << "No question section in packet from " << getRemote() <<", rcode="<<(int)d.rcode<<endl;
489 return -1;
490 }
491 }
492
493 qtype=mdp.d_qtype;
494 qclass=mdp.d_qclass;
495 return 0;
496 }
497 catch(std::exception& e) {
498 return -1;
499 }
500
501 int DNSPacket::getMaxReplyLen()
502 {
503 return d_maxreplylen;
504 }
505
506 void DNSPacket::setMaxReplyLen(int bytes)
507 {
508 d_maxreplylen=bytes;
509 }
510
511 //! Use this to set where this packet was received from or should be sent to
512 void DNSPacket::setRemote(const ComboAddress *s)
513 {
514 remote=*s;
515 }
516
517 void DNSPacket::setSocket(Utility::sock_t sock)
518 {
519 d_socket=sock;
520 }
521
522 void DNSPacket::commitD()
523 {
524 stringbuffer.replace(0,12,(char *)&d,12); // copy in d
525 }
526