root/trunk/pdns/pdns/packethandler.cc @ 1607

Revision 1607, 33.8 KB (checked in by ahu, 3 years ago)

fix 'same level soa' regression test, plus clean up some whitespace

  • 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) 2002-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#include "packetcache.hh"
19#include "utility.hh"
20#include <string>
21#include <sys/types.h>
22#include <boost/algorithm/string.hpp>
23#include <boost/foreach.hpp>
24#include <polarssl/rsa.h>
25#include "dnssecinfra.hh"
26#include "dnsseckeeper.hh"
27#include "dns.hh"
28#include "dnsbackend.hh"
29#include "ueberbackend.hh"
30#include "dnspacket.hh"
31#include "nameserver.hh"
32#include "distributor.hh"
33#include "logger.hh"
34#include "arguments.hh"
35#include "packethandler.hh"
36#include "statbag.hh"
37#include "resolver.hh"
38#include "communicator.hh"
39#include "dnsproxy.hh"
40
41#if 0
42#undef DLOG
43#define DLOG(x) x
44#endif
45
46extern StatBag S;
47extern PacketCache PC; 
48extern CommunicatorClass Communicator;
49extern DNSProxy *DP;
50
51int PacketHandler::s_count;
52extern string s_programname;
53
54PacketHandler::PacketHandler():B(s_programname)
55{
56  s_count++;
57  d_doFancyRecords = (::arg()["fancy-records"]!="no");
58  d_doWildcards = (::arg()["wildcards"]!="no");
59  d_doCNAME = (::arg()["skip-cname"]=="no");
60  d_doRecursion= ::arg().mustDo("recursor");
61  d_logDNSDetails= ::arg().mustDo("log-dns-details");
62  d_doIPv6AdditionalProcessing = ::arg().mustDo("do-ipv6-additional-processing");
63}
64
65DNSBackend *PacketHandler::getBackend()
66{
67  return &B;
68}
69
70PacketHandler::~PacketHandler()
71{
72  --s_count;
73  DLOG(L<<Logger::Error<<"PacketHandler destructor called - "<<s_count<<" left"<<endl);
74}
75
76void PacketHandler::addRootReferral(DNSPacket* r)
77{ 
78  // nobody reads what we output, but it appears to be the magic that shuts some nameservers up
79  static const char*ips[]={"198.41.0.4", "192.228.79.201", "192.33.4.12", "128.8.10.90", "192.203.230.10", "192.5.5.241", "192.112.36.4", "128.63.2.53", 
80                     "192.36.148.17","192.58.128.30", "193.0.14.129", "198.32.64.12", "202.12.27.33"};
81  static char templ[40];
82  strncpy(templ,"a.root-servers.net", sizeof(templ) - 1);
83
84  // add . NS records
85  DNSResourceRecord rr;
86  rr.qtype=QType::NS;
87  rr.ttl=518400;
88  rr.d_place=DNSResourceRecord::AUTHORITY;
89 
90  for(char c='a';c<='m';++c) {
91    *templ=c;
92    rr.content=templ;
93    r->addRecord(rr);
94  }
95
96  if(pdns_iequals(::arg()["send-root-referral"], "lean"))
97     return;
98
99  // add the additional stuff
100 
101  rr.ttl=3600000;
102  rr.qtype=QType::A;
103  rr.d_place=DNSResourceRecord::ADDITIONAL;
104
105  for(char c='a';c<='m';++c) {
106    *templ=c;
107    rr.qname=templ;
108    rr.content=ips[c-'a'];
109    r->addRecord(rr);
110  }
111}
112
113int PacketHandler::findMboxFW(DNSPacket *p, DNSPacket *r, string &target)
114{
115  DNSResourceRecord rr;
116  bool wedoforward=false;
117
118  SOAData sd;
119  int zoneId;
120  if(!getAuth(p, &sd, target, &zoneId))
121    return false;
122
123  B.lookup("MBOXFW",string("%@")+target,p, zoneId);
124     
125  while(B.get(rr))
126    wedoforward=true;
127
128  if(wedoforward) {
129    r->clearRecords();
130    rr.content=::arg()["smtpredirector"];
131    rr.priority=25;
132    rr.ttl=7200;
133    rr.qtype=QType::MX;
134    rr.qname=target;
135   
136    r->addRecord(rr);
137  }
138
139  return wedoforward;
140}
141
142int PacketHandler::findUrl(DNSPacket *p, DNSPacket *r, string &target)
143{
144  DNSResourceRecord rr;
145
146  bool found=false;
147     
148  B.lookup("URL",target,p); // search for a URL before we search for an A
149       
150  while(B.get(rr)) {
151    if(!found) 
152      r->clearRecords();
153    found=true;
154    DLOG(L << "Found a URL!" << endl);
155    rr.content=::arg()["urlredirector"];
156    rr.qtype=QType::A; 
157    rr.qname=target;
158         
159    r->addRecord(rr);
160  } 
161
162  if(found) 
163    return 1;
164
165  // now try CURL
166 
167  B.lookup("CURL",target,p); // search for a URL before we search for an A
168     
169  while(B.get(rr)) {
170    if(!found) 
171      r->clearRecords();
172    found=true;
173    DLOG(L << "Found a CURL!" << endl);
174    rr.content=::arg()["urlredirector"];
175    rr.qtype=1; // A
176    rr.qname=target;
177    rr.ttl=300;
178    r->addRecord(rr);
179  } 
180
181  if(found)
182    return found;
183  return 0;
184}
185
186/** Returns 0 if nothing was found, -1 if an error occured or 1 if the search
187    was satisfied */
188int PacketHandler::doFancyRecords(DNSPacket *p, DNSPacket *r, string &target)
189{
190  DNSResourceRecord rr;
191  if(p->qtype.getCode()==QType::MX)  // check if this domain has smtp service from us
192    return findMboxFW(p,r,target);
193 
194  if(p->qtype.getCode()==QType::A)   // search for a URL record for an A
195    return findUrl(p,r,target);
196  return 0;
197}
198
199/** This catches version requests. Returns 1 if it was handled, 0 if it wasn't */
200int PacketHandler::doDNSKEYRequest(DNSPacket *p, DNSPacket *r)
201{
202  DNSResourceRecord rr;
203  DNSSECKeeper dk(::arg()["key-repository"]);
204
205  if(p->qtype.getCode()!=QType::DNSKEY) 
206    return false;
207
208  bool haveOne=false;
209  DNSSECPrivateKey dpk;
210
211
212  if(dk.haveKSKFor(p->qdomain, &dpk)) {
213    rr.qtype=QType::DNSKEY;
214    rr.ttl=3600;
215    rr.qname=p->qdomain;
216    rr.content=dpk.getDNSKEY().getZoneRepresentation(); 
217    rr.auth = true;
218    r->addRecord(rr);
219    haveOne=true;
220  }
221
222  DNSSECKeeper::zskset_t zskset = dk.getZSKsFor(p->qdomain);
223  BOOST_FOREACH(DNSSECKeeper::zskset_t::value_type value, zskset) {
224    rr.qtype=QType::DNSKEY;
225    rr.ttl=3600;
226    rr.qname=p->qdomain;
227    rr.content=value.first.getDNSKEY().getZoneRepresentation();
228   
229    r->addRecord(rr);
230    haveOne=true;
231  }
232
233
234  return haveOne;
235}
236
237
238/** This catches version requests. Returns 1 if it was handled, 0 if it wasn't */
239int PacketHandler::doVersionRequest(DNSPacket *p, DNSPacket *r, string &target)
240{
241  DNSResourceRecord rr;
242 
243  // modes: anonymous, powerdns only, full, spoofed
244  const string mode=::arg()["version-string"];
245  if(p->qtype.getCode()==QType::TXT && target=="version.bind") {// TXT
246    if(mode.empty() || mode=="full") 
247      rr.content="Served by POWERDNS "VERSION" $Id$";
248    else if(mode=="anonymous") {
249      r->setRcode(RCode::ServFail);
250      return 1;
251    }
252    else if(mode=="powerdns")
253      rr.content="Served by PowerDNS - http://www.powerdns.com";
254    else 
255      rr.content=mode;
256
257    rr.ttl=5;
258    rr.qname=target;
259    rr.qtype=QType::TXT; // TXT
260    r->addRecord(rr);
261   
262    return 1;
263  }
264  return 0;
265}
266
267/** Determines if we are authoritative for a zone, and at what level */
268bool PacketHandler::getAuth(DNSPacket *p, SOAData *sd, const string &target, int *zoneId)
269{
270  string subdomain(target);
271  do {
272    if( B.getSOA( subdomain, *sd, p ) ) {
273      sd->qname = subdomain;
274      if(zoneId)
275        *zoneId = sd->domain_id;
276      return true;
277    }
278  }
279  while( chopOff( subdomain ) );   // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> ''
280  return false;
281}
282
283vector<DNSResourceRecord> PacketHandler::getBestReferralNS(DNSPacket *p, SOAData& sd, const string &target)
284{
285  vector<DNSResourceRecord> ret;
286  DNSResourceRecord rr;
287  string subdomain(target);
288  do {
289    if(subdomain == sd.qname) // stop at SOA
290      break;
291    B.lookup(QType(QType::NS), subdomain, p, sd.domain_id);
292    while(B.get(rr)) {
293      if(!rr.auth)
294        ret.push_back(rr);
295    }
296    if(!ret.empty())
297      return ret;
298  } while( chopOff( subdomain ) );   // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> ''
299  return ret;
300}
301
302vector<DNSResourceRecord> PacketHandler::getBestWildcard(DNSPacket *p, SOAData& sd, const string &target)
303{
304  vector<DNSResourceRecord> ret;
305  DNSResourceRecord rr;
306  string subdomain(target);
307  while( chopOff( subdomain ))  {
308    B.lookup(QType(QType::ANY), "*."+subdomain, p, sd.domain_id);
309    while(B.get(rr)) {
310      if(rr.qtype == p->qtype ||rr.qtype.getCode() == QType::CNAME )
311        ret.push_back(rr);
312    }
313   
314    if(!ret.empty())
315      return ret;
316   
317    if(subdomain == sd.qname) // stop at SOA
318      break;
319  } 
320
321  return ret;
322}
323
324
325/** returns 1 in case of a straight match, 2 in case of a wildcard CNAME (groan), 0 in case of no hit */
326int PacketHandler::doWildcardRecords(DNSPacket *p, DNSPacket *r, string &target)
327{
328  DNSResourceRecord rr;
329  bool found=false, retargeted=false;
330
331  // try chopping off domains and look for wildcard matches
332
333  // *.pietje.nl IN  A 1.2.3.4
334  // pietje.nl should now NOT match, but www.pietje.nl should
335
336  string subdomain=target;
337  string::size_type pos;
338
339  while((pos=subdomain.find("."))!=string::npos) {
340    subdomain=subdomain.substr(pos+1);
341    // DLOG();
342
343    string searchstr=string("*.")+subdomain;
344
345    B.lookup(QType(QType::ANY), searchstr,p); // start our search at the backend
346
347    while(B.get(rr)) { // read results
348      if(retargeted)
349        continue;
350      found=true;
351      if((p->qtype.getCode()==QType::ANY || rr.qtype==p->qtype) || rr.qtype.getCode()==QType::CNAME) {
352        rr.qname=target;
353
354        if(d_doFancyRecords && p->qtype.getCode()==QType::ANY && (rr.qtype.getCode()==QType::URL || rr.qtype.getCode()==QType::CURL)) {
355          rr.content=::arg()["urlredirector"];
356          rr.qtype=QType::A; 
357        }
358
359        r->addRecord(rr);  // and add
360        if(rr.qtype.getCode()==QType::CNAME) {
361          if(target==rr.content) {
362            L<<Logger::Error<<"Ignoring wildcard CNAME '"<<rr.qname<<"' pointing at itself"<<endl;
363            r->setRcode(RCode::ServFail);
364            continue;
365          }
366         
367          DLOG(L<<Logger::Error<<"Retargeting because of wildcard cname, from "<<target<<" to "<<rr.content<<endl);
368         
369          target=rr.content; // retarget
370          retargeted=true;
371        }
372      }
373      else if(d_doFancyRecords && ::arg().mustDo("wildcard-url") && p->qtype.getCode()==QType::A && rr.qtype.getName()=="URL") {
374        rr.content=::arg()["urlredirector"];
375        rr.qtype=QType::A; 
376        rr.qname=target;
377       
378        r->addRecord(rr);
379      }
380    }
381    if(found) {
382      DLOG(L<<"Wildcard match on '"<<string("*.")+subdomain<<"'"<<", retargeted="<<retargeted<<endl);
383      return retargeted ? 2 : 1;
384    }
385  }
386  DLOG(L<<"Returning no hit for '"<<string("*.")+subdomain<<"'"<<endl);
387  return 0;
388}
389
390/** dangling is declared true if we were unable to resolve everything */
391int PacketHandler::doAdditionalProcessingAndDropAA(DNSPacket *p, DNSPacket *r)
392{
393  DNSResourceRecord rr;
394  SOAData sd;
395
396  if(p->qtype.getCode()!=QType::AXFR) { // this packet needs additional processing
397    vector<DNSResourceRecord *> arrs=r->getAPRecords();
398    if(arrs.empty()) 
399      return 1;
400
401    DLOG(L<<Logger::Warning<<"This packet needs additional processing!"<<endl);
402
403    vector<DNSResourceRecord> crrs;
404
405    for(vector<DNSResourceRecord *>::const_iterator i=arrs.begin();
406        i!=arrs.end();  ++i) 
407      crrs.push_back(**i);
408
409    // we now have a copy, push_back on packet might reallocate!
410
411    for(vector<DNSResourceRecord>::const_iterator i=crrs.begin();
412        i!=crrs.end();
413        ++i) {
414     
415      if(r->d.aa && !i->qname.empty() && i->qtype.getCode()==QType::NS && !B.getSOA(i->qname,sd,p)) { // drop AA in case of non-SOA-level NS answer, except for root referral
416        r->d.aa=false;
417        //      i->d_place=DNSResourceRecord::AUTHORITY; // XXX FIXME
418      }
419
420      QType qtypes[2];
421      qtypes[0]="A"; qtypes[1]="AAAA";
422      for(int n=0 ; n < d_doIPv6AdditionalProcessing + 1; ++n) {
423        B.lookup(qtypes[n],i->content,p); 
424        bool foundOne=false;
425        while(B.get(rr)) {
426          foundOne=true;
427          if(rr.domain_id!=i->domain_id && ::arg()["out-of-zone-additional-processing"]=="no") {
428            DLOG(L<<Logger::Warning<<"Not including out-of-zone additional processing of "<<i->qname<<" ("<<rr.qname<<")"<<endl);
429            continue; // not adding out-of-zone additional data
430          }
431         
432          rr.d_place=DNSResourceRecord::ADDITIONAL;
433          r->addRecord(rr);
434        }
435      }
436    }
437  }
438  return 1;
439}
440
441
442void PacketHandler::emitNSEC(const std::string& begin, const std::string& end, const std::string& toNSEC, DNSPacket *r, int mode)
443{
444  cerr<<"We should emit '"<<begin<<"' - ('"<<toNSEC<<"') - '"<<end<<"'"<<endl;
445  NSECRecordContent nrc;
446  nrc.d_set.insert(QType::RRSIG);
447  nrc.d_set.insert(QType::NSEC);
448
449  DNSResourceRecord rr;
450  B.lookup(QType(QType::ANY), begin);
451  while(B.get(rr)) {
452    nrc.d_set.insert(rr.qtype.getCode());   
453  }
454 
455  nrc.d_next=end;
456
457  rr.qname=begin;
458  rr.ttl=3600;
459  rr.qtype=QType::NSEC;
460  rr.content=nrc.getZoneRepresentation();
461  rr.d_place = (mode == 2 ) ? DNSResourceRecord::ANSWER: DNSResourceRecord::AUTHORITY;
462  rr.auth = true;
463  r->addRecord(rr);
464}
465
466/* mode 0 = no error -> an NSEC that starts with 'target', in authority section
467   mode 1 = NXDOMAIN -> an NSEC from auth to first + a covering NSEC
468   mode 2 = ANY or direct NSEC request  -> an NSEC that starts with 'target'
469   mode 3 = a covering NSEC in the authority section (like 1, except for first)
470*/
471
472void PacketHandler::addNSEC(DNSPacket *p, DNSPacket *r, const string& target, const string& auth, int mode)
473{
474  if(!p->d_dnssecOk)
475    return;
476 
477  cerr<<"Should add NSEC covering '"<<target<<"' from zone '"<<auth<<"', mode = "<<mode<<endl;
478  SOAData sd;
479  sd.db=(DNSBackend *)-1; // force uncached answer
480
481  if(auth.empty()) {
482    getAuth(p, &sd, target, 0);
483  }
484  else if(!B.getSOA(auth, sd)) {
485    cerr<<"Could not get SOA for domain\n";
486    return;
487  }
488
489  string before,after;
490  cerr<<"Calling getBeforeandAfter!"<<endl;
491  sd.db->getBeforeAndAfterNames(sd.domain_id, auth, target, before, after); 
492  cerr<<"Done calling, before='"<<before<<"', after='"<<after<<"'"<<endl;
493
494  // this stuff is wrong
495 
496  if(mode ==0 || mode==2)
497    emitNSEC(target, after, target, r, mode);
498 
499
500  if(mode == 1)  {
501    emitNSEC(before, after, target, r, mode);
502
503    sd.db->getBeforeAndAfterNames(sd.domain_id, auth, auth, before, after); 
504    emitNSEC(auth, after, auth, r, mode);
505  }
506
507  if(mode == 3)
508    emitNSEC(before, after, target, r, mode);
509
510  return;
511}
512
513bool PacketHandler::doDNSSECProcessing(DNSPacket *p, DNSPacket *r)
514{
515  if(!p->d_dnssecOk)
516    return false;
517
518  vector<DNSResourceRecord *> arrs=r->getAnswerRecords();
519  if(arrs.empty()) 
520    return false;
521 
522  cerr<<"Have arrs "<<arrs.size()<<" records to sign\n";
523  vector<DNSResourceRecord> crrs;
524 
525  for(vector<DNSResourceRecord *>::const_iterator i=arrs.begin();
526      i!=arrs.end();    ++i) 
527    crrs.push_back(**i);
528 
529  // we now have a copy, push_back on packet might reallocate!
530 
531  for(vector<DNSResourceRecord>::const_iterator i=crrs.begin();
532      i!=crrs.end();
533      ++i) {
534    if(i->d_place!=DNSResourceRecord::ANSWER) 
535      continue;
536   
537    B.lookup(QType(QType::RRSIG),i->qname,p); 
538    DNSResourceRecord rr;
539    while(B.get(rr)) {
540      rr.d_place=DNSResourceRecord::ANSWER;
541      if(splitField(rr.content, ' ').first==i->qtype.getName())
542        r->addRecord(rr);
543    }
544  }
545 
546  return false;
547}
548
549/* returns 1 if everything is done & ready, 0 if the search should continue, 2 if a 'NO-ERROR' response should be generated */
550int PacketHandler::makeCanonic(DNSPacket *p, DNSPacket *r, string &target)
551{
552  DNSResourceRecord rr;
553
554  bool found=false, rfound=false;
555
556  if(p->qtype.getCode()!=QType::CNAME && !d_doCNAME)
557    return 0;
558
559  // Traverse a CNAME chain if needed
560  for(int numloops=0;;numloops++) {
561    if(numloops==10) {
562      L<<Logger::Error<<"Detected a CNAME loop involving "<<target<<", sending SERVFAIL"<<endl;
563      r->setRcode(2);
564      return 1;
565    }
566
567    B.lookup(QType(QType::ANY),target,p);
568       
569    bool shortcut=p->qtype.getCode()!=QType::SOA && p->qtype.getCode()!=QType::ANY;
570    int hits=0;
571    bool relevantNS=false;
572    bool sawDS=false;
573    bool crossedZoneCut = false;
574    while(B.get(rr)) {
575      if(rr.qtype.getCode() == QType::NS && p->qtype.getCode() != QType::NS) { // possible retargeting
576        relevantNS=true;
577      }
578
579      if(rr.qtype.getCode()==QType::DS && p->qtype.getCode() == QType::NS && p->d_dnssecOk) {
580        sawDS = true;
581        r->addRecord(rr);
582      }
583
584      if(rr.qtype.getCode()!=QType::NS || p->qtype.getCode()==QType::NS)
585        hits++;
586      if(!rfound && rr.qtype.getCode()==QType::CNAME) {
587        found=true;
588        r->addRecord(rr);
589        target=rr.content; // for retargeting
590      }
591      if(shortcut && !found && rr.qtype==p->qtype) {
592        if(!rr.auth) {
593        // no idea why this if is here
594        }
595         
596        rfound=true;
597        r->addRecord(rr);
598      }
599    }
600
601    if(crossedZoneCut) {
602      cerr<<"Should return NS records, and this A/AAAA record in the additional section.."<<endl;
603    }
604
605    if(!sawDS && p->qtype.getCode() == QType::NS && p->d_dnssecOk && rfound) {
606      addNSEC(p, r, p->qdomain, "", 2); // make it 'official' that we have no DS
607    }
608
609    if(hits && !relevantNS && !found && !rfound && shortcut ) { // XXX FIXME !numloops. we found matching qnames but not a qtype
610      DLOG(L<<"Found matching qname, but not the qtype"<<endl);
611      return 2;
612    }
613
614    if(rfound)
615      return 1; // ANY lookup found the right answer immediately
616
617    if(found) {
618      if(p->qtype.getCode()==QType::CNAME) // they really wanted a CNAME!
619        return 1;
620      DLOG(L<<"Looping because of a CNAME to "<<target<<endl);
621      found=false;
622    }
623    else 
624      break;
625  }
626
627  // we now have what we really search for ready in 'target'
628  return 0;
629}
630
631/* Semantics:
632   
633- only one backend owns the SOA of a zone
634- only one AXFR per zone at a time - double startTransaction should fail
635- backends need to implement transaction semantics
636
637
638How BindBackend would implement this:
639   startTransaction makes a file
640   feedRecord sends everything to that file
641   commitTransaction moves that file atomically over the regular file, and triggers a reload
642   rollbackTransaction removes the file
643
644
645How PostgreSQLBackend would implement this:
646   startTransaction starts a sql transaction, which also deletes all records
647   feedRecord is an insert statement
648   commitTransaction commits the transaction
649   rollbackTransaction aborts it
650
651How MySQLBackend would implement this:
652   (good question!)
653   
654*/     
655
656int PacketHandler::trySuperMaster(DNSPacket *p)
657{
658  Resolver::res_t nsset;
659  try {
660    Resolver resolver;
661    uint32_t theirserial;
662    resolver.getSoaSerial(p->getRemote(),p->qdomain, &theirserial); 
663 
664    resolver.resolve(p->getRemote(),p->qdomain.c_str(), QType::NS);
665
666    nsset=resolver.result();
667  }
668  catch(ResolverException &re) {
669    L<<Logger::Error<<"Error resolving SOA or NS at: "<< p->getRemote() <<": "<<re.reason<<endl;
670    return RCode::ServFail;
671  }
672
673  string account;
674  DNSBackend *db;
675  if(!B.superMasterBackend(p->getRemote(), p->qdomain, nsset, &account, &db)) {
676    L<<Logger::Error<<"Unable to find backend willing to host "<<p->qdomain<<" for potential supermaster "<<p->getRemote()<<endl;
677    return RCode::Refused;
678  }
679  db->createSlaveDomain(p->getRemote(),p->qdomain,account);
680  Communicator.addSuckRequest(p->qdomain, p->getRemote()); 
681  L<<Logger::Warning<<"Created new slave zone '"<<p->qdomain<<"' from supermaster "<<p->getRemote()<<", queued axfr"<<endl;
682  return RCode::NoError;
683}
684
685int PacketHandler::processNotify(DNSPacket *p)
686{
687  /* now what?
688     was this notification from an approved address?
689     We determine our internal SOA id (via UeberBackend)
690     We determine the SOA at our (known) master
691     if master is higher -> do stuff
692  */
693  if(!::arg().mustDo("slave")) {
694    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" but slave support is disabled in the configuration"<<endl;
695    return RCode::NotImp;
696  }
697  DNSBackend *db=0;
698  DomainInfo di;
699  di.serial = 0;
700  if(!B.getDomainInfo(p->qdomain, di) || !(db=di.backend)) {
701    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" for which we are not authoritative"<<endl;
702    return trySuperMaster(p);
703  }
704   
705  string authServer(p->getRemote());
706  if(::arg().contains("trusted-notification-proxy", p->getRemote())) {
707    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from trusted-notification-proxy "<< p->getRemote()<<endl;
708    if(di.masters.empty()) {
709      L<<Logger::Error<<"However, "<<p->qdomain<<" does not have any masters defined"<<endl;
710      return RCode::Refused;
711    }
712
713    authServer = *di.masters.begin();
714
715  }
716  else if(!db->isMaster(p->qdomain, p->getRemote())) {
717    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" which is not a master"<<endl;
718    return RCode::Refused;
719  }
720
721  uint32_t theirserial=0;
722
723  /* to quote Rusty Russell - this code is so bad that you can actually hear it suck */
724  /* this is an instant DoS, just spoof notifications from the address of the master and we block  */
725
726  Resolver resolver;
727  try {
728    resolver.getSoaSerial(authServer, p->qdomain, &theirserial);
729  }
730  catch(ResolverException& re) {
731    L<<Logger::Error<<re.reason<<endl;
732    return RCode::ServFail;
733  }
734
735  if(theirserial<=di.serial) {
736    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<< authServer <<", we are up to date: "<<
737      theirserial<<"<="<<di.serial<<endl;
738    return RCode::NoError;
739  }
740  else {
741    L<<Logger::Error<<"Received valid NOTIFY for "<<p->qdomain<<" (id="<<di.id<<") from master "<<p->getRemote()<<": "<<
742      theirserial<<" > "<<di.serial<<endl;
743
744    Communicator.addSuckRequest(p->qdomain, authServer, true); // priority
745  }
746  return -1; 
747}
748
749
750
751bool validDNSName(const string &name)
752{
753  string::size_type pos, length=name.length();
754  char c;
755  for(pos=0; pos < length; ++pos) {
756    c=name[pos];
757    if(!((c >= 'a' && c <= 'z') ||
758         (c >= 'A' && c <= 'Z') ||
759         (c >= '0' && c <= '9') ||
760         c =='-' || c == '_' || c=='*' || c=='.' || c=='/' || c=='@'))
761      return false;
762  }
763  return true;
764} 
765
766DNSPacket *PacketHandler::question(DNSPacket *p)
767{
768  bool shouldRecurse=false;
769  DNSPacket *ret=questionOrRecurse(p, &shouldRecurse);
770  if(shouldRecurse) {
771    DP->sendPacket(p);
772  }
773  return ret;
774}
775
776void PacketHandler::synthesiseRRSIGs(DNSPacket* p, DNSPacket* r)
777{
778  cerr<<"Need to fake up the RRSIGs some way.."<<endl;
779  B.lookup(QType(QType::ANY), p->qdomain, p);
780 
781  DNSResourceRecord rr;
782 
783  typedef map<uint16_t, vector<shared_ptr<DNSRecordContent> > > records_t;
784  records_t records;
785
786  NSECRecordContent nrc;
787  nrc.d_set.insert(QType::RRSIG);
788  nrc.d_set.insert(QType::NSEC);
789
790  while(B.get(rr)) {
791    if(!rr.auth) 
792      continue;
793   
794    // this deals with the 'prio' mismatch!
795    if(rr.qtype.getCode()==QType::MX || rr.qtype.getCode() == QType::SRV) { 
796      rr.content = lexical_cast<string>(rr.priority) + " " + rr.content;
797    }
798   
799    if(!rr.content.empty() && rr.qtype.getCode()==QType::TXT && rr.content[0]!='"') {
800      rr.content="\""+rr.content+"\"";
801    }
802    if(rr.content.empty())  // empty contents confuse the MOADNS setup
803      rr.content=".";
804    shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content)); 
805   
806    records[rr.qtype.getCode()].push_back(drc);
807    nrc.d_set.insert(rr.qtype.getCode());
808  }
809
810  // now get the fucking NSEC too (since we must sign it!)
811
812  SOAData sd;
813  sd.db=(DNSBackend *)-1; // force uncached answer
814  getAuth(p, &sd, p->qdomain, 0);
815
816  string before,after;
817  sd.db->getBeforeAndAfterNames(sd.domain_id, sd.qname, p->qdomain, before, after); 
818
819  nrc.d_next=after;
820
821  rr.qname=p->qdomain;
822  rr.ttl=3600;
823  rr.qtype=QType::NSEC;
824  rr.content=nrc.getZoneRepresentation();
825
826  records[QType::NSEC].push_back(shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content)));
827
828  // ok, the NSEC is in..
829
830  cerr<<"Have "<<records.size()<<" rrsets to sign"<<endl;
831
832  rr.qname = p->qdomain;
833  rr.ttl = 3600;
834  rr.auth = 0; // please don't sign this!
835  rr.d_place = DNSResourceRecord::ANSWER;
836  rr.qtype = QType::RRSIG;
837
838  BOOST_FOREACH(records_t::value_type& iter, records) {
839    RRSIGRecordContent rrc;
840    for(int ksk =0 ; ksk < 2; ++ksk) {
841      getRRSIGForRRSET(::arg()["key-repository"], p->qdomain, iter.first, 3600, iter.second, rrc, ksk);
842      rr.content=rrc.getZoneRepresentation();
843      r->addRecord(rr);
844      if(iter.first != QType::DNSKEY)
845        break;
846    }
847  }
848}
849
850void PacketHandler::makeNXDomain(DNSPacket* p, DNSPacket* r, const std::string& target, SOAData& sd)
851{
852  DNSResourceRecord rr;
853  rr.qname=sd.qname;
854  rr.qtype=QType::SOA;
855  rr.content=serializeSOAData(sd);
856  rr.ttl=sd.ttl;
857  rr.domain_id=sd.domain_id;
858  rr.d_place=DNSResourceRecord::AUTHORITY;
859  r->addRecord(rr);
860 
861  if(p->d_dnssecOk)
862    addNSEC(p, r, target, sd.qname, 1);
863  r->setRcode(RCode::NXDomain); 
864  S.ringAccount("nxdomain-queries",p->qdomain+"/"+p->qtype.getName());
865}
866
867void PacketHandler::makeNOError(DNSPacket* p, DNSPacket* r, const std::string& target, SOAData& sd)
868{
869  DNSResourceRecord rr;
870  rr.qname=sd.qname;
871  rr.qtype=QType::SOA;
872  rr.content=serializeSOAData(sd);
873  rr.ttl=sd.ttl;
874  rr.domain_id=sd.domain_id;
875  rr.d_place=DNSResourceRecord::AUTHORITY;
876  r->addRecord(rr);
877 
878  if(p->d_dnssecOk)
879    addNSEC(p, r, target, sd.qname, 0);
880
881  S.ringAccount("noerror-queries",p->qdomain+"/"+p->qtype.getName());
882}
883
884
885bool PacketHandler::addDSforNS(DNSPacket* p, DNSPacket* r, SOAData& sd, const string& dsname)
886{
887  B.lookup(QType(QType::DS), dsname, p, sd.domain_id);
888  DNSResourceRecord rr;
889  bool gotOne=false;
890  while(B.get(rr)) {
891    gotOne=true;
892    rr.d_place = DNSResourceRecord::AUTHORITY;
893    r->addRecord(rr);
894  }
895  return gotOne;
896}
897
898bool PacketHandler::tryReferral(DNSPacket *p, DNSPacket*r, SOAData& sd, const string &target)
899{
900  vector<DNSResourceRecord> rrset = getBestReferralNS(p, sd, target);
901  if(rrset.empty())
902    return false;
903 
904  cerr<<"The best NS is: "<<rrset.begin()->qname<<endl;
905  BOOST_FOREACH(DNSResourceRecord rr, rrset) {
906    cerr<<"\tadding '"<<rr.content<<"'\n";
907    rr.d_place=DNSResourceRecord::AUTHORITY;
908    r->addRecord(rr);
909  }
910  r->setA(false);
911
912  if(!addDSforNS(p, r, sd, rrset.begin()->qname))
913    addNSEC(p, r, rrset.begin()->qname, sd.qname, 0);
914 
915  return true;
916}
917
918void PacketHandler::completeANYRecords(DNSPacket *p, DNSPacket*r, SOAData& sd, const string &target)
919{
920  if(!p->d_dnssecOk)
921    cerr<<"Need to add all the RRSIGs too for '"<<target<<"', should do this manually since DNSSEC was not requested"<<endl;
922  //  cerr<<"Need to add all the NSEC too.."<<endl; /// XXX FIXME THE ABOVE IF IS WEIRD
923  addNSEC(p, r, target, sd.qname, 2); 
924}
925
926bool PacketHandler::tryWildcard(DNSPacket *p, DNSPacket*r, SOAData& sd, string &target, bool& retargeted)
927{
928  retargeted=false;
929
930  vector<DNSResourceRecord> rrset = getBestWildcard(p, sd, target);
931  if(rrset.empty())
932    return false;
933
934  cerr<<"The best wildcard match: "<<rrset.begin()->qname<<endl;
935  BOOST_FOREACH(DNSResourceRecord rr, rrset) {
936    if(rr.qtype.getCode() == QType::CNAME)  {
937      retargeted=true;
938      target=rr.content;
939    }
940
941    rr.wildcardname = rr.qname;
942    rr.qname=p->qdomain;
943    cerr<<"\tadding '"<<rr.content<<"'\n";
944    rr.d_place=DNSResourceRecord::ANSWER;
945    r->addRecord(rr);
946  }
947
948  if(p->d_dnssecOk) {
949    addNSEC(p, r, p->qdomain, sd.qname, 3);
950  }
951  return true;
952}
953
954
955//! Called by the Distributor to ask a question. Returns 0 in case of an error
956DNSPacket *PacketHandler::questionOrRecurse(DNSPacket *p, bool *shouldRecurse)
957{
958  *shouldRecurse=false;
959  DNSResourceRecord rr;
960  SOAData sd;
961  sd.db=0;
962 
963  string subdomain="";
964  string soa;
965  int retargetcount=0;
966
967  vector<DNSResourceRecord> rrset;
968  bool weDone=0, weRedirected=0, weHaveUnauth=0;
969
970  DNSPacket *r=0;
971  try {   
972    DLOG(L << Logger::Notice<<"Remote "<< p->remote.toString() <<" wants a type " << p->qtype.getName() << " ("<<p->qtype.getCode()<<") about '"<<p->qdomain << "'" << endl);
973
974    if(p->d.qr) { // QR bit from dns packet (thanks RA from N)
975      L<<Logger::Error<<"Received an answer (non-query) packet from "<<p->getRemote()<<", dropping"<<endl;
976      S.inc("corrupt-packets");
977      return 0;
978    }
979
980    // XXX FIXME do this in DNSPacket::parse ?
981
982    if(!validDNSName(p->qdomain)) {
983      if(::arg().mustDo("log-dns-details"))
984        L<<Logger::Error<<"Received a malformed qdomain from "<<p->getRemote()<<", '"<<p->qdomain<<"': sending servfail"<<endl;
985      S.inc("corrupt-packets");
986      r=p->replyPacket(); 
987      r->setRcode(RCode::ServFail);
988      return r;
989    }
990    if(p->d.opcode) { // non-zero opcode (again thanks RA!)
991      if(p->d.opcode==Opcode::Update) {
992        if(::arg().mustDo("log-failed-updates"))
993          L<<Logger::Notice<<"Received an UPDATE opcode from "<<p->getRemote()<<" for "<<p->qdomain<<", sending NOTIMP"<<endl;
994        r=p->replyPacket(); 
995        r->setRcode(RCode::NotImp); // notimp;
996        return r; 
997      }
998      else if(p->d.opcode==Opcode::Notify) {
999        int res=processNotify(p);
1000        if(res>=0) {
1001          DNSPacket *r=p->replyPacket();
1002          r->setRcode(res);
1003          r->setOpcode(Opcode::Notify);
1004          return r;
1005        }
1006        return 0;
1007      }
1008     
1009      L<<Logger::Error<<"Received an unknown opcode "<<p->d.opcode<<" from "<<p->getRemote()<<" for "<<p->qdomain<<endl;
1010
1011      r=p->replyPacket(); 
1012      r->setRcode(RCode::NotImp); 
1013      return r; 
1014    }
1015
1016    // L<<Logger::Warning<<"Query for '"<<p->qdomain<<"' "<<p->qtype.getName()<<" from "<<p->getRemote()<<endl;
1017   
1018    r=p->replyPacket();  // generate an empty reply packet
1019    if(p->d.rd && d_doRecursion && DP->recurseFor(p))  // make sure we set ra if rd was set, and we'll do it
1020      r->d.ra=true;
1021
1022    if(p->qtype.getCode()==QType::IXFR) {
1023      r->setRcode(RCode::NotImp);
1024      return r;
1025    }
1026
1027    // please don't query fancy records directly!
1028    if(d_doFancyRecords && (p->qtype.getCode()==QType::URL || p->qtype.getCode()==QType::CURL || p->qtype.getCode()==QType::MBOXFW)) {
1029      r->setRcode(RCode::ServFail);
1030      return r;
1031    }
1032   
1033    string target=p->qdomain;
1034    bool noCache=false;
1035
1036    if(doDNSKEYRequest(p,r)) 
1037      goto sendit;
1038
1039    if(doVersionRequest(p,r,target)) // catch version.bind requests
1040      goto sendit;
1041
1042    if(p->qclass==255) // any class query
1043      r->setA(false);
1044    else if(p->qclass!=1) // we only know about IN, so we don't find anything
1045      goto sendit;
1046
1047  retargeted:;
1048    if(retargetcount > 10) {    // XXX FIXME, retargetcount++?
1049      r->setRcode(RCode::ServFail);
1050      return r;
1051    }
1052   
1053    if(!getAuth(p, &sd, target, 0)) {
1054      r->setA(false);
1055      if(::arg().mustDo("send-root-referral")) {
1056        DLOG(L<<Logger::Warning<<"Adding root-referral"<<endl);
1057        addRootReferral(r);
1058      }
1059      else {
1060        DLOG(L<<Logger::Warning<<"Adding SERVFAIL"<<endl);
1061        r->setRcode(RCode::ServFail);  // 'sorry'
1062      }
1063      goto sendit;
1064    }
1065    DLOG(L<<Logger::Error<<"We have authority, zone='"<<sd.qname<<"', id="<<sd.domain_id<<endl);
1066    // we know we have authority
1067
1068    if(p->qtype.getCode() == QType::SOA && pdns_iequals(sd.qname, p->qdomain)) {
1069        rr.qname=sd.qname;
1070      rr.qtype=QType::SOA;
1071      rr.content=serializeSOAData(sd);
1072      rr.ttl=sd.ttl;
1073      rr.domain_id=sd.domain_id;
1074      rr.d_place=DNSResourceRecord::ANSWER;
1075      r->addRecord(rr);
1076      goto sendit;
1077    }
1078
1079    // this TRUMPS a cname!
1080    if(p->qtype.getCode() == QType::NSEC && p->d_dnssecOk) {
1081      addNSEC(p, r, target, "", 2); // only NSEC please
1082      goto sendit;
1083    }
1084   
1085    // this TRUMPS a cname!
1086    if(p->qtype.getCode() == QType::RRSIG && p->d_dnssecOk) {
1087      synthesiseRRSIGs(p, r);
1088      goto sendit; 
1089    }
1090
1091    // see what we get..
1092    B.lookup(QType(QType::ANY), target, p, sd.domain_id);
1093    rrset.clear();
1094    weDone=weRedirected=weHaveUnauth=0;
1095   
1096    while(B.get(rr)) {
1097      if((p->qtype.getCode() == QType::ANY || rr.qtype == p->qtype) && rr.auth) 
1098        weDone=1;
1099      if((rr.qtype == p->qtype || rr.qtype.getCode() == QType::NS) && !rr.auth) 
1100        weHaveUnauth=1;
1101
1102      if(rr.qtype.getCode() == QType::CNAME && p->qtype.getCode() != QType::CNAME) 
1103        weRedirected=1;
1104      rrset.push_back(rr);
1105    }
1106
1107    cerr<<"After first ANY query: weDone="<<weDone<<", weHaveUnauth="<<weHaveUnauth<<", weRedirected="<<weRedirected<<endl;
1108
1109    if(rrset.empty()) {
1110      // try wildcards, and if they don't work, go look for NS records
1111      cerr<<"Found nothing in the ANY, but let's try wildcards.."<<endl;
1112      bool wereRetargeted;
1113      if(tryWildcard(p, r, sd, target, wereRetargeted)) {
1114        if(wereRetargeted) {
1115          retargetcount++;
1116          goto retargeted;
1117        }
1118        goto sendit;
1119      }
1120      cerr<<"Found nothing in the ANY and wildcards, let's try NS referral"<<endl;
1121      if(!tryReferral(p, r, sd, target))
1122        makeNXDomain(p, r, target, sd);
1123
1124      goto sendit;
1125    }
1126                                       
1127    if(weRedirected) {
1128      BOOST_FOREACH(rr, rrset) {
1129        if(rr.qtype.getCode() == QType::CNAME) {
1130          r->addRecord(rr);
1131          target = rr.content;
1132          retargetcount++;
1133          goto retargeted;
1134        }
1135      }
1136         
1137    }
1138    else if(weDone) {
1139      BOOST_FOREACH(rr, rrset) {
1140        if((p->qtype.getCode() == QType::ANY || rr.qtype == p->qtype) && rr.auth) 
1141          r->addRecord(rr);
1142      }
1143
1144      if(p->qtype.getCode() == QType::ANY) {
1145        completeANYRecords(p, r, sd, target);
1146      }
1147
1148      goto sendit;
1149    }
1150    else if(weHaveUnauth) {
1151      cerr<<"Have unauth data, so need to hunt for best NS records"<<endl;
1152      if(tryReferral(p, r, sd, target))
1153        goto sendit;
1154      cerr<<"Should not get here!!"<<endl;
1155    }
1156    else {
1157      cerr<<"Have some data, but not the right data"<<endl;
1158      makeNOError(p, r, target, sd);
1159    }
1160   
1161  sendit:;
1162    if(doAdditionalProcessingAndDropAA(p,r)<0)
1163      return 0;
1164
1165    //    doDNSSECProcessing(p, r);
1166
1167    r->wrapup(); // needed for inserting in cache
1168    if(!noCache) {
1169      PC.insert(p,r); // in the packet cache
1170    }
1171  }
1172  catch(DBException &e) {
1173    L<<Logger::Error<<"Database module reported condition which prevented lookup ("+e.reason+") sending out servfail"<<endl;
1174    r->setRcode(RCode::ServFail);
1175    S.inc("servfail-packets");
1176    S.ringAccount("servfail-queries",p->qdomain);
1177  }
1178  catch(std::exception &e) {
1179    L<<Logger::Error<<"Exception building answer packet ("<<e.what()<<") sending out servfail"<<endl;
1180    delete r;
1181    r=p->replyPacket();  // generate an empty reply packet   
1182    r->setRcode(RCode::ServFail);
1183    S.inc("servfail-packets");
1184    S.ringAccount("servfail-queries",p->qdomain);
1185  }
1186  return r; 
1187
1188}
1189
Note: See TracBrowser for help on using the browser.