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

Revision 518, 24.4 KB (checked in by ahu, 8 years ago)

make ./configure fail if Boost is not installed, and give out helpful tip
add code to send out root referrals for unauth domains

and documentation --send-root-referral

remove some bogus errors on EINTR (there is a remaining bug out there)
prepare MOADNSParser for record types for which only the name is known, and no maker functions
expand and improve EDNS0 parsing code
templatize optString
fix qtype name reporting in pdns_recorsor (regressed during move to MOADNSParser)
make sure outgoing queries are randomised again (regressed etc)
complete AAAA support in MOADNSParser/generator stuff

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