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

Revision 147, 22.1 KB (checked in by ahu, 10 years ago)

added

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