root/trunk/pdns/pdns/dnspacket.cc @ 1754

Revision 1754, 14.1 KB (checked in by ahu, 2 years ago)

clean up some dnssec logging

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