root/trunk/pdns/pdns/slavecommunicator.cc

Revision 2976, 20.2 KB (checked in by peter, 6 months ago)

catch AhuException? during inbound AXFR

Line 
1/*
2    PowerDNS Versatile Database Driven Nameserver
3    Copyright (C) 2002-2012  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 "dnssecinfra.hh"
21#include "dnsseckeeper.hh"
22#include "base32.hh"
23#include <errno.h>
24#include "communicator.hh"
25#include <set>
26#include <boost/utility.hpp>
27#include "dnsbackend.hh"
28#include "ueberbackend.hh"
29#include "packethandler.hh"
30#include "resolver.hh"
31#include "logger.hh"
32#include "dns.hh"
33#include "arguments.hh"
34#include "session.hh"
35#include "packetcache.hh"
36#include <boost/foreach.hpp>
37#include <boost/lexical_cast.hpp>
38#include "base64.hh"
39#include "inflighter.cc"
40#include "lua-auth.hh"
41#include "namespaces.hh"
42#include "common_startup.hh"
43#include <boost/scoped_ptr.hpp>
44using boost::scoped_ptr;
45
46template<typename T> bool rfc1982LessThan(T a, T b)
47{
48  return ((signed)(a - b)) < 0;
49}
50
51void CommunicatorClass::addSuckRequest(const string &domain, const string &master)
52{
53  Lock l(&d_lock);
54  SuckRequest sr;
55  sr.domain = domain;
56  sr.master = master;
57  pair<UniQueue::iterator, bool>  res;
58
59  res=d_suckdomains.push_back(sr);
60 
61  if(res.second) {
62    d_suck_sem.post();
63  }
64}
65
66void CommunicatorClass::suck(const string &domain,const string &remote)
67{
68  L<<Logger::Error<<"Initiating transfer of '"<<domain<<"' from remote '"<<remote<<"'"<<endl;
69  uint32_t domain_id;
70  PacketHandler P; // fresh UeberBackend
71
72  DomainInfo di;
73  di.backend=0;
74  bool first=true;   
75  try {
76    UeberBackend *B=dynamic_cast<UeberBackend *>(P.getBackend());  // copy of the same UeberBackend
77    NSEC3PARAMRecordContent ns3pr, hadNs3pr;
78    bool narrow, hadNarrow=false;
79    DNSSECKeeper dk; // has its own ueberbackend
80    bool dnssecZone = false;
81    bool haveNSEC3=false;
82    if(dk.isSecuredZone(domain)) {
83      dnssecZone=true;
84      haveNSEC3=dk.getNSEC3PARAM(domain, &ns3pr, &narrow);
85      if (haveNSEC3) {
86        hadNs3pr = ns3pr;
87        hadNarrow = narrow;
88      }
89    }
90
91    const bool hadNSEC3 = haveNSEC3;
92    const bool hadPresigned = dk.isPresigned(domain);
93    const bool hadDnssecZone = dnssecZone;
94
95    if(dnssecZone) {
96      if(!haveNSEC3) 
97        L<<Logger::Info<<"Adding NSEC ordering information"<<endl;
98      else if(!narrow)
99        L<<Logger::Info<<"Adding NSEC3 hashed ordering information for '"<<domain<<"'"<<endl;
100      else 
101        L<<Logger::Info<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl;
102    }   
103
104    if(!B->getDomainInfo(domain, di) || !di.backend) { // di.backend and B are mostly identical
105      L<<Logger::Error<<"Can't determine backend for domain '"<<domain<<"'"<<endl;
106      return;
107    }
108    domain_id=di.id;
109
110    Resolver::res_t recs;
111    set<string> nsset, qnames, dsnames, nonterm, delnonterm;
112   
113    ComboAddress raddr(remote, 53);
114   
115    string tsigkeyname, tsigalgorithm, tsigsecret;
116 
117    if(dk.getTSIGForAccess(domain, remote, &tsigkeyname)) {
118      string tsigsecret64;
119      if(B->getTSIGKey(tsigkeyname, &tsigalgorithm, &tsigsecret64))
120      {
121        B64Decode(tsigsecret64, tsigsecret);
122      }
123      else
124      {
125        L<<Logger::Error<<"TSIG key '"<<tsigkeyname<<"' not found, ignoring 'AXFR-MASTER-TSIG' for domain '"<<domain<<"'"<<endl;
126        tsigkeyname="";
127      }
128    }
129   
130    scoped_ptr<AuthLua> pdl;
131    vector<string> scripts;
132    if(B->getDomainMetadata(domain, "LUA-AXFR-SCRIPT", scripts) && !scripts.empty()) {
133      try {
134        pdl.reset(new AuthLua(scripts[0]));
135        L<<Logger::Info<<"Loaded Lua script '"<<scripts[0]<<"' to edit the incoming AXFR of '"<<domain<<"'"<<endl;
136      }
137      catch(std::exception& e) {
138        L<<Logger::Error<<"Failed to load Lua editing script '"<<scripts[0]<<"' for incoming AXFR of '"<<domain<<"': "<<e.what()<<endl;
139        return;
140      }
141    }
142   
143    vector<string> localaddr;
144    ComboAddress laddr;
145   
146    if(B->getDomainMetadata(domain, "AXFR-SOURCE", localaddr) && !localaddr.empty()) {
147      try {
148        laddr = ComboAddress(localaddr[0]);
149        L<<Logger::Info<<"AXFR source for domain '"<<domain<<"' set to "<<localaddr[0]<<endl;
150      }
151      catch(std::exception& e) {
152        L<<Logger::Error<<"Failed to load AXFR source '"<<localaddr[0]<<"' for incoming AXFR of '"<<domain<<"': "<<e.what()<<endl;
153        return;
154      }
155    } else {
156                  laddr.sin4.sin_family = 0;
157    }
158
159    AXFRRetriever retriever(raddr, domain.c_str(), tsigkeyname, tsigalgorithm, tsigsecret,
160                (laddr.sin4.sin_family == 0) ? NULL : &laddr);
161
162    bool gotPresigned = false;
163    bool gotNSEC3 = false;
164    bool gotOptOutFlag = false;
165    unsigned int soa_serial = 0;
166    while(retriever.getChunk(recs)) {
167      if(first) {
168        L<<Logger::Error<<"AXFR started for '"<<domain<<"', transaction started"<<endl;
169        di.backend->startTransaction(domain, domain_id);
170        first=false;
171      }
172     
173      for(Resolver::res_t::iterator i=recs.begin();i!=recs.end();++i) {
174        if(i->qtype.getCode() == QType::OPT || i->qtype.getCode() == QType::TSIG) // ignore EDNS0 & TSIG
175          continue;
176         
177        if(i->qtype.getCode() == QType::SOA) {
178          if(soa_serial != 0)
179            continue; //skip the last SOA
180          SOAData sd;
181          fillSOAData(i->content,sd);
182          soa_serial = sd.serial;
183        }
184         
185        // we generate NSEC, NSEC3, NSEC3PARAM (sorry Olafur) on the fly, this could only confuse things
186        if (i->qtype.getCode() == QType::NSEC3PARAM) {
187          ns3pr = NSEC3PARAMRecordContent(i->content);
188          narrow = false;
189          dnssecZone = haveNSEC3 = gotPresigned = gotNSEC3 = true;
190          continue;
191        } else if (i->qtype.getCode() == QType::NSEC3) {
192          dnssecZone = gotPresigned = true;
193          gotOptOutFlag = NSEC3RecordContent(i->content).d_flags & 1;
194          continue;
195        } else if (i->qtype.getCode() == QType::NSEC) {
196          dnssecZone = gotPresigned = true;
197          continue;
198        }
199
200        if(!endsOn(i->qname, domain)) { 
201          L<<Logger::Error<<"Remote "<<remote<<" tried to sneak in out-of-zone data '"<<i->qname<<"'|"<<i->qtype.getName()<<" during AXFR of zone '"<<domain<<"', ignoring"<<endl;
202          continue;
203        }
204       
205        i->domain_id=domain_id;
206        if (i->qtype.getCode() == QType::SRV)
207          i->content = stripDot(i->content);
208#if 0
209        if(i->qtype.getCode()>=60000)
210          throw DBException("Database can't store unknown record type "+lexical_cast<string>(i->qtype.getCode()-1024));
211#endif
212        vector<DNSResourceRecord> out;
213        if(pdl && pdl->axfrfilter(raddr, domain, *i, out)) {
214          BOOST_FOREACH(const DNSResourceRecord& rr, out) {
215            di.backend->feedRecord(rr);
216            if(rr.qtype.getCode() == QType::NS && !pdns_iequals(rr.qname, domain)) 
217              nsset.insert(rr.qname);
218            if(rr.qtype.getCode() != QType::RRSIG) // this excludes us hashing RRSIGs for NSEC(3)
219              qnames.insert(rr.qname);
220            if(i->qtype.getCode() == QType::DS)
221              dsnames.insert(i->qname);
222          }
223        }
224        else {
225          di.backend->feedRecord(*i);
226          if(i->qtype.getCode() == QType::NS && !pdns_iequals(i->qname, domain)) 
227            nsset.insert(i->qname);
228          if(i->qtype.getCode() != QType::RRSIG) // this excludes us hashing RRSIGs for NSEC(3)
229            qnames.insert(i->qname);
230          if(i->qtype.getCode() == QType::DS)
231           dsnames.insert(i->qname);
232        }
233      }
234    }
235
236    if (hadPresigned && !gotNSEC3)
237    {
238      // we only had NSEC3 because we were a presigned zone...
239      haveNSEC3 = false;
240    }
241
242    bool doent=true;
243    bool realrr=true;
244    string hashed;
245
246    uint32_t maxent = ::arg().asNum("max-ent-entries");
247
248    dononterm:;
249    BOOST_FOREACH(const string& qname, qnames)
250    {
251      bool auth=true;
252      string shorter(qname);
253
254      if(realrr) {
255        do {
256          if(nsset.count(shorter)) {
257            auth=false;
258            break;
259          }
260        }while(chopOff(shorter));
261      }
262
263      if(dnssecZone && haveNSEC3)
264      {
265        if(!narrow) { 
266          hashed=toLower(toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, qname)));
267          di.backend->updateDNSSECOrderAndAuthAbsolute(domain_id, qname, hashed, auth);
268        }
269        else
270          di.backend->nullifyDNSSECOrderNameAndUpdateAuth(domain_id, qname, auth);
271        if(realrr)
272        {
273          if (dsnames.count(qname))
274            di.backend->setDNSSECAuthOnDsRecord(domain_id, qname);
275          if (!auth || nsset.count(qname)) {
276            di.backend->nullifyDNSSECOrderNameAndAuth(domain_id, qname, "NS");
277            di.backend->nullifyDNSSECOrderNameAndAuth(domain_id, qname, "A");
278            di.backend->nullifyDNSSECOrderNameAndAuth(domain_id, qname, "AAAA");
279          }
280        }
281      }
282      else // NSEC
283      {
284        if(realrr)
285        {
286          di.backend->updateDNSSECOrderAndAuth(domain_id, domain, qname, auth);
287          if (dsnames.count(qname))
288            di.backend->setDNSSECAuthOnDsRecord(domain_id, qname);
289          if (!auth || nsset.count(qname)) {
290            di.backend->nullifyDNSSECOrderNameAndAuth(domain_id, qname, "A");
291            di.backend->nullifyDNSSECOrderNameAndAuth(domain_id, qname, "AAAA");
292          }
293        }
294      }
295
296      if(auth && realrr && doent)
297      {
298        shorter=qname;
299        while(!pdns_iequals(shorter, domain) && chopOff(shorter))
300        {
301          if(!qnames.count(shorter) && !nonterm.count(shorter))
302          {
303            if(!(maxent))
304            {
305              L<<Logger::Error<<"AXFR zone "<<domain<<" has too many empty non terminals."<<endl;
306              nonterm.empty();
307              doent=false;
308              break;
309            }
310            nonterm.insert(shorter);
311            --maxent;
312          }
313        }
314      }
315    }
316
317    if(!nonterm.empty() && realrr && doent)
318    {
319      if(di.backend->updateEmptyNonTerminals(domain_id, domain, nonterm, delnonterm, false))
320      {
321        realrr=false;
322        qnames=nonterm;
323        goto dononterm;
324      }
325    }
326
327    // now we also need to update the presigned flag and NSEC3PARAM
328    // for the zone
329    if (gotPresigned) {
330      if (!hadDnssecZone && !hadPresigned) {
331        // zone is now presigned
332        dk.setPresigned(domain);
333      }
334
335      if (hadPresigned || !hadDnssecZone)
336      {
337        // this is a presigned zone, update NSEC3PARAM
338        if (gotNSEC3) {
339          ns3pr.d_flags = gotOptOutFlag ? 1 : 0;
340         // only update if there was a change
341          if (!hadNSEC3 || (narrow != hadNarrow) ||
342              (ns3pr.d_algorithm != hadNs3pr.d_algorithm) ||
343              (ns3pr.d_flags != hadNs3pr.d_flags) ||
344              (ns3pr.d_iterations != hadNs3pr.d_iterations) ||
345              (ns3pr.d_salt != hadNs3pr.d_salt)) {
346            dk.setNSEC3PARAM(domain, ns3pr, narrow);
347          }
348        } else if (hadNSEC3) {
349          dk.unsetNSEC3PARAM(domain);
350        }
351      }
352    } else if (hadPresigned) {
353      // zone is no longer presigned
354      dk.unsetPresigned(domain);
355      dk.unsetNSEC3PARAM(domain);
356    }
357
358    di.backend->commitTransaction();
359    di.backend->setFresh(domain_id);
360    PC.purge(domain+"$");
361
362
363    L<<Logger::Error<<"AXFR done for '"<<domain<<"', zone committed with serial number "<<soa_serial<<endl;
364    if(::arg().mustDo("slave-renotify"))
365      notifyDomain(domain);
366  }
367  catch(DBException &re) {
368    L<<Logger::Error<<"Unable to feed record during incoming AXFR of '"+domain+"': "<<re.reason<<endl;
369    if(di.backend && !first) {
370      L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
371      di.backend->abortTransaction();
372    }
373  }
374  catch(MOADNSException &re) {
375    L<<Logger::Error<<"Unable to parse record during incoming AXFR of '"+domain+"' (MOADNSException): "<<re.what()<<endl;
376    if(di.backend && !first) {
377      L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
378      di.backend->abortTransaction();
379    }
380  }
381  catch(std::exception &re) {
382    L<<Logger::Error<<"Unable to parse record during incoming AXFR of '"+domain+"' (std::exception): "<<re.what()<<endl;
383    if(di.backend && !first) {
384      L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
385      di.backend->abortTransaction();
386    }
387  }
388  catch(ResolverException &re) {
389    L<<Logger::Error<<"Unable to AXFR zone '"+domain+"' from remote '"<<remote<<"' (resolver): "<<re.reason<<endl;
390    if(di.backend && !first) {
391      L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
392      di.backend->abortTransaction();
393    }
394  }
395  catch(AhuException &ae) {
396    L<<Logger::Error<<"Unable to AXFR zone '"+domain+"' from remote '"<<remote<<"' (AhuException): "<<ae.reason<<endl;
397    if(di.backend && !first) {
398      L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
399      di.backend->abortTransaction();
400    }
401  }
402}
403namespace {
404struct QueryInfo
405{
406  struct timeval query_ttd;
407  uint16_t id;
408};
409
410struct DomainNotificationInfo
411{
412  DomainInfo di;
413  bool dnssecOk;
414  string tsigkeyname, tsigalgname, tsigsecret;
415};
416}
417
418
419struct SlaveSenderReceiver
420{
421  typedef pair<string, uint16_t> Identifier;
422 
423  struct Answer {
424    uint32_t theirSerial;
425    uint32_t theirInception;
426    uint32_t theirExpire;
427  };
428 
429  map<uint32_t, Answer> d_freshness;
430 
431  SlaveSenderReceiver()
432  {
433  }
434 
435  void deliverTimeout(const Identifier& i)
436  {
437  }
438 
439  Identifier send(DomainNotificationInfo& dni)
440  {
441    random_shuffle(dni.di.masters.begin(), dni.di.masters.end());
442    try {
443      ComboAddress remote(*dni.di.masters.begin());
444      return make_pair(dni.di.zone, 
445        d_resolver.sendResolve(ComboAddress(*dni.di.masters.begin(), 53), 
446          dni.di.zone.c_str(), 
447          QType::SOA, 
448          dni.dnssecOk, dni.tsigkeyname, dni.tsigalgname, dni.tsigsecret)
449      );
450    }
451    catch(AhuException& e) {
452      throw runtime_error("While attempting to query freshness of '"+dni.di.zone+"': "+e.reason);
453    }
454  }
455 
456  bool receive(Identifier& id, Answer& a)
457  {
458    if(d_resolver.tryGetSOASerial(&id.first, &a.theirSerial, &a.theirInception, &a.theirExpire, &id.second)) {
459      return 1;
460    }
461    return 0;
462  }
463 
464  void deliverAnswer(DomainNotificationInfo& dni, const Answer& a, unsigned int usec)
465  {
466    d_freshness[dni.di.id]=a;
467  }
468 
469  Resolver d_resolver;
470};
471
472void CommunicatorClass::addSlaveCheckRequest(const DomainInfo& di, const ComboAddress& remote)
473{
474  Lock l(&d_lock);
475  DomainInfo ours = di;
476  ours.backend = 0;
477  d_tocheck.insert(ours);
478  d_any_sem.post(); // kick the loop!
479}
480
481void CommunicatorClass::addTrySuperMasterRequest(DNSPacket *p)
482{
483  Lock l(&d_lock);
484  DNSPacket ours = *p;
485  d_potentialsupermasters.push_back(ours);
486  d_any_sem.post(); // kick the loop!
487}
488
489void CommunicatorClass::slaveRefresh(PacketHandler *P)
490{
491  UeberBackend *B=dynamic_cast<UeberBackend *>(P->getBackend());
492  vector<DomainInfo> rdomains;
493  vector<DomainNotificationInfo> sdomains; // the bool is for 'presigned'
494  vector<DNSPacket> trysuperdomains;
495 
496  {
497    Lock l(&d_lock);
498    rdomains.insert(rdomains.end(), d_tocheck.begin(), d_tocheck.end());
499    d_tocheck.clear();
500    trysuperdomains.insert(trysuperdomains.end(), d_potentialsupermasters.begin(), d_potentialsupermasters.end());
501    d_potentialsupermasters.clear();
502  }
503 
504  BOOST_FOREACH(DNSPacket& dp, trysuperdomains) {
505    int res;
506    res=P->trySuperMasterSynchronous(&dp);
507    if(res>=0) {
508      DNSPacket *r=dp.replyPacket();
509      r->setRcode(res);
510      r->setOpcode(Opcode::Notify);
511      N->send(r);
512      delete r;
513    }
514  }
515
516  if(rdomains.empty()) // if we have priority domains, check them first
517    B->getUnfreshSlaveInfos(&rdomains);
518   
519  DNSSECKeeper dk(B); // NOW HEAR THIS! This DK uses our B backend, so no interleaved access!
520  {
521    Lock l(&d_lock);
522    domains_by_name_t& nameindex=boost::multi_index::get<IDTag>(d_suckdomains);
523
524    BOOST_FOREACH(DomainInfo& di, rdomains) {
525      SuckRequest sr;
526      sr.domain=di.zone;
527      if(di.masters.empty()) // slave domains w/o masters are ignored
528        continue;
529      // remove unfresh domains already queued for AXFR, no sense polling them again
530      sr.master=*di.masters.begin();
531      if(nameindex.count(sr)) {
532        continue;
533      }
534      DomainNotificationInfo dni;
535      dni.di=di;
536      dni.dnssecOk = dk.isPresigned(di.zone);
537     
538      if(dk.getTSIGForAccess(di.zone, sr.master, &dni.tsigkeyname)) {
539        string secret64;
540        B->getTSIGKey(dni.tsigkeyname, &dni.tsigalgname, &secret64);
541        B64Decode(secret64, dni.tsigsecret);
542      }
543      sdomains.push_back(dni);
544    }
545  }
546 
547  if(sdomains.empty())
548  {
549    if(d_slaveschanged) {
550      Lock l(&d_lock);
551      L<<Logger::Warning<<"No new unfresh slave domains, "<<d_suckdomains.size()<<" queued for AXFR already"<<endl;
552    }
553    d_slaveschanged = !rdomains.empty();
554    return;
555  }
556  else {
557    Lock l(&d_lock);
558    L<<Logger::Warning<<sdomains.size()<<" slave domain"<<(sdomains.size()>1 ? "s" : "")<<" need"<<
559      (sdomains.size()>1 ? "" : "s")<<
560      " checking, "<<d_suckdomains.size()<<" queued for AXFR"<<endl;
561  }
562     
563  SlaveSenderReceiver ssr;
564 
565  Inflighter<vector<DomainNotificationInfo>, SlaveSenderReceiver> ifl(sdomains, ssr);
566 
567  ifl.d_maxInFlight = 200;
568
569  for(;;) {
570    try {
571      ifl.run();
572      break;
573    }
574    catch(std::exception& e) {
575      L<<Logger::Error<<"While checking domain freshness: " << e.what()<<endl;
576    }
577    catch(AhuException &re) { 
578      L<<Logger::Error<<"While checking domain freshness: " << re.reason<<endl;
579    }
580  }
581  L<<Logger::Warning<<"Received serial number updates for "<<ssr.d_freshness.size()<<" zones, had "<<ifl.getTimeouts()<<" timeouts"<<endl;
582
583  typedef DomainNotificationInfo val_t;
584  BOOST_FOREACH(val_t& val, sdomains) {
585    DomainInfo& di(val.di);
586    // might've come from the packethandler
587    if(!di.backend && !B->getDomainInfo(di.zone, di)) {
588        L<<Logger::Warning<<"Ignore domain "<< di.zone<<" since it has been removed from our backend"<<endl;
589        continue;
590    }
591     
592    if(!ssr.d_freshness.count(di.id)) 
593      continue;
594    uint32_t theirserial = ssr.d_freshness[di.id].theirSerial, ourserial = di.serial;
595   
596    if(rfc1982LessThan(theirserial, ourserial)) {
597      L<<Logger::Error<<"Domain '"<<di.zone<<"' more recent than master, our serial " << ourserial << " > their serial "<< theirserial << endl;
598      di.backend->setFresh(di.id);
599    }
600    else if(theirserial == ourserial) {
601      if(!dk.isPresigned(di.zone)) {
602        L<<Logger::Warning<<"Domain '"<< di.zone<<"' is fresh (not presigned, no RRSIG check)"<<endl;
603        di.backend->setFresh(di.id);
604      }
605      else {
606        B->lookup(QType(QType::RRSIG), di.zone); // can't use DK before we are done with this lookup!
607        DNSResourceRecord rr;
608        uint32_t maxExpire=0, maxInception=0;
609        while(B->get(rr)) {
610          RRSIGRecordContent rrc(rr.content);
611          if(rrc.d_type == QType::SOA) {
612            maxInception = std::max(maxInception, rrc.d_siginception);
613            maxExpire = std::max(maxExpire, rrc.d_sigexpire);
614          }
615        }
616        if(maxInception == ssr.d_freshness[di.id].theirInception && maxExpire == ssr.d_freshness[di.id].theirExpire) {
617          L<<Logger::Warning<<"Domain '"<< di.zone<<"' is fresh and apex RRSIGs match"<<endl;
618          di.backend->setFresh(di.id);
619        }
620        else {
621          L<<Logger::Warning<<"Domain '"<< di.zone<<"' is fresh, but RRSIGS differ, so DNSSEC stale"<<endl;
622          addSuckRequest(di.zone, *di.masters.begin());
623        }
624      }
625    }
626    else {
627      L<<Logger::Warning<<"Domain '"<< di.zone<<"' is stale, master serial "<<theirserial<<", our serial "<< ourserial <<endl;
628      addSuckRequest(di.zone, *di.masters.begin());
629    }
630  }
631} 
632
633// stub for PowerDNSLua linking
634int directResolve(const std::string& qname, const QType& qtype, int qclass, vector<DNSResourceRecord>& ret)
635{
636  return -1;
637}
638
639
Note: See TracBrowser for help on using the browser.