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

Revision 1472, 28.6 KB (checked in by ahu, 3 years ago)

and the final bit of whitespace/tab cleanup

  • 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-2009  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 version 2 as
7    published by the Free Software Foundation
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17*/
18#include "packetcache.hh"
19#include "utility.hh"
20#include <string>
21#include <sys/types.h>
22#include <boost/algorithm/string.hpp>
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
37/*
38#undef DLOG
39#define DLOG(x) x
40*/
41
42extern StatBag S;
43extern PacketCache PC; 
44extern CommunicatorClass Communicator;
45extern DNSProxy *DP;
46
47int PacketHandler::s_count;
48extern string s_programname;
49
50PacketHandler::PacketHandler():B(s_programname)
51{
52  s_count++;
53  d_doFancyRecords = (::arg()["fancy-records"]!="no");
54  d_doWildcards = (::arg()["wildcards"]!="no");
55  d_doCNAME = (::arg()["skip-cname"]=="no");
56  d_doRecursion= ::arg().mustDo("recursor");
57  d_logDNSDetails= ::arg().mustDo("log-dns-details");
58  d_doIPv6AdditionalProcessing = ::arg().mustDo("do-ipv6-additional-processing");
59}
60
61DNSBackend *PacketHandler::getBackend()
62{
63  return &B;
64}
65
66PacketHandler::~PacketHandler()
67{
68  --s_count;
69  DLOG(L<<Logger::Error<<"PacketHandler destructor called - "<<s_count<<" left"<<endl);
70}
71
72void PacketHandler::addRootReferral(DNSPacket* r)
73{ 
74  // nobody reads what we output, but it appears to be the magic that shuts some nameservers up
75  static const char*ips[]={"198.41.0.4", "192.228.79.201", "192.33.4.12", "128.8.10.90", "192.203.230.10", "192.5.5.241", "192.112.36.4", "128.63.2.53", 
76                     "192.36.148.17","192.58.128.30", "193.0.14.129", "198.32.64.12", "202.12.27.33"};
77  static char templ[40];
78  strncpy(templ,"a.root-servers.net", sizeof(templ) - 1);
79
80  // add . NS records
81  DNSResourceRecord rr;
82  rr.qtype=QType::NS;
83  rr.ttl=518400;
84  rr.d_place=DNSResourceRecord::AUTHORITY;
85 
86  for(char c='a';c<='m';++c) {
87    *templ=c;
88    rr.content=templ;
89    r->addRecord(rr);
90  }
91
92  if(pdns_iequals(::arg()["send-root-referral"], "lean"))
93     return;
94
95  // add the additional stuff
96 
97  rr.ttl=3600000;
98  rr.qtype=QType::A;
99  rr.d_place=DNSResourceRecord::ADDITIONAL;
100
101  for(char c='a';c<='m';++c) {
102    *templ=c;
103    rr.qname=templ;
104    rr.content=ips[c-'a'];
105    r->addRecord(rr);
106  }
107}
108
109int PacketHandler::findMboxFW(DNSPacket *p, DNSPacket *r, string &target)
110{
111  DNSResourceRecord rr;
112  bool wedoforward=false;
113
114  SOAData sd;
115  int zoneId;
116  if(!getAuth(p, &sd, target, &zoneId))
117    return false;
118
119  B.lookup("MBOXFW",string("%@")+target,p, zoneId);
120     
121  while(B.get(rr))
122    wedoforward=true;
123
124  if(wedoforward) {
125    r->clearRecords();
126    rr.content=::arg()["smtpredirector"];
127    rr.priority=25;
128    rr.ttl=7200;
129    rr.qtype=QType::MX;
130    rr.qname=target;
131   
132    r->addRecord(rr);
133  }
134
135  return wedoforward;
136}
137
138int PacketHandler::findUrl(DNSPacket *p, DNSPacket *r, string &target)
139{
140  DNSResourceRecord rr;
141
142  bool found=false;
143     
144  B.lookup("URL",target,p); // search for a URL before we search for an A
145       
146  while(B.get(rr)) {
147    if(!found) 
148      r->clearRecords();
149    found=true;
150    DLOG(L << "Found a URL!" << endl);
151    rr.content=::arg()["urlredirector"];
152    rr.qtype=QType::A; 
153    rr.qname=target;
154         
155    r->addRecord(rr);
156  } 
157
158  if(found) 
159    return 1;
160
161  // now try CURL
162 
163  B.lookup("CURL",target,p); // search for a URL before we search for an A
164     
165  while(B.get(rr)) {
166    if(!found) 
167      r->clearRecords();
168    found=true;
169    DLOG(L << "Found a CURL!" << endl);
170    rr.content=::arg()["urlredirector"];
171    rr.qtype=1; // A
172    rr.qname=target;
173    rr.ttl=300;
174    r->addRecord(rr);
175  } 
176
177  if(found)
178    return found;
179  return 0;
180}
181
182/** Returns 0 if nothing was found, -1 if an error occured or 1 if the search
183    was satisfied */
184int PacketHandler::doFancyRecords(DNSPacket *p, DNSPacket *r, string &target)
185{
186  DNSResourceRecord rr;
187  if(p->qtype.getCode()==QType::MX)  // check if this domain has smtp service from us
188    return findMboxFW(p,r,target);
189 
190  if(p->qtype.getCode()==QType::A)   // search for a URL record for an A
191    return findUrl(p,r,target);
192  return 0;
193}
194
195/** This catches version requests. Returns 1 if it was handled, 0 if it wasn't */
196int PacketHandler::doVersionRequest(DNSPacket *p, DNSPacket *r, string &target)
197{
198  DNSResourceRecord rr;
199 
200  // modes: anonymous, powerdns only, full, spoofed
201  const string mode=::arg()["version-string"];
202  if(p->qtype.getCode()==QType::TXT && target=="version.bind") {// TXT
203    if(mode.empty() || mode=="full") 
204      rr.content="Served by POWERDNS "VERSION" $Id$";
205    else if(mode=="anonymous") {
206      r->setRcode(RCode::ServFail);
207      return 1;
208    }
209    else if(mode=="powerdns")
210      rr.content="Served by PowerDNS - http://www.powerdns.com";
211    else 
212      rr.content=mode;
213
214    rr.ttl=5;
215    rr.qname=target;
216    rr.qtype=QType::TXT; // TXT
217    r->addRecord(rr);
218   
219    return 1;
220  }
221  return 0;
222}
223
224/** Determines if we are authoritative for a zone, and at what level */
225bool PacketHandler::getAuth(DNSPacket *p, SOAData *sd, const string &target, int *zoneId)
226{
227  string subdomain(target);
228  do {
229    if( B.getSOA( subdomain, *sd, p ) ) {
230      sd->qname = subdomain;
231      if(zoneId)
232        *zoneId = sd->domain_id;
233      return true;
234    }
235  }
236  while( chopOff( subdomain ) );   // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> ''
237  return false;
238}
239
240/** returns 1 in case of a straight match, 2 in case of a wildcard CNAME (groan), 0 in case of no hit */
241int PacketHandler::doWildcardRecords(DNSPacket *p, DNSPacket *r, string &target)
242{
243  DNSResourceRecord rr;
244  bool found=false, retargeted=false;
245
246  // try chopping off domains and look for wildcard matches
247
248  // *.pietje.nl IN  A 1.2.3.4
249  // pietje.nl should now NOT match, but www.pietje.nl should
250
251  string subdomain=target;
252  string::size_type pos;
253
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      if(retargeted)
264        continue;
265      found=true;
266      if((p->qtype.getCode()==QType::ANY || rr.qtype==p->qtype) || rr.qtype.getCode()==QType::CNAME) {
267        rr.qname=target;
268
269        if(d_doFancyRecords && p->qtype.getCode()==QType::ANY && (rr.qtype.getCode()==QType::URL || rr.qtype.getCode()==QType::CURL)) {
270          rr.content=::arg()["urlredirector"];
271          rr.qtype=QType::A; 
272        }
273
274        r->addRecord(rr);  // and add
275        if(rr.qtype.getCode()==QType::CNAME) {
276          if(target==rr.content) {
277            L<<Logger::Error<<"Ignoring wildcard CNAME '"<<rr.qname<<"' pointing at itself"<<endl;
278            r->setRcode(RCode::ServFail);
279            continue;
280          }
281         
282          DLOG(L<<Logger::Error<<"Retargeting because of wildcard cname, from "<<target<<" to "<<rr.content<<endl);
283         
284          target=rr.content; // retarget
285          retargeted=true;
286        }
287      }
288      else if(d_doFancyRecords && ::arg().mustDo("wildcard-url") && p->qtype.getCode()==QType::A && rr.qtype.getName()=="URL") {
289        rr.content=::arg()["urlredirector"];
290        rr.qtype=QType::A; 
291        rr.qname=target;
292       
293        r->addRecord(rr);
294      }
295    }
296    if(found) {
297      DLOG(L<<"Wildcard match on '"<<string("*.")+subdomain<<"'"<<", retargeted="<<retargeted<<endl);
298      return retargeted ? 2 : 1;
299    }
300  }
301  DLOG(L<<"Returning no hit for '"<<string("*.")+subdomain<<"'"<<endl);
302  return 0;
303}
304
305/** dangling is declared true if we were unable to resolve everything */
306int PacketHandler::doAdditionalProcessingAndDropAA(DNSPacket *p, DNSPacket *r)
307{
308  DNSResourceRecord rr;
309  SOAData sd;
310
311  if(p->qtype.getCode()!=QType::AXFR) { // this packet needs additional processing
312    vector<DNSResourceRecord *> arrs=r->getAPRecords();
313    if(arrs.empty()) 
314      return 1;
315
316    DLOG(L<<Logger::Warning<<"This packet needs additional processing!"<<endl);
317
318    vector<DNSResourceRecord> crrs;
319
320    for(vector<DNSResourceRecord *>::const_iterator i=arrs.begin();
321        i!=arrs.end();  ++i) 
322      crrs.push_back(**i);
323
324    // we now have a copy, push_back on packet might reallocate!
325
326    for(vector<DNSResourceRecord>::const_iterator i=crrs.begin();
327        i!=crrs.end();
328        ++i) {
329     
330      if(r->d.aa && !i->qname.empty() && i->qtype.getCode()==QType::NS && !B.getSOA(i->qname,sd,p)) { // drop AA in case of non-SOA-level NS answer, except for root referral
331        r->d.aa=false;
332        //      i->d_place=DNSResourceRecord::AUTHORITY; // XXX FIXME
333      }
334
335      QType qtypes[2];
336      qtypes[0]="A"; qtypes[1]="AAAA";
337      for(int n=0 ; n < d_doIPv6AdditionalProcessing + 1; ++n) {
338        B.lookup(qtypes[n],i->content,p); 
339        bool foundOne=false;
340        while(B.get(rr)) {
341          foundOne=true;
342          if(rr.domain_id!=i->domain_id && ::arg()["out-of-zone-additional-processing"]=="no") {
343            DLOG(L<<Logger::Warning<<"Not including out-of-zone additional processing of "<<i->qname<<" ("<<rr.qname<<")"<<endl);
344            continue; // not adding out-of-zone additional data
345          }
346         
347          rr.d_place=DNSResourceRecord::ADDITIONAL;
348          r->addRecord(rr);
349        }
350      }
351    }
352  }
353  return 1;
354}
355
356/* returns 1 if everything is done & ready, 0 if the search should continue, 2 if a 'NO-ERROR' response should be generated */
357int PacketHandler::makeCanonic(DNSPacket *p, DNSPacket *r, string &target)
358{
359  DNSResourceRecord rr;
360
361  bool found=false, rfound=false;
362
363  if(p->qtype.getCode()!=QType::CNAME && !d_doCNAME)
364    return 0;
365
366  // Traverse a CNAME chain if needed
367  for(int numloops=0;;numloops++) {
368    if(numloops==10) {
369      L<<Logger::Error<<"Detected a CNAME loop involving "<<target<<", sending SERVFAIL"<<endl;
370      r->setRcode(2);
371      return 1;
372    }
373
374    B.lookup(QType(QType::ANY),target,p);
375       
376    bool shortcut=p->qtype.getCode()!=QType::SOA && p->qtype.getCode()!=QType::ANY;
377    int hits=0;
378    bool relevantNS=false;
379
380    while(B.get(rr)) {
381      if(rr.qtype.getCode() == QType::NS && p->qtype.getCode() != QType::NS) { // possible retargeting
382        relevantNS=true;
383      }
384      if(rr.qtype.getCode()!=QType::NS || p->qtype.getCode()==QType::NS)
385        hits++;
386      if(!rfound && rr.qtype.getCode()==QType::CNAME) {
387        found=true;
388        r->addRecord(rr);
389        target=rr.content; // for retargeting
390      }
391      if(shortcut && !found && rr.qtype==p->qtype) {
392        rfound=true;
393        r->addRecord(rr);
394      }
395    }
396    if(hits && !relevantNS && !found && !rfound && shortcut ) { // we found matching qnames but not a qtype
397      DLOG(L<<"Found matching qname, but not the qtype"<<endl);
398      return 2;
399    }
400
401    if(rfound)
402      return 1; // ANY lookup found the right answer immediately
403
404    if(found) {
405      if(p->qtype.getCode()==QType::CNAME) // they really wanted a CNAME!
406        return 1;
407      DLOG(L<<"Looping because of a CNAME to "<<target<<endl);
408      found=false;
409    }
410    else 
411      break;
412  }
413
414  // we now have what we really search for ready in 'target'
415  return 0;
416}
417
418/* Semantics:
419   
420- only one backend owns the SOA of a zone
421- only one AXFR per zone at a time - double startTransaction should fail
422- backends need to implement transaction semantics
423
424
425How BindBackend would implement this:
426   startTransaction makes a file
427   feedRecord sends everything to that file
428   commitTransaction moves that file atomically over the regular file, and triggers a reload
429   rollbackTransaction removes the file
430
431
432How PostgreSQLBackend would implement this:
433   startTransaction starts a sql transaction, which also deletes all records
434   feedRecord is an insert statement
435   commitTransaction commits the transaction
436   rollbackTransaction aborts it
437
438How MySQLBackend would implement this:
439   (good question!)
440   
441*/     
442
443int PacketHandler::trySuperMaster(DNSPacket *p)
444{
445  Resolver::res_t nsset;
446  try {
447    Resolver resolver;
448    uint32_t theirserial;
449    resolver.getSoaSerial(p->getRemote(),p->qdomain, &theirserial); 
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 at: "<< p->getRemote() <<": "<<re.reason<<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::processNotify(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  di.serial = 0;
487  if(!B.getDomainInfo(p->qdomain, di) || !(db=di.backend)) {
488    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" for which we are not authoritative"<<endl;
489    return trySuperMaster(p);
490  }
491   
492  string authServer(p->getRemote());
493  if(::arg().contains("trusted-notification-proxy", p->getRemote())) {
494    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from trusted-notification-proxy "<< p->getRemote()<<endl;
495    if(di.masters.empty()) {
496      L<<Logger::Error<<"However, "<<p->qdomain<<" does not have any masters defined"<<endl;
497      return RCode::Refused;
498    }
499
500    authServer = *di.masters.begin();
501
502  }
503  else if(!db->isMaster(p->qdomain, p->getRemote())) {
504    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" which is not a master"<<endl;
505    return RCode::Refused;
506  }
507
508  uint32_t theirserial=0;
509
510  /* to quote Rusty Russell - this code is so bad that you can actually hear it suck */
511  /* this is an instant DoS, just spoof notifications from the address of the master and we block  */
512
513  Resolver resolver;
514  try {
515    resolver.getSoaSerial(authServer, p->qdomain, &theirserial);
516  }
517  catch(ResolverException& re) {
518    L<<Logger::Error<<re.reason<<endl;
519    return RCode::ServFail;
520  }
521
522  if(theirserial<=di.serial) {
523    L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<< authServer <<", we are up to date: "<<
524      theirserial<<"<="<<di.serial<<endl;
525    return RCode::NoError;
526  }
527  else {
528    L<<Logger::Error<<"Received valid NOTIFY for "<<p->qdomain<<" (id="<<di.id<<") from master "<<p->getRemote()<<": "<<
529      theirserial<<" > "<<di.serial<<endl;
530
531    Communicator.addSuckRequest(p->qdomain, authServer, true); // priority
532  }
533  return -1; 
534}
535
536
537
538bool validDNSName(const string &name)
539{
540  string::size_type pos, length=name.length();
541  char c;
542  for(pos=0; pos < length; ++pos) {
543    c=name[pos];
544    if(!((c >= 'a' && c <= 'z') ||
545         (c >= 'A' && c <= 'Z') ||
546         (c >= '0' && c <= '9') ||
547         c =='-' || c == '_' || c=='*' || c=='.' || c=='/' || c=='@'))
548      return false;
549  }
550  return true;
551} 
552
553DNSPacket *PacketHandler::question(DNSPacket *p)
554{
555  bool shouldRecurse=false;
556  DNSPacket *ret=questionOrRecurse(p, &shouldRecurse);
557  if(shouldRecurse) {
558    DP->sendPacket(p);
559  }
560  return ret;
561}
562
563//! Called by the Distributor to ask a question. Returns 0 in case of an error
564DNSPacket *PacketHandler::questionOrRecurse(DNSPacket *p, bool *shouldRecurse)
565{
566  *shouldRecurse=false;
567  DNSResourceRecord rr;
568  SOAData sd;
569  sd.db=0;
570 
571  string subdomain="";
572  string soa;
573  int retargetcount=0;
574  bool noSameLevelNS;
575
576  DNSPacket *r=0;
577  try {   
578    DLOG(L << Logger::Notice<<"Remote "<< p->remote.toString() <<" wants a type " << p->qtype.getName() << " ("<<p->qtype.getCode()<<") about '"<<p->qdomain << "'" << endl);
579
580// XXX FIXME Find out why this isn't working!
581#ifndef WIN32
582    if(p->d.qr) { // QR bit from dns packet (thanks RA from N)
583      L<<Logger::Error<<"Received an answer (non-query) packet from "<<p->getRemote()<<", dropping"<<endl;
584      S.inc("corrupt-packets");
585      return 0;
586    }
587#endif // WIN32
588
589    // XXX FIXME do this in DNSPacket::parse ?
590
591    if(!validDNSName(p->qdomain)) {
592      if(::arg().mustDo("log-dns-details"))
593        L<<Logger::Error<<"Received a malformed qdomain from "<<p->getRemote()<<", '"<<p->qdomain<<"': sending servfail"<<endl;
594      S.inc("corrupt-packets");
595      r=p->replyPacket(); 
596      r->setRcode(RCode::ServFail);
597      return r;
598    }
599    if(p->d.opcode) { // non-zero opcode (again thanks RA!)
600      if(p->d.opcode==Opcode::Update) {
601        if(::arg().mustDo("log-failed-updates"))
602          L<<Logger::Notice<<"Received an UPDATE opcode from "<<p->getRemote()<<" for "<<p->qdomain<<", sending NOTIMP"<<endl;
603        r=p->replyPacket(); 
604        r->setRcode(RCode::NotImp); // notimp;
605        return r; 
606      }
607      else if(p->d.opcode==Opcode::Notify) {
608        int res=processNotify(p);
609        if(res>=0) {
610          DNSPacket *r=p->replyPacket();
611          r->setRcode(res);
612          r->setOpcode(Opcode::Notify);
613          return r;
614        }
615        return 0;
616      }
617     
618      L<<Logger::Error<<"Received an unknown opcode "<<p->d.opcode<<" from "<<p->getRemote()<<" for "<<p->qdomain<<endl;
619
620      r=p->replyPacket(); 
621      r->setRcode(RCode::NotImp); 
622      return r; 
623    }
624
625    DLOG(L<<Logger::Warning<<"Query for '"<<p->qdomain<<"' "<<p->qtype.getName()<<" from "<<p->getRemote()<<endl);
626   
627    r=p->replyPacket();  // generate an empty reply packet
628    if(p->d.rd && d_doRecursion && DP->recurseFor(p))  // make sure we set ra if rd was set, and we'll do it
629      r->d.ra=true;
630
631    if(p->qtype.getCode()==QType::IXFR) {
632      r->setRcode(RCode::NotImp);
633      return r;
634    }
635
636    // please don't query fancy records directly!
637    if(d_doFancyRecords && (p->qtype.getCode()==QType::URL || p->qtype.getCode()==QType::CURL || p->qtype.getCode()==QType::MBOXFW)) {
638      r->setRcode(RCode::ServFail);
639      return r;
640    }
641
642    bool found=false;
643   
644    string target=p->qdomain;
645    bool noCache=false;
646
647    if(doVersionRequest(p,r,target)) // catch version.bind requests
648      goto sendit;
649
650    if(p->qclass==255) // any class query
651      r->setA(false);
652    else if(p->qclass!=1) // we only know about IN, so we don't find anything
653      goto sendit;
654
655    int mret;
656  retargeted:;
657    if(retargetcount++>10) {
658      L<<Logger::Error<<"Detected wildcard CNAME loop involving '"<<target<<"'"<<endl;
659      r->setRcode(RCode::ServFail);
660      goto sendit;
661    }
662    mret=makeCanonic(p, r, target); // traverse CNAME chain until we have a useful record (may actually give the correct answer!)
663    DLOG(L<<Logger::Warning<<"MakeCanonic returned "<<mret<<endl);
664
665    if(mret==2) { // there is some data, but not of the correct type
666      r->clearRecords();
667    }
668    if(d_doFancyRecords) { // MBOXFW, URL <- fake records, emulated with MX and A
669      DLOG(L<<"There is some data, but not of the correct type, checking fancy records"<<endl);
670      int res=doFancyRecords(p,r,target);
671      if(res) { // had a result
672        if(res<0) // it was an error
673          r->setRcode(RCode::ServFail);
674        goto sendit; 
675      }
676    }
677    if(mret == 2) {
678      DLOG(L<<"There is some data, but not of the correct type, adding SOA for NXRECORDSET"<<endl);
679      SOAData sd;
680      if(getAuth(p, &sd, target, 0)) {
681        rr.qname=sd.qname;
682        rr.qtype=QType::SOA;
683        rr.content=serializeSOAData(sd);
684        rr.ttl=sd.ttl;
685        rr.domain_id=sd.domain_id;
686        rr.d_place=DNSResourceRecord::AUTHORITY;
687        r->addRecord(rr);
688        if(mret == 2)
689          goto sendit;
690      }
691    }
692
693    if(mret == 1) 
694      goto sendit; // this might be the end of it (client requested a CNAME, or we found the answer already)
695     
696    // now ready to start the real direct search
697
698    if(p->qtype.getCode()==QType::SOA || p->qtype.getCode()==QType::ANY) { // this is special
699
700      if(B.getSOA(target,sd,p)) {
701        rr.qname=target;
702        rr.qtype=QType::SOA;
703        rr.content=serializeSOAData(sd);
704        rr.ttl=sd.ttl;
705        rr.domain_id=sd.domain_id;
706        rr.d_place=DNSResourceRecord::ANSWER;
707        r->addRecord(rr);
708        if(p->qtype.getCode()==QType::SOA) { // we are done
709          goto sendit;
710        }
711      }
712    }
713    noSameLevelNS=true;
714
715    if(p->qtype.getCode()!=QType::SOA) { // regular direct lookup
716      B.lookup(QType(QType::ANY), target,p);
717     
718      while(B.get(rr)) {
719        if(rr.qtype.getCode()==QType::SOA) // skip any direct SOA responses as they may be different
720          continue;
721        if(rr.qtype==p->qtype || p->qtype.getCode()==QType::ANY ) {
722          DLOG(L<<"Found a direct answer: "<<rr.content<<endl);
723          found=true;
724          if(d_doFancyRecords && p->qtype.getCode()==QType::ANY && (rr.qtype.getCode()==QType::URL || rr.qtype.getCode()==QType::CURL)) {
725            rr.content=::arg()["urlredirector"];
726            rr.qtype=QType::A; 
727            rr.qname=target;
728          }
729
730          r->addRecord(rr);  // and add
731        }
732        else
733          if(rr.qtype.getCode()==QType::NS)
734            noSameLevelNS=false;
735      }
736     
737      if(p->qtype.getCode()==QType::ANY) {
738        if(d_doFancyRecords) { 
739          int res=findMboxFW(p,r,target);
740          if(res<0)
741            L<<Logger::Error<<"Error finding a mailbox record after an ANY query"<<endl;
742          if(res>0) {
743            DLOG(L<<Logger::Error<<"Frobbed an MX in!"<<endl);
744            found=true;
745          }
746        }
747      }
748      if(found) 
749        goto sendit;
750    }
751    else
752      noSameLevelNS = false; // who knows.. this probably closes ticket 224. this code is a mess
753   
754    // not found yet, try wildcards (we only try here in case of recursion - we should check before we hand off)
755
756    if(mret != 2 && p->d.rd && d_doRecursion && d_doWildcards) { 
757      int res=doWildcardRecords(p,r,target);
758      if(res) { // had a result
759        // FIXME: wildCard may retarget us in the future
760        if(res==1)  // had a straight result
761          goto sendit; 
762        if(res==2)
763          goto retargeted;
764        goto sendit; 
765      }
766    }
767
768    // RECURSION CUT-OUT!
769
770    bool weAuth;
771    int zoneId;
772    zoneId=-1;
773   
774    if(p->d.rd && d_doRecursion && ::arg().mustDo("allow-recursion-override"))
775      weAuth=getAuth(p, &sd, target, &zoneId);
776    else
777      weAuth=false;
778
779    if(p->d.rd && d_doRecursion && !weAuth) {
780      if(DP->recurseFor(p)) {
781        *shouldRecurse=true;
782        delete r;
783        return 0;
784      }
785      else noCache=true;
786    }
787   
788
789    string::size_type pos;
790   
791    DLOG(L<<"Nothing found so far for '"<<target<<"', do we even have authority over this domain?"<<endl);
792
793    if(zoneId==-1)
794      weAuth=getAuth(p, &sd, target, &zoneId); // TLDAuth perhaps
795
796    if(weAuth) {
797      DLOG(L<<Logger::Warning<<"Soa found: '"<<sd.qname<<"'"<<endl);
798      ;
799    }
800    if(!weAuth) {
801      DLOG(L<<Logger::Warning<<"We're not authoritative"<<endl);
802      if(p->d.rd || target==p->qdomain) { // only servfail if we didn't follow a CNAME
803        DLOG(L<<Logger::Warning<<"Adding SERVFAIL as we did not followed a CNAME"<<endl);
804        if(d_logDNSDetails)
805          L<<Logger::Warning<<"Not authoritative for '"<< target<<"', sending servfail to "<<
806            p->getRemote()<< (p->d.rd ? " (recursion was desired)" : "") <<endl;
807
808        r->setA(false);
809        if(::arg().mustDo("send-root-referral")) {
810          DLOG(L<<Logger::Warning<<"Adding root-referral"<<endl);
811          addRootReferral(r);
812        }
813        else {
814          DLOG(L<<Logger::Warning<<"Adding SERVFAIL"<<endl);
815          r->setRcode(RCode::ServFail);  // 'sorry'
816        }
817      }
818      else if(!p->d.rd) {
819        if(::arg().mustDo("send-root-referral")) { // addresses ticket 223
820          DLOG(L<<Logger::Warning<<"Adding root-referral"<<endl);
821          addRootReferral(r);
822        }
823        goto sendit;
824      }
825                                       
826      S.ringAccount("unauth-queries",p->qdomain+"/"+p->qtype.getName());
827      S.ringAccount("remotes-unauth",p->getRemote());
828    }
829    else {
830      DLOG(L<<Logger::Warning<<"We ARE authoritative for a subdomain of '"<<target<<"' ("<<sd.qname<<"), perhaps we have a suitable NS record then, no same: "<<noSameLevelNS<<endl);
831      subdomain=target;
832      found=0;
833      pos=0; 
834     
835      do {
836        if(pos) // skip dot
837          pos++;
838       
839        subdomain=subdomain.substr(pos);
840        if(noSameLevelNS) { // skip first lookup if it is known not to exist
841          noSameLevelNS=false;
842          continue;
843        }
844         
845        if(pdns_iequals(subdomain,sd.qname)) // about to break out of our zone
846          break; 
847       
848        B.lookup("NS", subdomain,p,zoneId);  // start our search at the backend
849       
850        while(B.get(rr)) {
851          if(target==p->qdomain && !found) { // don't strip data if we've been retargeted!
852            DLOG(L<<Logger::Warning<<"Clearing records - we've discovered we are auth (see cs 947)"<<endl);
853            r->clearRecords(); // we need to start out with an empty slate
854          }
855          found=true;
856          rr.d_place=DNSResourceRecord::AUTHORITY; // this for the authority section
857          r->addRecord(rr);
858        }
859        if(found || (!subdomain.empty() && subdomain[0]=='.')) {  // this catches '..'
860          r->setA(false);  // send out an NS referral, which should be unauth
861          break;
862        }
863      }while((pos=subdomain.find("."))!=string::npos);
864     
865      if(!found) {
866        // try wildcards then
867        if(d_doWildcards) { 
868          int res=doWildcardRecords(p,r,target);
869
870          if(res==1)  // had a straight result
871            goto sendit; 
872          if(res==2)
873            goto retargeted;
874        }
875
876        // we have authority but no answer, so we add the SOA for negative caching
877        rr.qname=sd.qname;
878        rr.qtype=QType::SOA;
879        rr.content=serializeSOAData(sd);
880        rr.ttl=sd.ttl;
881        rr.domain_id=sd.domain_id;
882        rr.d_place=DNSResourceRecord::AUTHORITY;
883        r->addRecord(rr);
884
885
886        // need to send NXDOMAIN if there are 0 records for whatever type for target
887       
888        B.lookup("ANY",target,p);
889        while(B.get(rr))
890          found=true;
891       
892        if(!found) {
893          SOAData sd2;
894          if(B.getSOA(target,sd2,p)) // is there a SOA perhaps? (which may not appear in an ANY query)
895            found=true;
896        }
897
898        if(!found) { 
899          if(d_logDNSDetails)
900            L<<Logger::Notice<<"Authoritative NXDOMAIN to "<< p->getRemote() <<" for '"<<target<<"' ("<<p->qtype.getName()<<")"<<endl;
901
902          r->setRcode(RCode::NXDomain); 
903          S.ringAccount("nxdomain-queries",p->qdomain+"/"+p->qtype.getName());
904        }
905        else {
906          if(d_logDNSDetails)
907            L<<Logger::Notice<<"Authoritative empty NO ERROR to "<< p->getRemote() <<" for '"<<target<<"' ("<<p->qtype.getName()<<"), other types do exist"<<endl;
908          S.ringAccount("noerror-queries",p->qdomain+"/"+p->qtype.getName());
909        }
910      }
911    }
912   
913    // whatever we've built so far, do additional processing
914   
915  sendit:;
916    if(doAdditionalProcessingAndDropAA(p,r)<0)
917      return 0;
918
919    r->wrapup(); // needed for inserting in cache
920    if(!noCache)
921      noCache = !p->couldBeCached();
922
923    if(!noCache) {
924      PC.insert(p,r); // in the packet cache
925    }
926  }
927  catch(DBException &e) {
928    L<<Logger::Error<<"Database module reported condition which prevented lookup ("+e.reason+") sending out servfail"<<endl;
929    r->setRcode(RCode::ServFail);
930    S.inc("servfail-packets");
931    S.ringAccount("servfail-queries",p->qdomain);
932  }
933  catch(std::exception &e) {
934    L<<Logger::Error<<"Exception building answer packet ("<<e.what()<<") sending out servfail"<<endl;
935    delete r;
936    r=p->replyPacket();  // generate an empty reply packet   
937    r->setRcode(RCode::ServFail);
938    S.inc("servfail-packets");
939    S.ringAccount("servfail-queries",p->qdomain);
940  }
941  return r; 
942
943}
944
Note: See TracBrowser for help on using the browser.