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

Revision 1270, 11.5 KB (checked in by ahu, 5 years ago)

further ::arg() stuff

  • 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 - 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
43DNSPacket::DNSPacket() 
44{
45  d_wrapped=false;
46  d_compress=true;
47  d_tcp=false;
48  d_wantsnsid=false;
49}
50
51string DNSPacket::getString()
52{
53  return stringbuffer;
54}
55
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
69string DNSPacket::getRemote() const
70{
71  return remote.toString();
72}
73
74uint16_t DNSPacket::getRemotePort() const
75{
76  return remote.sin4.sin_port;
77}
78
79DNSPacket::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
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
123void DNSPacket::setID(uint16_t id)
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
138void DNSPacket::setOpcode(uint16_t opcode)
139{
140  d.opcode=opcode;
141}
142
143
144void DNSPacket::clearRecords()
145{
146  rrs.clear();
147}
148
149void 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
165string& 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
183void 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
225string 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
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
243vector<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
263void DNSPacket::setCompress(bool compress)
264{
265  d_compress=compress;
266  stringbuffer.reserve(65000);
267  rrs.reserve(200);
268}
269
270bool 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 */
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;
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(exception& e) {
347      L<<Logger::Error<<"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
362void 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
374void 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! */
388DNSPacket *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
412void 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/** 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)
425try
426{
427  stringbuffer.assign(mesg,length); 
428 
429  len=length;
430  if(length < 12) { 
431    L << Logger::Warning << "Ignoring packet: too short from "
432      << getRemote() << endl;
433    return -1;
434  }
435
436  MOADNSParser mdp(stringbuffer);
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)) {
445    d_maxreplylen=max(edo.d_packetsize, (uint16_t)1280);
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      }
453      else if(iter->first == 5) {// 'EDNS PING'
454        d_ednsping = iter->second;
455      }
456      else
457        ; // cerr<<"Have an option #"<<iter->first<<endl;
458    }
459  }
460  else  {
461    d_maxreplylen=512;
462  }
463
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);
468
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;
473    }
474  }
475 
476  qtype=mdp.d_qtype;
477  qclass=mdp.d_qclass;
478  return 0;
479}
480catch(exception& e) {
481  return -1;
482}
483
484int DNSPacket::getMaxReplyLen()
485{
486  return d_maxreplylen;
487}
488
489//! Use this to set where this packet was received from or should be sent to
490void DNSPacket::setRemote(const ComboAddress *s)
491{
492  remote=*s;
493}
494
495void DNSPacket::spoofID(uint16_t id)
496{
497  stringbuffer[1]=(id>>8)&0xff; 
498  stringbuffer[0]=id&0xff;
499  d.id=id;
500}
501
502void DNSPacket::setSocket(Utility::sock_t sock)
503{
504  d_socket=sock;
505}
506
507void DNSPacket::commitD()
508{
509  stringbuffer.replace(0,12,(char *)&d,12); // copy in d
510}
511
Note: See TracBrowser for help on using the browser.