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

Revision 1004, 25.5 KB (checked in by ahu, 6 years ago)

be a bit more precise about database errrors in logging

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