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

Revision 172, 22.9 KB (checked in by ahu, 10 years ago)

fixed 1 crasher bug

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