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

Revision 138, 22.0 KB (checked in by ahu, 10 years ago)

juh

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