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

Revision 539, 24.5 KB (checked in by ahu, 8 years ago)

Fix 'same level NS referral', as exemplified by the new same-level-referrel regression test.
This solves where the zone contains 'france.example.com NS somewhere.com' and a query comes in for 'france.example.com'
This problem was new in 2.9.19, caused by the cname/wildcard patches
(plus removal of an unused file)

  • 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      if(rr.qtype.getCode()!=QType::NS || p->qtype.getCode()==QType::NS)
393        hits++;
394      if(!rfound && rr.qtype.getCode()==QType::CNAME) {
395        found=true;
396        r->addRecord(rr);
397        target=rr.content; // for retargeting
398      }
399      if(shortcut && !found && rr.qtype==p->qtype) {
400        rfound=true;
401        r->addRecord(rr);
402      }
403    }
404    if(hits && !found && !rfound && shortcut ) // we found matching qnames but not a qtype
405      return 2;
406
407    if(rfound)
408      return 1; // ANY lookup found the right answer immediately
409
410    if(found) {
411      if(p->qtype.getCode()==QType::CNAME) // they really wanted a CNAME!
412        return 1;
413      DLOG(L<<"Looping because of a CNAME to "<<target<<endl);
414      found=false;
415    }
416    else 
417      break;
418  }
419
420  // we now have what we really search for ready in 'target'
421  return 0;
422}
423
424/* Semantics:
425   
426- only one backend owns the SOA of a zone
427- only one AXFR per zone at a time - double startTransaction should fail
428- backends need to implement transaction semantics
429
430
431How BindBackend would implement this:
432   startTransaction makes a file
433   feedRecord sends everything to that file
434   commitTransaction moves that file atomically over the regular file, and triggers a reload
435   rollbackTransaction removes the file
436
437
438How PostgreSQLBackend would implement this:
439   startTransaction starts a sql transaction, which also deletes all records
440   feedRecord is an insert statement
441   commitTransaction commits the transaction
442   rollbackTransaction aborts it
443
444How MySQLBackend would implement this:
445   (good question!)
446   
447*/     
448
449int PacketHandler::trySuperMaster(DNSPacket *p)
450{
451  Resolver::res_t nsset;
452  try {
453    Resolver resolver;
454    uint32_t theirserial;
455    int res=resolver.getSoaSerial(p->getRemote(),p->qdomain, &theirserial); 
456    if(res<=0) {
457      L<<Logger::Error<<"Unable to determine SOA serial for "<<p->qdomain<<" at potential supermaster "<<p->getRemote()<<endl;
458      return RCode::ServFail;
459    }
460 
461    resolver.resolve(p->getRemote(),p->qdomain.c_str(), QType::NS);
462
463    nsset=resolver.result();
464  }
465  catch(ResolverException &re) {
466    L<<Logger::Error<<"Error resolving SOA or NS for '"<<p->qdomain<<"' at "<<p->getRemote()<<endl;
467    return RCode::ServFail;
468  }
469
470  string account;
471  DNSBackend *db;
472  if(!B.superMasterBackend(p->getRemote(), p->qdomain, nsset, &account, &db)) {
473   L<<Logger::Error<<"Unable to find backend willing to host "<<p->qdomain<<" for potential supermaster "<<p->getRemote()<<endl;
474    return RCode::Refused;
475  }
476  db->createSlaveDomain(p->getRemote(),p->qdomain,account);
477  Communicator.addSuckRequest(p->qdomain, p->getRemote()); 
478  L<<Logger::Warning<<"Created new slave zone '"<<p->qdomain<<"' from supermaster "<<p->getRemote()<<", queued axfr"<<endl;
479  return RCode::NoError;
480}
481
482int PacketHandler::processNotify(DNSPacket *p)
483{
484  /* now what?
485     was this notification from an approved address?
486     We determine our internal SOA id (via UeberBackend)
487     We determine the SOA at our (known) master
488     if master is higher -> do stuff
489  */
490  if(!arg().mustDo("slave")) {
491    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" but slave support is disabled in the configuration"<<endl;
492    return RCode::NotImp;
493  }
494  DNSBackend *db=0;
495  DomainInfo di;
496  if(!B.getDomainInfo(p->qdomain,di) || !(db=di.backend)) {
497    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" for which we are not authoritative"<<endl;
498    return trySuperMaster(p);
499  }
500   
501  if(!db->isMaster(p->qdomain, p->getRemote())) {
502    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" which is not a master"<<endl;
503    return RCode::Refused;
504  }
505
506  uint32_t theirserial=0;
507
508  /* to quote Rusty Russell - this code is so bad that you can actually hear it suck */
509  /* this is an instant DoS, just spoof notifications from the address of the master and we block  */
510
511  Resolver resolver;
512  int res=resolver.getSoaSerial(p->getRemote(),p->qdomain, &theirserial);
513  if(res<=0) {
514    L<<Logger::Error<<"Unable to determine SOA serial for "<<p->qdomain<<" at "<<p->getRemote()<<endl;
515    return RCode::ServFail;
516  }
517       
518
519  if(theirserial<=di.serial) {
520    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from master "<<p->getRemote()<<", we are up to date: "<<
521      theirserial<<"<="<<di.serial<<endl;
522    return RCode::NoError;
523  }
524  else {
525    L<<Logger::Error<<"Received valid NOTIFY for "<<p->qdomain<<" (id="<<di.id<<") from master "<<p->getRemote()<<": "<<
526      theirserial<<" > "<<di.serial<<endl;
527
528    Communicator.addSuckRequest(p->qdomain, p->getRemote(),true); // priority
529  }
530  return -1; 
531}
532
533
534
535bool validDNSName(const string &name)
536{
537  string::size_type pos, length=name.length();
538  char c;
539  for(pos=0; pos < length; ++pos) {
540    c=name[pos];
541    if(!((c >= 'a' && c <= 'z') ||
542         (c >= 'A' && c <= 'Z') ||
543         (c >= '0' && c <= '9') ||
544         c =='-' || c == '_' || c=='*' || c=='.' || c=='/'))
545      return false;
546  }
547  return true;
548} 
549
550//! Called by the Distributor to ask a question. Returns 0 in case of an error
551DNSPacket *PacketHandler::question(DNSPacket *p)
552{
553  DNSResourceRecord rr;
554  SOAData sd;
555  sd.db=0;
556 
557  string subdomain="";
558  string soa;
559  int retargetcount=0;
560  bool noSameLevelNS;
561
562  DNSPacket *r=0;
563  try {   
564    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);
565
566// XXX FIXME Find out why this isn't working!
567#ifndef WIN32
568    if(p->d.qr) { // QR bit from dns packet (thanks RA from N)
569      L<<Logger::Error<<"Received an answer (non-query) packet from "<<p->getRemote()<<", dropping"<<endl;
570      S.inc("corrupt-packets");
571      return 0;
572    }
573#endif // WIN32
574
575    // XXX FIXME do this in DNSPacket::parse ?
576
577    if(!validDNSName(p->qdomain)) {
578      L<<Logger::Error<<"Received a malformed qdomain from "<<p->getRemote()<<", '"<<p->qdomain<<"': dropping"<<endl;
579      S.inc("corrupt-packets");
580      return 0;
581    }
582    if(p->d.opcode) { // non-zero opcode (again thanks RA!)
583      if(p->d.opcode==Opcode::Update) {
584        if(arg().mustDo("log-failed-updates"))
585          L<<Logger::Notice<<"Received an UPDATE opcode from "<<p->getRemote()<<" for "<<p->qdomain<<", sending NOTIMP"<<endl;
586        r=p->replyPacket(); 
587        r->setRcode(RCode::NotImp); // notimp;
588        return r; 
589      }
590      else if(p->d.opcode==Opcode::Notify) {
591        int res=processNotify(p);
592        if(res>=0) {
593          DNSPacket *r=p->replyPacket();
594          r->setRcode(res);
595          return r;
596        }
597        return 0;
598      }
599     
600      L<<Logger::Error<<"Received an unknown opcode "<<p->d.opcode<<" from "<<p->getRemote()<<" for "<<p->qdomain<<endl;
601
602      r=p->replyPacket(); 
603      r->setRcode(RCode::NotImp); 
604      return r; 
605    }
606   
607    r=p->replyPacket();  // generate an empty reply packet
608
609    if(p->qtype.getCode()==QType::IXFR) {
610      r->setRcode(RCode::NotImp);
611      return r;
612    }
613
614    bool found=false;
615   
616    string target=p->qdomain;
617    bool noCache=false;
618
619    if (doDNSCheckRequest(p, r, target))
620      goto sendit;
621   
622    if(doVersionRequest(p,r,target)) // catch version.bind requests
623      goto sendit;
624
625    if(p->qclass==255) // any class query
626      r->setA(false);
627    else if(p->qclass!=1) // we only know about IN, so we don't find anything
628      goto sendit;
629
630    int mret;
631  retargeted:;
632    if(retargetcount++>10) {
633      L<<Logger::Error<<"Detected wildcard CNAME loop involving '"<<target<<"'"<<endl;
634      r->setRcode(RCode::ServFail);
635      goto sendit;
636    }
637    mret=makeCanonic(p, r, target); // traverse CNAME chain until we have a useful record (may actually give the correct answer!)
638
639    if(mret==2) { // there is some data, but not of the correct type
640      DLOG(L<<"There is some data, but not of the correct type"<<endl);
641      SOAData sd;
642      if(getAuth(p, &sd, target, 0)) {
643        rr.qname=sd.qname;
644        rr.qtype=QType::SOA;
645        rr.content=DNSPacket::serializeSOAData(sd);
646        rr.ttl=sd.ttl;
647        rr.domain_id=sd.domain_id;
648        rr.d_place=DNSResourceRecord::AUTHORITY;
649        r->addRecord(rr);
650      }
651    }
652
653    if(mret > 0) 
654      goto sendit; // this might be the end of it (client requested a CNAME, or we found the answer already)
655   
656
657    if(d_doFancyRecords) { // MBOXFW, URL <- fake records, emulated with MX and A
658      int res=doFancyRecords(p,r,target);
659      if(res) { // had a result
660        if(res<0) // it was an error
661          r->setRcode(RCode::ServFail);
662        goto sendit; 
663      }
664    }
665   
666    // now ready to start the real direct search
667
668    if(p->qtype.getCode()==QType::SOA || p->qtype.getCode()==QType::ANY) { // this is special
669
670      if(B.getSOA(target,sd,p)) {
671        rr.qname=target;
672        rr.qtype=QType::SOA;
673        rr.content=DNSPacket::serializeSOAData(sd);
674        rr.ttl=sd.ttl;
675        rr.domain_id=sd.domain_id;
676        rr.d_place=DNSResourceRecord::ANSWER;
677        r->addRecord(rr);
678        if(p->qtype.getCode()==QType::SOA) { // we are done
679          goto sendit;
680        }
681      }
682    }
683
684    noSameLevelNS=true;
685
686    if(p->qtype.getCode()!=QType::SOA) { // regular direct lookup
687      B.lookup(QType(QType::ANY), target,p);
688     
689      while(B.get(rr)) {
690        if(rr.qtype.getCode()==QType::SOA) // skip any direct SOA responses as they may be different
691          continue;
692        if(rr.qtype==p->qtype || p->qtype.getCode()==QType::ANY ) {
693          DLOG(L<<"Found a direct answer: "<<rr.content<<endl);
694          found=true;
695          r->addRecord(rr);  // and add
696        }
697        else
698          if(rr.qtype.getCode()==QType::NS)
699            noSameLevelNS=false;
700      }
701     
702      if(p->qtype.getCode()==QType::ANY) {
703        if(d_doFancyRecords) { 
704          int res=findMboxFW(p,r,target);
705          if(res<0)
706            L<<Logger::Error<<"Error finding a mailbox record after an ANY query"<<endl;
707          if(res>0) {
708            DLOG(L<<Logger::Error<<"Frobbed an MX in!"<<endl);
709            found=true;
710          }
711        }
712      }
713      if(found) 
714        goto sendit;
715     
716    }
717   
718    // not found yet, try wildcards (we only try here in case of recursion - we should check before we hand off)
719
720    if(p->d.rd && d_doRecursion && d_doWildcards) { 
721      int res=doWildcardRecords(p,r,target);
722      if(res) { // had a result
723        // FIXME: wildCard may retarget us in the future
724        if(res==1)  // had a straight result
725          goto sendit; 
726        if(res==2)
727          goto retargeted;
728        goto sendit; 
729      }
730    }
731
732    // RECURSION CUT-OUT!
733
734
735    bool weAuth;
736    int zoneId;
737    zoneId=-1;
738   
739    if(p->d.rd && d_doRecursion && arg().mustDo("allow-recursion-override"))
740      weAuth=getAuth(p, &sd, target, &zoneId);
741    else
742      weAuth=false;
743
744
745    if(p->d.rd && d_doRecursion && !weAuth) {
746      if(DP->sendPacket(p)) {
747        delete r;
748        return 0;
749      }
750      else noCache=true;
751    }
752   
753    string::size_type pos;
754   
755    DLOG(L<<"Nothing found so far for '"<<target<<"', do we even have authority over this domain?"<<endl);
756
757    if(zoneId==-1)
758      weAuth=getAuth(p, &sd, target, &zoneId); // TLDAuth perhaps
759
760    if(weAuth) {
761      DLOG(L<<Logger::Warning<<"Soa found: "<<soa<<endl);
762      ;
763    }
764    if(!weAuth) {
765      if(p->d.rd || target==p->qdomain) { // only servfail if we didn't follow a CNAME
766        if(d_logDNSDetails)
767          L<<Logger::Warning<<"Not authoritative for '"<< target<<"', sending servfail to "<<
768            p->getRemote()<< (p->d.rd ? " (recursion was desired)" : "") <<endl;
769
770        r->setA(false);
771        if(arg().mustDo("send-root-referral")) {
772          addRootReferral(r);
773        }
774        else {
775          r->setRcode(RCode::ServFail);  // 'sorry' - this is where we might send out a root referral
776        }
777      }
778                                       
779      S.ringAccount("unauth-queries",p->qdomain+"/"+p->qtype.getName());
780      S.ringAccount("remotes-unauth",p->getRemote());
781    }
782    else {
783      DLOG(L<<Logger::Warning<<"We ARE authoritative for a subdomain of '"<<target<<"' ("<<sd.qname<<"), perhaps we have a suitable NS record then"<<endl);
784      subdomain=target;
785      found=0;
786      pos=0; 
787     
788      do {
789        if(pos) // skip dot
790          pos++;
791       
792        subdomain=subdomain.substr(pos);
793        if(noSameLevelNS) { // skip first lookup if it is known not to exist
794          noSameLevelNS=false;
795          continue;
796        }
797         
798        if(!Utility::strcasecmp(subdomain.c_str(),sd.qname.c_str())) // about to break out of our zone
799          break; 
800
801        B.lookup("NS", subdomain,p,zoneId);  // start our search at the backend
802       
803        while(B.get(rr)) {
804          found=true;
805          rr.d_place=DNSResourceRecord::AUTHORITY; // this for the authority section
806          r->addRecord(rr);
807        }
808        if(found || (!subdomain.empty() && subdomain[0]=='.')) {  // this catches '..'
809          r->setA(false);  // send out an NS referral, which should be unauth
810          break;
811        }
812      }while((pos=subdomain.find("."))!=string::npos);
813     
814      if(!found) {
815        // try wildcards then
816        if(d_doWildcards) { 
817          int res=doWildcardRecords(p,r,target);
818
819          if(res==1)  // had a straight result
820            goto sendit; 
821          if(res==2)
822            goto retargeted;
823        }
824
825        // we have authority but no answer, so we add the SOA for negative caching
826        rr.qname=sd.qname;
827        rr.qtype=QType::SOA;
828        rr.content=DNSPacket::serializeSOAData(sd);
829        rr.ttl=sd.ttl;
830        rr.domain_id=sd.domain_id;
831        rr.d_place=DNSResourceRecord::AUTHORITY;
832        r->addRecord(rr);
833
834
835        // need to send NXDOMAIN if there are 0 records for whatever type for target
836       
837        B.lookup("ANY",target,p);
838        while(B.get(rr))
839          found=true;
840       
841        if(!found) {
842          SOAData sd2;
843          if(B.getSOA(target,sd2,p)) // is there a SOA perhaps? (which may not appear in an ANY query)
844            found=true;
845        }
846
847        if(!found) { 
848          if(d_logDNSDetails)
849            L<<Logger::Notice<<"Authoritative NXDOMAIN to "<< p->getRemote() <<" for '"<<target<<"' ("<<p->qtype.getName()<<")"<<endl;
850
851          r->setRcode(RCode::NXDomain); 
852          S.ringAccount("nxdomain-queries",p->qdomain+"/"+p->qtype.getName());
853        }
854        else {
855          if(d_logDNSDetails)
856            L<<Logger::Notice<<"Authoritative empty NO ERROR to "<< p->getRemote() <<" for '"<<target<<"' ("<<p->qtype.getName()<<"), other types do exist"<<endl;
857          S.ringAccount("noerror-queries",p->qdomain+"/"+p->qtype.getName());
858        }
859      }
860    }
861   
862    // whatever we've built so far, do additional processing
863   
864  sendit:;
865    if(doAdditionalProcessingAndDropAA(p,r)<0)
866      return 0;
867   
868    r->wrapup(); // needed for inserting in cache
869    if(!noCache)
870      PC.insert(p,r); // in the packet cache
871  }
872  catch(DBException &e) {
873    L<<Logger::Error<<"Database module reported condition which prevented lookup - sending out servfail"<<endl;
874    r->setRcode(RCode::ServFail);
875    S.inc("servfail-packets");
876    S.ringAccount("servfail-queries",p->qdomain);
877  }
878  return r; 
879
880}
881
Note: See TracBrowser for help on using the browser.