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

Revision 167, 22.7 KB (checked in by ahu, 10 years ago)

lots of work

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