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

Revision 947, 24.8 KB (checked in by ahu, 6 years ago)

fix 'ns with identical glue' giving sort of NXRRSET, plus regression test. Should close Augie Schwer's issue.

  • 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-2007  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 "utility.hh"
19#include <string>
20#include <sys/types.h>
21#include <boost/algorithm/string.hpp>
22#include "dns.hh"
23#include "dnsbackend.hh"
24#include "ueberbackend.hh"
25#include "dnspacket.hh"
26#include "nameserver.hh"
27#include "distributor.hh"
28#include "logger.hh"
29#include "arguments.hh"
30#include "packethandler.hh"
31#include "statbag.hh"
32#include "resolver.hh"
33#include "communicator.hh"
34#include "dnsproxy.hh"
35
36extern StatBag S;
37extern PacketCache PC; 
38extern CommunicatorClass Communicator;
39extern DNSProxy *DP;
40
41int PacketHandler::s_count;
42extern string s_programname;
43
44PacketHandler::PacketHandler():B(s_programname)
45{
46  s_count++;
47  d_doFancyRecords = (arg()["fancy-records"]!="no");
48  d_doWildcards = (arg()["wildcards"]!="no");
49  d_doCNAME = (arg()["skip-cname"]=="no");
50  d_doRecursion= arg().mustDo("recursor");
51  d_logDNSDetails= arg().mustDo("log-dns-details");
52  d_doIPv6AdditionalProcessing = arg().mustDo("do-ipv6-additional-processing");
53}
54
55DNSBackend *PacketHandler::getBackend()
56{
57  return &B;
58}
59
60PacketHandler::~PacketHandler()
61{
62  --s_count;
63  DLOG(L<<Logger::Error<<"PacketHandler destructor called - "<<s_count<<" left"<<endl);
64}
65
66void PacketHandler::addRootReferral(DNSPacket* r)
67{ 
68  // nobody reads what we output, but it appears to be the magic that shuts some nameservers up
69  static 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", 
70                     "192.36.148.17","192.58.128.30", "193.0.14.129", "198.32.64.12", "202.12.27.33"};
71  static char templ[40];
72  strncpy(templ,"a.root-servers.net", sizeof(templ) - 1);
73
74  // add . NS records
75  DNSResourceRecord rr;
76  rr.qtype=QType::NS;
77  rr.ttl=518400;
78  rr.d_place=DNSResourceRecord::AUTHORITY;
79 
80  for(char c='a';c<='m';++c) {
81    *templ=c;
82    rr.content=templ;
83    r->addRecord(rr);
84  }
85
86  if(boost::iequals(arg()["send-root-referral"], "lean"))
87     return;
88
89  // add the additional stuff
90 
91  rr.ttl=3600000;
92  rr.qtype=QType::A;
93  rr.d_place=DNSResourceRecord::ADDITIONAL;
94
95  for(char c='a';c<='m';++c) {
96    *templ=c;
97    rr.qname=templ;
98    rr.content=ips[c-'a'];
99    r->addRecord(rr);
100  }
101}
102
103int PacketHandler::findMboxFW(DNSPacket *p, DNSPacket *r, string &target)
104{
105  DNSResourceRecord rr;
106  bool wedoforward=false;
107
108  SOAData sd;
109  int zoneId;
110  if(!getAuth(p, &sd, target, &zoneId))
111    return false;
112
113  B.lookup("MBOXFW",string("%@")+target,p, zoneId);
114     
115  while(B.get(rr))
116    wedoforward=true;
117
118  if(wedoforward) {
119    rr.content=arg()["smtpredirector"];
120    rr.priority=25;
121    rr.ttl=7200;
122    rr.qtype=QType::MX;
123    rr.qname=target;
124   
125    r->addRecord(rr);
126  }
127
128  return wedoforward;
129}
130
131int PacketHandler::findUrl(DNSPacket *p, DNSPacket *r, string &target)
132{
133  DNSResourceRecord rr;
134
135  bool found=false;
136     
137  B.lookup("URL",target,p); // search for a URL before we search for an A
138       
139  while(B.get(rr)) {
140    found=true;
141    DLOG(L << "Found a URL!" << endl);
142    rr.content=arg()["urlredirector"];
143    rr.qtype=QType::A; 
144    rr.qname=target;
145         
146    r->addRecord(rr);
147  } 
148
149  if(found)
150    return 1;
151     
152  // now try CURL
153 
154  B.lookup("CURL",target,p); // search for a URL before we search for an A
155     
156  while(B.get(rr)) {
157    found=true;
158    DLOG(L << "Found a CURL!" << endl);
159    rr.content=arg()["urlredirector"];
160    rr.qtype=1; // A
161    rr.qname=target;
162    rr.ttl=300;
163    r->addRecord(rr);
164  } 
165
166  if(found)
167    return found;
168  return 0;
169}
170
171/** Returns 0 if nothing was found, -1 if an error occured or 1 if the search
172    was satisfied */
173int PacketHandler::doFancyRecords(DNSPacket *p, DNSPacket *r, string &target)
174{
175  DNSResourceRecord rr;
176
177  if(p->qtype.getCode()==QType::MX)  // check if this domain has smtp service from us
178    return findMboxFW(p,r,target);
179 
180  if(p->qtype.getCode()==QType::A)   // search for a URL record for an A
181    return findUrl(p,r,target);
182
183  return 0;
184}
185
186int PacketHandler::doDNSCheckRequest(DNSPacket *p, DNSPacket *r, string &target)
187{
188  int result = 0;
189  DNSResourceRecord rr;
190
191  if (p->qclass == 3 && p->qtype.getName() == "HINFO") {
192    rr.content = "PowerDNS $Id$";
193    rr.ttl = 5;
194    rr.qname=target;
195    rr.qtype=13; // hinfo
196    r->addRecord(rr);
197    result = 1;
198  }
199 
200  return result;
201}
202
203/** This catches version requests. Returns 1 if it was handled, 0 if it wasn't */
204int PacketHandler::doVersionRequest(DNSPacket *p, DNSPacket *r, string &target)
205{
206  DNSResourceRecord rr;
207 
208  // modes: anonymous, powerdns only, full, spoofed
209  const string mode=arg()["version-string"];
210  if(p->qtype.getCode()==QType::TXT && target=="version.bind") {// TXT
211    if(mode.empty() || mode=="full") 
212      rr.content="Served by POWERDNS "VERSION" $Id$";
213    else if(mode=="anonymous") {
214      r->setRcode(RCode::ServFail);
215      return 1;
216    }
217    else if(mode=="powerdns")
218      rr.content="Served by PowerDNS - http://www.powerdns.com";
219    else 
220      rr.content=mode;
221
222    rr.ttl=5;
223    rr.qname=target;
224    rr.qtype=QType::TXT; // TXT
225    r->addRecord(rr);
226   
227    return 1;
228  }
229  return 0;
230}
231
232/** Determines if we are authoritative for a zone, and at what level */
233bool PacketHandler::getAuth(DNSPacket *p, SOAData *sd, const string &target, int *zoneId)
234{
235  string subdomain(target);
236  do {
237    if( B.getSOA( subdomain, *sd, p ) ) {
238      sd->qname = subdomain;
239      if(zoneId)
240        *zoneId = sd->domain_id;
241      return true;
242    }
243  }
244  while( chopOff( subdomain ) );   // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> ''
245  return false;
246}
247
248/** returns 1 in case of a straight match, 2 in case of a wildcard CNAME (groan), 0 in case of no hit */
249int PacketHandler::doWildcardRecords(DNSPacket *p, DNSPacket *r, string &target)
250{
251  DNSResourceRecord rr;
252  bool found=false, retargeted=false;
253
254  // try chopping off domains and look for wildcard matches
255
256  // *.pietje.nl IN  A 1.2.3.4
257  // pietje.nl should now NOT match, but www.pietje.nl should
258
259  string subdomain=target;
260  string::size_type pos;
261  while((pos=subdomain.find("."))!=string::npos) {
262    subdomain=subdomain.substr(pos+1);
263    // DLOG();
264
265    string searchstr=string("*.")+subdomain;
266
267    B.lookup(QType(QType::ANY), searchstr,p); // start our search at the backend
268
269    while(B.get(rr)) { // read results
270      found=true;
271      if((p->qtype.getCode()==QType::ANY || rr.qtype==p->qtype) || rr.qtype.getCode()==QType::CNAME) {
272        rr.qname=target;
273        r->addRecord(rr);  // and add
274        if(rr.qtype.getCode()==QType::CNAME) {
275          if(target==rr.content) {
276            L<<Logger::Error<<"Ignoring wildcard CNAME '"<<rr.qname<<"' pointing at itself"<<endl;
277            r->setRcode(RCode::ServFail);
278            continue;
279          }
280         
281          DLOG(L<<Logger::Error<<"Retargeting because of wildcard cname, from "<<target<<" to "<<rr.content<<endl);
282         
283          target=rr.content; // retarget
284          retargeted=true;
285        }
286      }
287      else if(d_doFancyRecords && arg().mustDo("wildcard-url") && p->qtype.getCode()==QType::A && rr.qtype.getName()=="URL") {
288        rr.content=arg()["urlredirector"];
289        rr.qtype=QType::A; 
290        rr.qname=target;
291       
292        r->addRecord(rr);
293      }
294    }
295    if(found) {
296      DLOG(L<<"Wildcard match on '"<<string("*.")+subdomain<<"'"<<endl);
297      return retargeted ? 2 : 1;
298    }
299  }
300  return 0;
301}
302
303/** dangling is declared true if we were unable to resolve everything */
304int PacketHandler::doAdditionalProcessingAndDropAA(DNSPacket *p, DNSPacket *r)
305{
306  DNSResourceRecord rr;
307  SOAData sd;
308
309  if(p->qtype.getCode()!=QType::AXFR && r->needAP()) { // this packet needs additional processing
310    DLOG(L<<Logger::Warning<<"This packet needs additional processing!"<<endl);
311
312    vector<DNSResourceRecord *> arrs=r->getAPRecords();
313    vector<DNSResourceRecord> crrs;
314
315    for(vector<DNSResourceRecord *>::const_iterator i=arrs.begin();
316        i!=arrs.end();  ++i) 
317      crrs.push_back(**i);
318
319    // we now have a copy, push_back on packet might reallocate!
320
321    for(vector<DNSResourceRecord>::const_iterator i=crrs.begin();
322        i!=crrs.end();
323        ++i) {
324     
325      if(i->qtype.getCode()==QType::NS && !B.getSOA(i->qname,sd,p)) { // drop AA in case of non-SOA-level NS answer
326        r->d.aa=false;
327        //      i->d_place=DNSResourceRecord::AUTHORITY; // XXX FIXME
328      }
329
330      QType qtypes[2];
331      qtypes[0]="A"; qtypes[1]="AAAA";
332      for(int n=0;n < d_doIPv6AdditionalProcessing + 1; ++n) {
333        B.lookup(qtypes[n],i->content,p); 
334        bool foundOne=false;
335        while(B.get(rr)) {
336          foundOne=true;
337          if(rr.domain_id!=i->domain_id && arg()["out-of-zone-additional-processing"]=="no") {
338            DLOG(L<<Logger::Warning<<"Not including out-of-zone additional processing of "<<i->qname<<" ("<<rr.qname<<")"<<endl);
339            continue; // not adding out-of-zone additional data
340          }
341         
342          rr.d_place=DNSResourceRecord::ADDITIONAL;
343          r->addRecord(rr);
344         
345        }
346        if(!foundOne) {
347          if(d_doRecursion && DP->recurseFor(p)) {
348            try {
349              Resolver resolver;
350              resolver.resolve(arg()["recursor"],i->content.c_str(),QType::A);
351              Resolver::res_t res=resolver.result();
352              for(Resolver::res_t::const_iterator j=res.begin();j!=res.end();++j) {
353                if(j->d_place==DNSResourceRecord::ANSWER) {
354                  rr=*j;
355                  rr.d_place=DNSResourceRecord::ADDITIONAL;
356                  r->addRecord(rr);
357                }
358              }
359            }
360            catch(ResolverException& re) {
361              // L<<Logger::Error<<"Trying to do additional processing for answer to '"<<p->qdomain<<"' query: "<<re.reason<<endl;
362            }
363          }
364        }
365      }
366    }
367  }
368  return 1;
369}
370
371/* returns 1 if everything is done & ready, 0 if the search should continue, 2 if a 'NO-ERROR' response should be generated */
372int PacketHandler::makeCanonic(DNSPacket *p, DNSPacket *r, string &target)
373{
374  DNSResourceRecord rr;
375
376  bool found=false, rfound=false;
377
378  if(p->qtype.getCode()!=QType::CNAME && !d_doCNAME)
379    return 0;
380
381  // Traverse a CNAME chain if needed
382  for(int numloops=0;;numloops++) {
383    if(numloops==10) {
384      L<<Logger::Error<<"Detected a CNAME loop involving "<<target<<", sending SERVFAIL"<<endl;
385      r->setRcode(2);
386      return 1;
387    }
388
389    B.lookup(QType(QType::ANY),target,p);
390       
391    bool shortcut=p->qtype.getCode()!=QType::SOA && p->qtype.getCode()!=QType::ANY;
392    int hits=0;
393
394    while(B.get(rr)) {
395      if(rr.qtype.getCode()!=QType::NS || p->qtype.getCode()==QType::NS)
396        hits++;
397      if(!rfound && rr.qtype.getCode()==QType::CNAME) {
398        found=true;
399        r->addRecord(rr);
400        target=rr.content; // for retargeting
401      }
402      if(shortcut && !found && rr.qtype==p->qtype) {
403        rfound=true;
404        r->addRecord(rr);
405      }
406    }
407    if(hits && !found && !rfound && shortcut ) // we found matching qnames but not a qtype
408      return 2;
409
410    if(rfound)
411      return 1; // ANY lookup found the right answer immediately
412
413    if(found) {
414      if(p->qtype.getCode()==QType::CNAME) // they really wanted a CNAME!
415        return 1;
416      DLOG(L<<"Looping because of a CNAME to "<<target<<endl);
417      found=false;
418    }
419    else 
420      break;
421  }
422
423  // we now have what we really search for ready in 'target'
424  return 0;
425}
426
427/* Semantics:
428   
429- only one backend owns the SOA of a zone
430- only one AXFR per zone at a time - double startTransaction should fail
431- backends need to implement transaction semantics
432
433
434How BindBackend would implement this:
435   startTransaction makes a file
436   feedRecord sends everything to that file
437   commitTransaction moves that file atomically over the regular file, and triggers a reload
438   rollbackTransaction removes the file
439
440
441How PostgreSQLBackend would implement this:
442   startTransaction starts a sql transaction, which also deletes all records
443   feedRecord is an insert statement
444   commitTransaction commits the transaction
445   rollbackTransaction aborts it
446
447How MySQLBackend would implement this:
448   (good question!)
449   
450*/     
451
452int PacketHandler::trySuperMaster(DNSPacket *p)
453{
454  Resolver::res_t nsset;
455  try {
456    Resolver resolver;
457    uint32_t theirserial;
458    int res=resolver.getSoaSerial(p->getRemote(),p->qdomain, &theirserial); 
459    if(res<=0) {
460      L<<Logger::Error<<"Unable to determine SOA serial for "<<p->qdomain<<" at potential supermaster "<<p->getRemote()<<endl;
461      return RCode::ServFail;
462    }
463 
464    resolver.resolve(p->getRemote(),p->qdomain.c_str(), QType::NS);
465
466    nsset=resolver.result();
467  }
468  catch(ResolverException &re) {
469    L<<Logger::Error<<"Error resolving SOA or NS for '"<<p->qdomain<<"' at "<<p->getRemote()<<endl;
470    return RCode::ServFail;
471  }
472
473  string account;
474  DNSBackend *db;
475  if(!B.superMasterBackend(p->getRemote(), p->qdomain, nsset, &account, &db)) {
476   L<<Logger::Error<<"Unable to find backend willing to host "<<p->qdomain<<" for potential supermaster "<<p->getRemote()<<endl;
477    return RCode::Refused;
478  }
479  db->createSlaveDomain(p->getRemote(),p->qdomain,account);
480  Communicator.addSuckRequest(p->qdomain, p->getRemote()); 
481  L<<Logger::Warning<<"Created new slave zone '"<<p->qdomain<<"' from supermaster "<<p->getRemote()<<", queued axfr"<<endl;
482  return RCode::NoError;
483}
484
485int PacketHandler::processNotify(DNSPacket *p)
486{
487  /* now what?
488     was this notification from an approved address?
489     We determine our internal SOA id (via UeberBackend)
490     We determine the SOA at our (known) master
491     if master is higher -> do stuff
492  */
493  if(!arg().mustDo("slave")) {
494    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" but slave support is disabled in the configuration"<<endl;
495    return RCode::NotImp;
496  }
497  DNSBackend *db=0;
498  DomainInfo di;
499  if(!B.getDomainInfo(p->qdomain, di) || !(db=di.backend)) {
500    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" for which we are not authoritative"<<endl;
501    return trySuperMaster(p);
502  }
503   
504  if(!db->isMaster(p->qdomain, p->getRemote())) {
505    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" which is not a master"<<endl;
506    return RCode::Refused;
507  }
508
509  uint32_t theirserial=0;
510
511  /* to quote Rusty Russell - this code is so bad that you can actually hear it suck */
512  /* this is an instant DoS, just spoof notifications from the address of the master and we block  */
513
514  Resolver resolver;
515  int res=resolver.getSoaSerial(p->getRemote(),p->qdomain, &theirserial);
516  if(res<=0) {
517    L<<Logger::Error<<"Unable to determine SOA serial for "<<p->qdomain<<" at "<<p->getRemote()<<endl;
518    return RCode::ServFail;
519  }
520       
521
522  if(theirserial<=di.serial) {
523    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from master "<<p->getRemote()<<", we are up to date: "<<
524      theirserial<<"<="<<di.serial<<endl;
525    return RCode::NoError;
526  }
527  else {
528    L<<Logger::Error<<"Received valid NOTIFY for "<<p->qdomain<<" (id="<<di.id<<") from master "<<p->getRemote()<<": "<<
529      theirserial<<" > "<<di.serial<<endl;
530
531    Communicator.addSuckRequest(p->qdomain, p->getRemote(),true); // priority
532  }
533  return -1; 
534}
535
536
537
538bool validDNSName(const string &name)
539{
540  string::size_type pos, length=name.length();
541  char c;
542  for(pos=0; pos < length; ++pos) {
543    c=name[pos];
544    if(!((c >= 'a' && c <= 'z') ||
545         (c >= 'A' && c <= 'Z') ||
546         (c >= '0' && c <= '9') ||
547         c =='-' || c == '_' || c=='*' || c=='.' || c=='/'))
548      return false;
549  }
550  return true;
551} 
552
553//! Called by the Distributor to ask a question. Returns 0 in case of an error
554DNSPacket *PacketHandler::question(DNSPacket *p)
555{
556  DNSResourceRecord rr;
557  SOAData sd;
558  sd.db=0;
559 
560  string subdomain="";
561  string soa;
562  int retargetcount=0;
563  bool noSameLevelNS;
564
565  DNSPacket *r=0;
566  try {   
567    DLOG(L << Logger::Notice<<"Remote "<< p->remote.toString() <<" wants a type " << p->qtype.getName() << " ("<<p->qtype.getCode()<<") about '"<<p->qdomain << "'" << endl);
568
569// XXX FIXME Find out why this isn't working!
570#ifndef WIN32
571    if(p->d.qr) { // QR bit from dns packet (thanks RA from N)
572      L<<Logger::Error<<"Received an answer (non-query) packet from "<<p->getRemote()<<", dropping"<<endl;
573      S.inc("corrupt-packets");
574      return 0;
575    }
576#endif // WIN32
577
578    // XXX FIXME do this in DNSPacket::parse ?
579
580    if(!validDNSName(p->qdomain)) {
581      if(arg().mustDo("log-dns-details"))
582        L<<Logger::Error<<"Received a malformed qdomain from "<<p->getRemote()<<", '"<<p->qdomain<<"': dropping"<<endl;
583      S.inc("corrupt-packets");
584      return 0;
585    }
586    if(p->d.opcode) { // non-zero opcode (again thanks RA!)
587      if(p->d.opcode==Opcode::Update) {
588        if(arg().mustDo("log-failed-updates"))
589          L<<Logger::Notice<<"Received an UPDATE opcode from "<<p->getRemote()<<" for "<<p->qdomain<<", sending NOTIMP"<<endl;
590        r=p->replyPacket(); 
591        r->setRcode(RCode::NotImp); // notimp;
592        return r; 
593      }
594      else if(p->d.opcode==Opcode::Notify) {
595        int res=processNotify(p);
596        if(res>=0) {
597          DNSPacket *r=p->replyPacket();
598          r->setRcode(res);
599          return r;
600        }
601        return 0;
602      }
603     
604      L<<Logger::Error<<"Received an unknown opcode "<<p->d.opcode<<" from "<<p->getRemote()<<" for "<<p->qdomain<<endl;
605
606      r=p->replyPacket(); 
607      r->setRcode(RCode::NotImp); 
608      return r; 
609    }
610   
611    r=p->replyPacket();  // generate an empty reply packet
612
613    if(p->qtype.getCode()==QType::IXFR) {
614      r->setRcode(RCode::NotImp);
615      return r;
616    }
617
618    bool found=false;
619   
620    string target=p->qdomain;
621    bool noCache=false;
622
623    if (doDNSCheckRequest(p, r, target))
624      goto sendit;
625   
626    if(doVersionRequest(p,r,target)) // catch version.bind requests
627      goto sendit;
628
629    if(p->qclass==255) // any class query
630      r->setA(false);
631    else if(p->qclass!=1) // we only know about IN, so we don't find anything
632      goto sendit;
633
634    int mret;
635  retargeted:;
636    if(retargetcount++>10) {
637      L<<Logger::Error<<"Detected wildcard CNAME loop involving '"<<target<<"'"<<endl;
638      r->setRcode(RCode::ServFail);
639      goto sendit;
640    }
641    mret=makeCanonic(p, r, target); // traverse CNAME chain until we have a useful record (may actually give the correct answer!)
642
643    if(mret==2) { // there is some data, but not of the correct type
644      DLOG(L<<"There is some data, but not of the correct type, adding SOA for NXRECORDSET"<<endl);
645      SOAData sd;
646      if(getAuth(p, &sd, target, 0)) {
647        rr.qname=sd.qname;
648        rr.qtype=QType::SOA;
649        rr.content=DNSPacket::serializeSOAData(sd);
650        rr.ttl=sd.ttl;
651        rr.domain_id=sd.domain_id;
652        rr.d_place=DNSResourceRecord::AUTHORITY;
653        r->addRecord(rr);
654      }
655    }
656
657    if(mret == 1) 
658      goto sendit; // this might be the end of it (client requested a CNAME, or we found the answer already)
659   
660
661    if(d_doFancyRecords) { // MBOXFW, URL <- fake records, emulated with MX and A
662      int res=doFancyRecords(p,r,target);
663      if(res) { // had a result
664        if(res<0) // it was an error
665          r->setRcode(RCode::ServFail);
666        goto sendit; 
667      }
668    }
669   
670    // now ready to start the real direct search
671
672    if(p->qtype.getCode()==QType::SOA || p->qtype.getCode()==QType::ANY) { // this is special
673
674      if(B.getSOA(target,sd,p)) {
675        rr.qname=target;
676        rr.qtype=QType::SOA;
677        rr.content=DNSPacket::serializeSOAData(sd);
678        rr.ttl=sd.ttl;
679        rr.domain_id=sd.domain_id;
680        rr.d_place=DNSResourceRecord::ANSWER;
681        r->addRecord(rr);
682        if(p->qtype.getCode()==QType::SOA) { // we are done
683          goto sendit;
684        }
685      }
686    }
687
688    noSameLevelNS=true;
689
690    if(p->qtype.getCode()!=QType::SOA) { // regular direct lookup
691      B.lookup(QType(QType::ANY), target,p);
692     
693      while(B.get(rr)) {
694        if(rr.qtype.getCode()==QType::SOA) // skip any direct SOA responses as they may be different
695          continue;
696        if(rr.qtype==p->qtype || p->qtype.getCode()==QType::ANY ) {
697          DLOG(L<<"Found a direct answer: "<<rr.content<<endl);
698          found=true;
699          r->addRecord(rr);  // and add
700        }
701        else
702          if(rr.qtype.getCode()==QType::NS)
703            noSameLevelNS=false;
704      }
705     
706      if(p->qtype.getCode()==QType::ANY) {
707        if(d_doFancyRecords) { 
708          int res=findMboxFW(p,r,target);
709          if(res<0)
710            L<<Logger::Error<<"Error finding a mailbox record after an ANY query"<<endl;
711          if(res>0) {
712            DLOG(L<<Logger::Error<<"Frobbed an MX in!"<<endl);
713            found=true;
714          }
715        }
716      }
717      if(found) 
718        goto sendit;
719     
720    }
721   
722    // not found yet, try wildcards (we only try here in case of recursion - we should check before we hand off)
723
724    if(p->d.rd && d_doRecursion && d_doWildcards) { 
725      int res=doWildcardRecords(p,r,target);
726      if(res) { // had a result
727        // FIXME: wildCard may retarget us in the future
728        if(res==1)  // had a straight result
729          goto sendit; 
730        if(res==2)
731          goto retargeted;
732        goto sendit; 
733      }
734    }
735
736    // RECURSION CUT-OUT!
737
738
739    bool weAuth;
740    int zoneId;
741    zoneId=-1;
742   
743    if(p->d.rd && d_doRecursion && arg().mustDo("allow-recursion-override"))
744      weAuth=getAuth(p, &sd, target, &zoneId);
745    else
746      weAuth=false;
747
748
749    if(p->d.rd && d_doRecursion && !weAuth) {
750      if(DP->sendPacket(p)) {
751        delete r;
752        return 0;
753      }
754      else noCache=true;
755    }
756   
757    string::size_type pos;
758   
759    DLOG(L<<"Nothing found so far for '"<<target<<"', do we even have authority over this domain?"<<endl);
760
761    if(zoneId==-1)
762      weAuth=getAuth(p, &sd, target, &zoneId); // TLDAuth perhaps
763
764    if(weAuth) {
765      DLOG(L<<Logger::Warning<<"Soa found: '"<<sd.qname<<"'"<<endl);
766      ;
767    }
768    if(!weAuth) {
769      if(p->d.rd || target==p->qdomain) { // only servfail if we didn't follow a CNAME
770        if(d_logDNSDetails)
771          L<<Logger::Warning<<"Not authoritative for '"<< target<<"', sending servfail to "<<
772            p->getRemote()<< (p->d.rd ? " (recursion was desired)" : "") <<endl;
773
774        r->setA(false);
775        if(arg().mustDo("send-root-referral")) {
776          addRootReferral(r);
777        }
778        else {
779          r->setRcode(RCode::ServFail);  // 'sorry' - this is where we might send out a root referral
780        }
781      }
782                                       
783      S.ringAccount("unauth-queries",p->qdomain+"/"+p->qtype.getName());
784      S.ringAccount("remotes-unauth",p->getRemote());
785    }
786    else {
787      DLOG(L<<Logger::Warning<<"We ARE authoritative for a subdomain of '"<<target<<"' ("<<sd.qname<<"), perhaps we have a suitable NS record then"<<endl);
788      subdomain=target;
789      found=0;
790      pos=0; 
791     
792      do {
793        if(pos) // skip dot
794          pos++;
795       
796        subdomain=subdomain.substr(pos);
797        if(noSameLevelNS) { // skip first lookup if it is known not to exist
798          noSameLevelNS=false;
799          continue;
800        }
801         
802        if(!Utility::strcasecmp(subdomain.c_str(),sd.qname.c_str())) // about to break out of our zone
803          break; 
804
805        B.lookup("NS", subdomain,p,zoneId);  // start our search at the backend
806       
807        while(B.get(rr)) {
808          if(!found)
809            r->clearRecords(); // we need to start out with an empty slate
810          found=true;
811          rr.d_place=DNSResourceRecord::AUTHORITY; // this for the authority section
812          r->addRecord(rr);
813        }
814        if(found || (!subdomain.empty() && subdomain[0]=='.')) {  // this catches '..'
815          r->setA(false);  // send out an NS referral, which should be unauth
816          break;
817        }
818      }while((pos=subdomain.find("."))!=string::npos);
819     
820      if(!found) {
821        // try wildcards then
822        if(d_doWildcards) { 
823          int res=doWildcardRecords(p,r,target);
824
825          if(res==1)  // had a straight result
826            goto sendit; 
827          if(res==2)
828            goto retargeted;
829        }
830
831        // we have authority but no answer, so we add the SOA for negative caching
832        rr.qname=sd.qname;
833        rr.qtype=QType::SOA;
834        rr.content=DNSPacket::serializeSOAData(sd);
835        rr.ttl=sd.ttl;
836        rr.domain_id=sd.domain_id;
837        rr.d_place=DNSResourceRecord::AUTHORITY;
838        r->addRecord(rr);
839
840
841        // need to send NXDOMAIN if there are 0 records for whatever type for target
842       
843        B.lookup("ANY",target,p);
844        while(B.get(rr))
845          found=true;
846       
847        if(!found) {
848          SOAData sd2;
849          if(B.getSOA(target,sd2,p)) // is there a SOA perhaps? (which may not appear in an ANY query)
850            found=true;
851        }
852
853        if(!found) { 
854          if(d_logDNSDetails)
855            L<<Logger::Notice<<"Authoritative NXDOMAIN to "<< p->getRemote() <<" for '"<<target<<"' ("<<p->qtype.getName()<<")"<<endl;
856
857          r->setRcode(RCode::NXDomain); 
858          S.ringAccount("nxdomain-queries",p->qdomain+"/"+p->qtype.getName());
859        }
860        else {
861          if(d_logDNSDetails)
862            L<<Logger::Notice<<"Authoritative empty NO ERROR to "<< p->getRemote() <<" for '"<<target<<"' ("<<p->qtype.getName()<<"), other types do exist"<<endl;
863          S.ringAccount("noerror-queries",p->qdomain+"/"+p->qtype.getName());
864        }
865      }
866    }
867   
868    // whatever we've built so far, do additional processing
869   
870  sendit:;
871    if(doAdditionalProcessingAndDropAA(p,r)<0)
872      return 0;
873   
874    r->wrapup(); // needed for inserting in cache
875    if(!noCache)
876      PC.insert(p,r); // in the packet cache
877  }
878  catch(DBException &e) {
879    L<<Logger::Error<<"Database module reported condition which prevented lookup - sending out servfail"<<endl;
880    r->setRcode(RCode::ServFail);
881    S.inc("servfail-packets");
882    S.ringAccount("servfail-queries",p->qdomain);
883  }
884  return r; 
885
886}
887
Note: See TracBrowser for help on using the browser.