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

Revision 234, 22.7 KB (checked in by ahu, 9 years ago)

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