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

Revision 1571, 33.2 KB (checked in by ahu, 3 years ago)

make sure we do not add NSEC records to non-DNSSEC queries

  • 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/*
42#undef DLOG
43#define DLOG(x) x
44*/
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   
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    B.lookup(QType(QType::NS), subdomain, p, sd.domain_id);
290    while(B.get(rr)) {
291      if(!rr.auth)
292        ret.push_back(rr);
293    }
294    if(!ret.empty())
295      return ret;
296  } while( chopOff( subdomain ) );   // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> ''
297  return ret;
298}
299
300vector<DNSResourceRecord> PacketHandler::getBestWildcard(DNSPacket *p, SOAData& sd, const string &target)
301{
302  vector<DNSResourceRecord> ret;
303  DNSResourceRecord rr;
304  string subdomain(target);
305  while( chopOff( subdomain ))  {
306    B.lookup(QType(QType::ANY), "*."+subdomain, p, sd.domain_id);
307    while(B.get(rr)) {
308      if(rr.qtype == p->qtype ||rr.qtype.getCode() == QType::CNAME )
309        ret.push_back(rr);
310    }
311   
312    if(!ret.empty())
313      return ret;
314  } 
315
316  return ret;
317}
318
319
320/** returns 1 in case of a straight match, 2 in case of a wildcard CNAME (groan), 0 in case of no hit */
321int PacketHandler::doWildcardRecords(DNSPacket *p, DNSPacket *r, string &target)
322{
323  DNSResourceRecord rr;
324  bool found=false, retargeted=false;
325
326  // try chopping off domains and look for wildcard matches
327
328  // *.pietje.nl IN  A 1.2.3.4
329  // pietje.nl should now NOT match, but www.pietje.nl should
330
331  string subdomain=target;
332  string::size_type pos;
333
334  while((pos=subdomain.find("."))!=string::npos) {
335    subdomain=subdomain.substr(pos+1);
336    // DLOG();
337
338    string searchstr=string("*.")+subdomain;
339
340    B.lookup(QType(QType::ANY), searchstr,p); // start our search at the backend
341
342    while(B.get(rr)) { // read results
343      if(retargeted)
344        continue;
345      found=true;
346      if((p->qtype.getCode()==QType::ANY || rr.qtype==p->qtype) || rr.qtype.getCode()==QType::CNAME) {
347        rr.qname=target;
348
349        if(d_doFancyRecords && p->qtype.getCode()==QType::ANY && (rr.qtype.getCode()==QType::URL || rr.qtype.getCode()==QType::CURL)) {
350          rr.content=::arg()["urlredirector"];
351          rr.qtype=QType::A; 
352        }
353
354        r->addRecord(rr);  // and add
355        if(rr.qtype.getCode()==QType::CNAME) {
356          if(target==rr.content) {
357            L<<Logger::Error<<"Ignoring wildcard CNAME '"<<rr.qname<<"' pointing at itself"<<endl;
358            r->setRcode(RCode::ServFail);
359            continue;
360          }
361         
362          DLOG(L<<Logger::Error<<"Retargeting because of wildcard cname, from "<<target<<" to "<<rr.content<<endl);
363         
364          target=rr.content; // retarget
365          retargeted=true;
366        }
367      }
368      else if(d_doFancyRecords && ::arg().mustDo("wildcard-url") && p->qtype.getCode()==QType::A && rr.qtype.getName()=="URL") {
369        rr.content=::arg()["urlredirector"];
370        rr.qtype=QType::A; 
371        rr.qname=target;
372       
373        r->addRecord(rr);
374      }
375    }
376    if(found) {
377      DLOG(L<<"Wildcard match on '"<<string("*.")+subdomain<<"'"<<", retargeted="<<retargeted<<endl);
378      return retargeted ? 2 : 1;
379    }
380  }
381  DLOG(L<<"Returning no hit for '"<<string("*.")+subdomain<<"'"<<endl);
382  return 0;
383}
384
385/** dangling is declared true if we were unable to resolve everything */
386int PacketHandler::doAdditionalProcessingAndDropAA(DNSPacket *p, DNSPacket *r)
387{
388  DNSResourceRecord rr;
389  SOAData sd;
390
391  if(p->qtype.getCode()!=QType::AXFR) { // this packet needs additional processing
392    vector<DNSResourceRecord *> arrs=r->getAPRecords();
393    if(arrs.empty()) 
394      return 1;
395
396    DLOG(L<<Logger::Warning<<"This packet needs additional processing!"<<endl);
397
398    vector<DNSResourceRecord> crrs;
399
400    for(vector<DNSResourceRecord *>::const_iterator i=arrs.begin();
401        i!=arrs.end();  ++i) 
402      crrs.push_back(**i);
403
404    // we now have a copy, push_back on packet might reallocate!
405
406    for(vector<DNSResourceRecord>::const_iterator i=crrs.begin();
407        i!=crrs.end();
408        ++i) {
409     
410      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
411        r->d.aa=false;
412        //      i->d_place=DNSResourceRecord::AUTHORITY; // XXX FIXME
413      }
414
415      QType qtypes[2];
416      qtypes[0]="A"; qtypes[1]="AAAA";
417      for(int n=0 ; n < d_doIPv6AdditionalProcessing + 1; ++n) {
418        B.lookup(qtypes[n],i->content,p); 
419        bool foundOne=false;
420        while(B.get(rr)) {
421          foundOne=true;
422          if(rr.domain_id!=i->domain_id && ::arg()["out-of-zone-additional-processing"]=="no") {
423            DLOG(L<<Logger::Warning<<"Not including out-of-zone additional processing of "<<i->qname<<" ("<<rr.qname<<")"<<endl);
424            continue; // not adding out-of-zone additional data
425          }
426         
427          rr.d_place=DNSResourceRecord::ADDITIONAL;
428          r->addRecord(rr);
429        }
430      }
431    }
432  }
433  return 1;
434}
435
436
437void PacketHandler::emitNSEC(const std::string& begin, const std::string& end, const std::string& toNSEC, DNSPacket *r, int mode)
438{
439  cerr<<"We should emit '"<<begin<<"' - ('"<<toNSEC<<"') - '"<<end<<"'"<<endl;
440  NSECRecordContent nrc;
441  nrc.d_set.insert(QType::RRSIG);
442  nrc.d_set.insert(QType::NSEC);
443
444  DNSResourceRecord rr;
445  B.lookup(QType(QType::ANY), begin);
446  while(B.get(rr)) {
447    nrc.d_set.insert(rr.qtype.getCode());   
448  }
449 
450  nrc.d_next=end;
451
452  rr.qname=begin;
453  rr.ttl=3600;
454  rr.qtype=QType::NSEC;
455  rr.content=nrc.getZoneRepresentation();
456  rr.d_place = (mode == 2 ) ? DNSResourceRecord::ANSWER: DNSResourceRecord::AUTHORITY;
457  rr.auth = true;
458  r->addRecord(rr);
459}
460
461/* mode 0 = no error -> an NSEC that starts with 'target', in authority section
462   mode 1 = NXDOMAIN -> an NSEC from auth to first + a covering NSEC
463   mode 2 = ANY or direct NSEC request  -> an NSEC that starts with 'target'
464   mode 3 = a covering NSEC in the authority section (like 1, except for first)
465*/
466
467void PacketHandler::addNSEC(DNSPacket *p, DNSPacket *r, const string& target, const string& auth, int mode)
468{
469  if(!p->d_dnssecOk)
470    return;
471 
472  cerr<<"Should add NSEC covering '"<<target<<"' from zone '"<<auth<<"', mode = "<<mode<<endl;
473  SOAData sd;
474  sd.db=(DNSBackend *)-1; // force uncached answer
475
476  if(auth.empty()) {
477    getAuth(p, &sd, target, 0);
478  }
479  else if(!B.getSOA(auth, sd)) {
480    cerr<<"Could not get SOA for domain\n";
481    return;
482  }
483
484  string before,after;
485  cerr<<"Calling getBeforeandAfter!"<<endl;
486  sd.db->getBeforeAndAfterNames(sd.domain_id, target, before, after); 
487  cerr<<"Done calling"<<endl;
488
489  // this stuff is wrong
490 
491  if(mode ==0 || mode==2)
492    emitNSEC(target, after, target, r, mode);
493 
494
495  if(mode == 1)  {
496    emitNSEC(before, after, target, r, mode);
497
498    sd.db->getBeforeAndAfterNames(sd.domain_id, auth, before, after); 
499    emitNSEC(auth, after, auth, r, mode);
500  }
501
502  if(mode == 3)
503    emitNSEC(before, after, target, r, mode);
504
505  return;
506}
507
508bool PacketHandler::doDNSSECProcessing(DNSPacket *p, DNSPacket *r)
509{
510  if(!p->d_dnssecOk)
511    return false;
512
513  vector<DNSResourceRecord *> arrs=r->getAnswerRecords();
514  if(arrs.empty()) 
515    return false;
516 
517  cerr<<"Have arrs "<<arrs.size()<<" records to sign\n";
518  vector<DNSResourceRecord> crrs;
519 
520  for(vector<DNSResourceRecord *>::const_iterator i=arrs.begin();
521      i!=arrs.end();    ++i) 
522    crrs.push_back(**i);
523 
524  // we now have a copy, push_back on packet might reallocate!
525 
526  for(vector<DNSResourceRecord>::const_iterator i=crrs.begin();
527      i!=crrs.end();
528      ++i) {
529    if(i->d_place!=DNSResourceRecord::ANSWER) 
530      continue;
531   
532    B.lookup(QType(QType::RRSIG),i->qname,p); 
533    DNSResourceRecord rr;
534    while(B.get(rr)) {
535      rr.d_place=DNSResourceRecord::ANSWER;
536      if(splitField(rr.content, ' ').first==i->qtype.getName())
537        r->addRecord(rr);
538    }
539  }
540 
541  return false;
542}
543
544/* returns 1 if everything is done & ready, 0 if the search should continue, 2 if a 'NO-ERROR' response should be generated */
545int PacketHandler::makeCanonic(DNSPacket *p, DNSPacket *r, string &target)
546{
547  DNSResourceRecord rr;
548
549  bool found=false, rfound=false;
550
551  if(p->qtype.getCode()!=QType::CNAME && !d_doCNAME)
552    return 0;
553
554  // Traverse a CNAME chain if needed
555  for(int numloops=0;;numloops++) {
556    if(numloops==10) {
557      L<<Logger::Error<<"Detected a CNAME loop involving "<<target<<", sending SERVFAIL"<<endl;
558      r->setRcode(2);
559      return 1;
560    }
561
562    B.lookup(QType(QType::ANY),target,p);
563       
564    bool shortcut=p->qtype.getCode()!=QType::SOA && p->qtype.getCode()!=QType::ANY;
565    int hits=0;
566    bool relevantNS=false;
567    bool sawDS=false;
568    bool crossedZoneCut = false;
569    while(B.get(rr)) {
570      if(rr.qtype.getCode() == QType::NS && p->qtype.getCode() != QType::NS) { // possible retargeting
571        relevantNS=true;
572      }
573
574      if(rr.qtype.getCode()==QType::DS && p->qtype.getCode() == QType::NS && p->d_dnssecOk) {
575        sawDS = true;
576        r->addRecord(rr);
577      }
578
579      if(rr.qtype.getCode()!=QType::NS || p->qtype.getCode()==QType::NS)
580        hits++;
581      if(!rfound && rr.qtype.getCode()==QType::CNAME) {
582        found=true;
583        r->addRecord(rr);
584        target=rr.content; // for retargeting
585      }
586      if(shortcut && !found && rr.qtype==p->qtype) {
587        if(!rr.auth) {
588         
589        }
590         
591        rfound=true;
592        r->addRecord(rr);
593      }
594    }
595
596    if(crossedZoneCut) {
597      cerr<<"Should return NS records, and this A/AAAA record in the additional section.."<<endl;
598    }
599
600    if(!sawDS && p->qtype.getCode() == QType::NS && p->d_dnssecOk && rfound) {
601      addNSEC(p, r, p->qdomain, "", 2); // make it 'official' that we have no DS
602    }
603
604    if(hits && !relevantNS && !found && !rfound && shortcut ) { // XXX FIXME !numloops. we found matching qnames but not a qtype
605      DLOG(L<<"Found matching qname, but not the qtype"<<endl);
606      return 2;
607    }
608
609    if(rfound)
610      return 1; // ANY lookup found the right answer immediately
611
612    if(found) {
613      if(p->qtype.getCode()==QType::CNAME) // they really wanted a CNAME!
614        return 1;
615      DLOG(L<<"Looping because of a CNAME to "<<target<<endl);
616      found=false;
617    }
618    else 
619      break;
620  }
621
622  // we now have what we really search for ready in 'target'
623  return 0;
624}
625
626/* Semantics:
627   
628- only one backend owns the SOA of a zone
629- only one AXFR per zone at a time - double startTransaction should fail
630- backends need to implement transaction semantics
631
632
633How BindBackend would implement this:
634   startTransaction makes a file
635   feedRecord sends everything to that file
636   commitTransaction moves that file atomically over the regular file, and triggers a reload
637   rollbackTransaction removes the file
638
639
640How PostgreSQLBackend would implement this:
641   startTransaction starts a sql transaction, which also deletes all records
642   feedRecord is an insert statement
643   commitTransaction commits the transaction
644   rollbackTransaction aborts it
645
646How MySQLBackend would implement this:
647   (good question!)
648   
649*/     
650
651int PacketHandler::trySuperMaster(DNSPacket *p)
652{
653  Resolver::res_t nsset;
654  try {
655    Resolver resolver;
656    uint32_t theirserial;
657    resolver.getSoaSerial(p->getRemote(),p->qdomain, &theirserial); 
658 
659    resolver.resolve(p->getRemote(),p->qdomain.c_str(), QType::NS);
660
661    nsset=resolver.result();
662  }
663  catch(ResolverException &re) {
664    L<<Logger::Error<<"Error resolving SOA or NS at: "<< p->getRemote() <<": "<<re.reason<<endl;
665    return RCode::ServFail;
666  }
667
668  string account;
669  DNSBackend *db;
670  if(!B.superMasterBackend(p->getRemote(), p->qdomain, nsset, &account, &db)) {
671    L<<Logger::Error<<"Unable to find backend willing to host "<<p->qdomain<<" for potential supermaster "<<p->getRemote()<<endl;
672    return RCode::Refused;
673  }
674  db->createSlaveDomain(p->getRemote(),p->qdomain,account);
675  Communicator.addSuckRequest(p->qdomain, p->getRemote()); 
676  L<<Logger::Warning<<"Created new slave zone '"<<p->qdomain<<"' from supermaster "<<p->getRemote()<<", queued axfr"<<endl;
677  return RCode::NoError;
678}
679
680int PacketHandler::processNotify(DNSPacket *p)
681{
682  /* now what?
683     was this notification from an approved address?
684     We determine our internal SOA id (via UeberBackend)
685     We determine the SOA at our (known) master
686     if master is higher -> do stuff
687  */
688  if(!::arg().mustDo("slave")) {
689    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" but slave support is disabled in the configuration"<<endl;
690    return RCode::NotImp;
691  }
692  DNSBackend *db=0;
693  DomainInfo di;
694  di.serial = 0;
695  if(!B.getDomainInfo(p->qdomain, di) || !(db=di.backend)) {
696    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" for which we are not authoritative"<<endl;
697    return trySuperMaster(p);
698  }
699   
700  string authServer(p->getRemote());
701  if(::arg().contains("trusted-notification-proxy", p->getRemote())) {
702    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from trusted-notification-proxy "<< p->getRemote()<<endl;
703    if(di.masters.empty()) {
704      L<<Logger::Error<<"However, "<<p->qdomain<<" does not have any masters defined"<<endl;
705      return RCode::Refused;
706    }
707
708    authServer = *di.masters.begin();
709
710  }
711  else if(!db->isMaster(p->qdomain, p->getRemote())) {
712    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" which is not a master"<<endl;
713    return RCode::Refused;
714  }
715
716  uint32_t theirserial=0;
717
718  /* to quote Rusty Russell - this code is so bad that you can actually hear it suck */
719  /* this is an instant DoS, just spoof notifications from the address of the master and we block  */
720
721  Resolver resolver;
722  try {
723    resolver.getSoaSerial(authServer, p->qdomain, &theirserial);
724  }
725  catch(ResolverException& re) {
726    L<<Logger::Error<<re.reason<<endl;
727    return RCode::ServFail;
728  }
729
730  if(theirserial<=di.serial) {
731    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<< authServer <<", we are up to date: "<<
732      theirserial<<"<="<<di.serial<<endl;
733    return RCode::NoError;
734  }
735  else {
736    L<<Logger::Error<<"Received valid NOTIFY for "<<p->qdomain<<" (id="<<di.id<<") from master "<<p->getRemote()<<": "<<
737      theirserial<<" > "<<di.serial<<endl;
738
739    Communicator.addSuckRequest(p->qdomain, authServer, true); // priority
740  }
741  return -1; 
742}
743
744
745
746bool validDNSName(const string &name)
747{
748  string::size_type pos, length=name.length();
749  char c;
750  for(pos=0; pos < length; ++pos) {
751    c=name[pos];
752    if(!((c >= 'a' && c <= 'z') ||
753         (c >= 'A' && c <= 'Z') ||
754         (c >= '0' && c <= '9') ||
755         c =='-' || c == '_' || c=='*' || c=='.' || c=='/' || c=='@'))
756      return false;
757  }
758  return true;
759} 
760
761DNSPacket *PacketHandler::question(DNSPacket *p)
762{
763  bool shouldRecurse=false;
764  DNSPacket *ret=questionOrRecurse(p, &shouldRecurse);
765  if(shouldRecurse) {
766    DP->sendPacket(p);
767  }
768  return ret;
769}
770
771void PacketHandler::synthesiseRRSIGs(DNSPacket* p, DNSPacket* r)
772{
773  cerr<<"Need to fake up the RRSIGs some way.."<<endl;
774  B.lookup(QType(QType::ANY), p->qdomain, p);
775 
776  DNSResourceRecord rr;
777 
778  typedef map<uint16_t, vector<shared_ptr<DNSRecordContent> > > records_t;
779  records_t records;
780
781  NSECRecordContent nrc;
782  nrc.d_set.insert(QType::RRSIG);
783  nrc.d_set.insert(QType::NSEC);
784
785  while(B.get(rr)) {
786    if(!rr.auth) 
787      continue;
788   
789    // this needs to deal with the 'prio' mismatch!
790    if(rr.qtype.getCode()==QType::MX || rr.qtype.getCode() == QType::SRV) { 
791      rr.content = lexical_cast<string>(rr.priority) + " " + rr.content;
792    }
793   
794    if(!rr.content.empty() && rr.qtype.getCode()==QType::TXT && rr.content[0]!='"') {
795      rr.content="\""+rr.content+"\"";
796    }
797    if(rr.content.empty())  // empty contents confuse the MOADNS setup
798      rr.content=".";
799    shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content)); 
800   
801    records[rr.qtype.getCode()].push_back(drc);
802    nrc.d_set.insert(rr.qtype.getCode());
803  }
804
805  // now get the fucking NSEC too..
806
807  SOAData sd;
808  sd.db=(DNSBackend *)-1; // force uncached answer
809  getAuth(p, &sd, p->qdomain, 0);
810
811  string before,after;
812  sd.db->getBeforeAndAfterNames(sd.domain_id, p->qdomain, before, after); 
813
814  nrc.d_next=after;
815
816  rr.qname=p->qdomain;
817  rr.ttl=3600;
818  rr.qtype=QType::NSEC;
819  rr.content=nrc.getZoneRepresentation();
820
821  records[QType::NSEC].push_back(shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content)));
822
823  // ok, the NSEC is in..
824
825  cerr<<"Have "<<records.size()<<" rrsets to sign"<<endl;
826
827  rr.qname = p->qdomain;
828  rr.ttl = 3600;
829  rr.auth = 0; // please don't sign this!
830  rr.d_place = DNSResourceRecord::ANSWER;
831  rr.qtype = QType::RRSIG;
832
833  BOOST_FOREACH(records_t::value_type& iter, records) {
834    RRSIGRecordContent rrc;
835    for(int ksk =0 ; ksk < 2; ++ksk) {
836      getRRSIGForRRSET(::arg()["key-repository"], p->qdomain, iter.first, 3600, iter.second, rrc, ksk);
837      rr.content=rrc.getZoneRepresentation();
838      r->addRecord(rr);
839      if(iter.first != QType::DNSKEY)
840        break;
841    }
842  }
843}
844
845void PacketHandler::makeNXDomain(DNSPacket* p, DNSPacket* r, const std::string& target, SOAData& sd)
846{
847  DNSResourceRecord rr;
848  rr.qname=sd.qname;
849  rr.qtype=QType::SOA;
850  rr.content=serializeSOAData(sd);
851  rr.ttl=sd.ttl;
852  rr.domain_id=sd.domain_id;
853  rr.d_place=DNSResourceRecord::AUTHORITY;
854  r->addRecord(rr);
855 
856  if(p->d_dnssecOk)
857    addNSEC(p, r, target, sd.qname, 1);
858  r->setRcode(RCode::NXDomain); 
859  S.ringAccount("nxdomain-queries",p->qdomain+"/"+p->qtype.getName());
860}
861
862void PacketHandler::makeNOError(DNSPacket* p, DNSPacket* r, const std::string& target, SOAData& sd)
863{
864  DNSResourceRecord rr;
865  rr.qname=sd.qname;
866  rr.qtype=QType::SOA;
867  rr.content=serializeSOAData(sd);
868  rr.ttl=sd.ttl;
869  rr.domain_id=sd.domain_id;
870  rr.d_place=DNSResourceRecord::AUTHORITY;
871  r->addRecord(rr);
872 
873  if(p->d_dnssecOk)
874    addNSEC(p, r, target, sd.qname, 0);
875
876  S.ringAccount("noerror-queries",p->qdomain+"/"+p->qtype.getName());
877}
878
879
880bool PacketHandler::addDSforNS(DNSPacket* p, DNSPacket* r, SOAData& sd, const string& dsname)
881{
882  B.lookup(QType(QType::DS), dsname, p, sd.domain_id);
883  DNSResourceRecord rr;
884  bool gotOne=false;
885  while(B.get(rr)) {
886    gotOne=true;
887    rr.d_place = DNSResourceRecord::AUTHORITY;
888    r->addRecord(rr);
889  }
890  return gotOne;
891}
892
893bool PacketHandler::tryReferral(DNSPacket *p, DNSPacket*r, SOAData& sd, const string &target)
894{
895  vector<DNSResourceRecord> rrset = getBestReferralNS(p, sd, target);
896  if(rrset.empty())
897    return false;
898 
899  cerr<<"The best NS is: "<<rrset.begin()->qname<<endl;
900  BOOST_FOREACH(DNSResourceRecord rr, rrset) {
901    cerr<<"\tadding '"<<rr.content<<"'\n";
902    rr.d_place=DNSResourceRecord::AUTHORITY;
903    r->addRecord(rr);
904  }
905  r->setA(false);
906
907  if(!addDSforNS(p, r, sd, rrset.begin()->qname))
908    addNSEC(p, r, rrset.begin()->qname, sd.qname, 0);
909 
910  return true;
911}
912
913void PacketHandler::completeANYRecords(DNSPacket *p, DNSPacket*r, SOAData& sd, const string &target)
914{
915  if(!p->d_dnssecOk)
916    cerr<<"Need to add all the RRSIGs too for '"<<target<<"'"<<endl;
917  //  cerr<<"Need to add all the NSEC too.."<<endl; /// XXX FIXME THE ABOVE IF IS WEIRD
918  addNSEC(p, r, target, sd.qname, 2); 
919}
920
921bool PacketHandler::tryWildcard(DNSPacket *p, DNSPacket*r, SOAData& sd, string &target, bool& retargeted)
922{
923  retargeted=false;
924
925  vector<DNSResourceRecord> rrset = getBestWildcard(p, sd, target);
926  if(rrset.empty())
927    return false;
928
929  cerr<<"The best wildcard match: "<<rrset.begin()->qname<<endl;
930  BOOST_FOREACH(DNSResourceRecord rr, rrset) {
931    if(rr.qtype.getCode() == QType::CNAME)  {
932      retargeted=true;
933      target=rr.content;
934    }
935
936    rr.wildcardname = rr.qname;
937    rr.qname=p->qdomain;
938    cerr<<"\tadding '"<<rr.content<<"'\n";
939    rr.d_place=DNSResourceRecord::ANSWER;
940    r->addRecord(rr);
941  }
942
943  if(p->d_dnssecOk) {
944    addNSEC(p, r, p->qdomain, sd.qname, 3);
945  }
946  return true;
947}
948
949
950//! Called by the Distributor to ask a question. Returns 0 in case of an error
951DNSPacket *PacketHandler::questionOrRecurse(DNSPacket *p, bool *shouldRecurse)
952{
953  *shouldRecurse=false;
954  DNSResourceRecord rr;
955  SOAData sd;
956  sd.db=0;
957 
958  string subdomain="";
959  string soa;
960  int retargetcount=0;
961
962  vector<DNSResourceRecord> rrset;
963  bool weDone=0, weRedirected=0, weHaveUnauth=0;
964
965  DNSPacket *r=0;
966  try {   
967    DLOG(L << Logger::Notice<<"Remote "<< p->remote.toString() <<" wants a type " << p->qtype.getName() << " ("<<p->qtype.getCode()<<") about '"<<p->qdomain << "'" << endl);
968
969    if(p->d.qr) { // QR bit from dns packet (thanks RA from N)
970      L<<Logger::Error<<"Received an answer (non-query) packet from "<<p->getRemote()<<", dropping"<<endl;
971      S.inc("corrupt-packets");
972      return 0;
973    }
974
975    // XXX FIXME do this in DNSPacket::parse ?
976
977    if(!validDNSName(p->qdomain)) {
978      if(::arg().mustDo("log-dns-details"))
979        L<<Logger::Error<<"Received a malformed qdomain from "<<p->getRemote()<<", '"<<p->qdomain<<"': sending servfail"<<endl;
980      S.inc("corrupt-packets");
981      r=p->replyPacket(); 
982      r->setRcode(RCode::ServFail);
983      return r;
984    }
985    if(p->d.opcode) { // non-zero opcode (again thanks RA!)
986      if(p->d.opcode==Opcode::Update) {
987        if(::arg().mustDo("log-failed-updates"))
988          L<<Logger::Notice<<"Received an UPDATE opcode from "<<p->getRemote()<<" for "<<p->qdomain<<", sending NOTIMP"<<endl;
989        r=p->replyPacket(); 
990        r->setRcode(RCode::NotImp); // notimp;
991        return r; 
992      }
993      else if(p->d.opcode==Opcode::Notify) {
994        int res=processNotify(p);
995        if(res>=0) {
996          DNSPacket *r=p->replyPacket();
997          r->setRcode(res);
998          r->setOpcode(Opcode::Notify);
999          return r;
1000        }
1001        return 0;
1002      }
1003     
1004      L<<Logger::Error<<"Received an unknown opcode "<<p->d.opcode<<" from "<<p->getRemote()<<" for "<<p->qdomain<<endl;
1005
1006      r=p->replyPacket(); 
1007      r->setRcode(RCode::NotImp); 
1008      return r; 
1009    }
1010
1011    // L<<Logger::Warning<<"Query for '"<<p->qdomain<<"' "<<p->qtype.getName()<<" from "<<p->getRemote()<<endl;
1012   
1013    r=p->replyPacket();  // generate an empty reply packet
1014    if(p->d.rd && d_doRecursion && DP->recurseFor(p))  // make sure we set ra if rd was set, and we'll do it
1015      r->d.ra=true;
1016
1017    if(p->qtype.getCode()==QType::IXFR) {
1018      r->setRcode(RCode::NotImp);
1019      return r;
1020    }
1021
1022    // please don't query fancy records directly!
1023    if(d_doFancyRecords && (p->qtype.getCode()==QType::URL || p->qtype.getCode()==QType::CURL || p->qtype.getCode()==QType::MBOXFW)) {
1024      r->setRcode(RCode::ServFail);
1025      return r;
1026    }
1027
1028    bool found=false;
1029   
1030    string target=p->qdomain;
1031    bool noCache=false;
1032
1033    if(doDNSKEYRequest(p,r)) 
1034      goto sendit;
1035
1036    if(doVersionRequest(p,r,target)) // catch version.bind requests
1037      goto sendit;
1038
1039    if(p->qclass==255) // any class query
1040      r->setA(false);
1041    else if(p->qclass!=1) // we only know about IN, so we don't find anything
1042      goto sendit;
1043
1044  retargeted:;
1045    if(retargetcount > 10) {    // XXX FIXME, retargetcount++?
1046      r->setRcode(RCode::ServFail);
1047      return r;
1048    }
1049
1050    if(!getAuth(p, &sd, target, 0)) {
1051      r->setA(false);
1052      if(::arg().mustDo("send-root-referral")) {
1053        DLOG(L<<Logger::Warning<<"Adding root-referral"<<endl);
1054        addRootReferral(r);
1055      }
1056      else {
1057        DLOG(L<<Logger::Warning<<"Adding SERVFAIL"<<endl);
1058        r->setRcode(RCode::ServFail);  // 'sorry'
1059    }
1060          goto sendit;
1061      }
1062     
1063    // we know we have authority
1064
1065    if(p->qtype.getCode() == QType::SOA) {
1066        rr.qname=sd.qname;
1067        rr.qtype=QType::SOA;
1068        rr.content=serializeSOAData(sd);
1069        rr.ttl=sd.ttl;
1070        rr.domain_id=sd.domain_id;
1071        rr.d_place=DNSResourceRecord::ANSWER;
1072        r->addRecord(rr);
1073          goto sendit;
1074        }
1075
1076    // this TRUMPS a cname!
1077    if(p->qtype.getCode() == QType::NSEC && p->d_dnssecOk) {
1078      addNSEC(p, r, target, "", 2); // only NSEC please
1079        goto sendit;
1080    }
1081   
1082    // this TRUMPS a cname!
1083    if(p->qtype.getCode() == QType::RRSIG && p->d_dnssecOk) {
1084      synthesiseRRSIGs(p, r);
1085        goto sendit; 
1086      }
1087
1088    // see what we get..
1089    B.lookup(QType(QType::ANY), target, p, sd.domain_id);
1090    rrset.clear();
1091    weDone=weRedirected=weHaveUnauth=0;
1092   
1093    while(B.get(rr)) {
1094      if((p->qtype.getCode() == QType::ANY || rr.qtype == p->qtype) && rr.auth) 
1095        weDone=1;
1096      if((rr.qtype == p->qtype || rr.qtype.getCode() == QType::NS) && !rr.auth) 
1097        weHaveUnauth=1;
1098
1099      if(rr.qtype.getCode() == QType::CNAME && p->qtype.getCode() != QType::CNAME) 
1100        weRedirected=1;
1101      rrset.push_back(rr);
1102    }
1103
1104    cerr<<"After first ANY query: weDone="<<weDone<<", weHaveUnauth="<<weHaveUnauth<<", weRedirected="<<weRedirected<<endl;
1105
1106    if(rrset.empty()) {
1107      // try wildcards, and if they don't work, go look for NS records
1108      cerr<<"Found nothing in the ANY, but let's try the NS set"<<endl;
1109      bool wereRetargeted;
1110      if(tryWildcard(p, r, sd, target, wereRetargeted)) {
1111        if(wereRetargeted) {
1112          retargetcount++;
1113          goto retargeted;
1114      }
1115        goto sendit;
1116    }
1117   
1118      if(!tryReferral(p, r, sd, target))
1119        makeNXDomain(p, r, target, sd);
1120
1121        goto sendit;
1122      }
1123                                       
1124    if(weRedirected) {
1125      BOOST_FOREACH(rr, rrset) {
1126        if(rr.qtype.getCode() == QType::CNAME) {
1127          r->addRecord(rr);
1128          target = rr.content;
1129          retargetcount++;
1130          goto retargeted;
1131    }
1132        }
1133         
1134          }
1135    else if(weDone) {
1136      BOOST_FOREACH(rr, rrset) {
1137        if((p->qtype.getCode() == QType::ANY || rr.qtype == p->qtype) && rr.auth) 
1138          r->addRecord(rr);
1139        }
1140
1141      if(p->qtype.getCode() == QType::ANY) {
1142        completeANYRecords(p, r, sd, target);
1143        }
1144
1145      goto sendit;
1146        }
1147    else if(weHaveUnauth) {
1148      cerr<<"Have unauth data, so need to hunt for best NS records"<<endl;
1149      if(tryReferral(p, r, sd, target))
1150        goto sendit;
1151      cerr<<"Should not get here!!"<<endl;
1152        }
1153        else {
1154      cerr<<"Have some data, but not the right data"<<endl;
1155      makeNOError(p, r, target, sd);
1156      }
1157   
1158  sendit:;
1159    if(doAdditionalProcessingAndDropAA(p,r)<0)
1160      return 0;
1161
1162    //    doDNSSECProcessing(p, r);
1163
1164    r->wrapup(); // needed for inserting in cache
1165    if(!noCache) {
1166      PC.insert(p,r); // in the packet cache
1167    }
1168  }
1169  catch(DBException &e) {
1170    L<<Logger::Error<<"Database module reported condition which prevented lookup ("+e.reason+") sending out servfail"<<endl;
1171    r->setRcode(RCode::ServFail);
1172    S.inc("servfail-packets");
1173    S.ringAccount("servfail-queries",p->qdomain);
1174  }
1175  catch(std::exception &e) {
1176    L<<Logger::Error<<"Exception building answer packet ("<<e.what()<<") sending out servfail"<<endl;
1177    delete r;
1178    r=p->replyPacket();  // generate an empty reply packet   
1179    r->setRcode(RCode::ServFail);
1180    S.inc("servfail-packets");
1181    S.ringAccount("servfail-queries",p->qdomain);
1182  }
1183  return r; 
1184
1185}
1186
Note: See TracBrowser for help on using the browser.