]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnspacket.cc
close very rare issue with tcp/ip close reporting ECONNRESET, reported by Andrei...
[thirdparty/pdns.git] / pdns / dnspacket.cc
CommitLineData
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
43DNSPacket::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
51string DNSPacket::getString()
52{
53 return stringbuffer;
54}
55
b8e0f341
BH
56const char *DNSPacket::getData(void)
57{
58 if(!d_wrapped)
59 wrapup();
60
61 return stringbuffer.data();
62}
63
64const char *DNSPacket::getRaw(void)
65{
66 return stringbuffer.data();
67}
68
12c86877
BH
69string DNSPacket::getRemote() const
70{
809fe23f 71 return remote.toString();
12c86877
BH
72}
73
092f210a 74uint16_t DNSPacket::getRemotePort() const
288f4aa9 75{
809fe23f 76 return remote.sin4.sin_port;
288f4aa9 77}
12c86877 78
12c86877
BH
79DNSPacket::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
103void DNSPacket::setRcode(int v)
104{
105 d.rcode=v;
106}
107
108void 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
118void DNSPacket::setA(bool b)
119{
120 d.aa=b;
121}
122
092f210a 123void DNSPacket::setID(uint16_t id)
12c86877
BH
124{
125 d.id=id;
126}
127
128void DNSPacket::setRA(bool b)
129{
130 d.ra=b;
131}
132
133void DNSPacket::setRD(bool b)
134{
135 d.rd=b;
136}
137
092f210a 138void DNSPacket::setOpcode(uint16_t opcode)
12c86877
BH
139{
140 d.opcode=opcode;
141}
142
12c86877 143
77235722
BH
144void DNSPacket::clearRecords()
145{
146 rrs.clear();
147}
148
12c86877
BH
149void 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 165string& 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 183void 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 225string 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
235static 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 243vector<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
263void DNSPacket::setCompress(bool compress)
264{
265 d_compress=compress;
266 stringbuffer.reserve(65000);
267 rrs.reserve(200);
268}
269
7f7b8d55
BH
270bool 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 */
278void 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
362void 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 374void 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! */
388DNSPacket *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 412void 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*/
424int DNSPacket::parse(const char *mesg, int length)
9f064e90 425try
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 480catch(std::exception& e) {
9f064e90
BH
481 return -1;
482}
12c86877 483
657e9124
BH
484int 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
490void DNSPacket::setRemote(const ComboAddress *s)
12c86877 491{
b8e0f341
BH
492 remote=*s;
493}
12c86877 494
b8e0f341
BH
495void 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 502void DNSPacket::setSocket(Utility::sock_t sock)
12c86877 503{
b8e0f341 504 d_socket=sock;
12c86877
BH
505}
506
b8e0f341 507void DNSPacket::commitD()
12c86877 508{
b8e0f341 509 stringbuffer.replace(0,12,(char *)&d,12); // copy in d
12c86877
BH
510}
511