root/trunk/pdns/pdns/slavecommunicator.cc @ 1893

Revision 1893, 8.6 KB (checked in by ahu, 2 years ago)

implement 'pdnssec set-presigned', allowing PowerDNSSEC to serve pre-signed zones. Rather experimental, but does appear to work

Line 
1/*
2    PowerDNS Versatile Database Driven Nameserver
3    Copyright (C) 2002-2011  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 "inflighter.cc"
39
40#include "namespaces.hh"
41
42void CommunicatorClass::addSuckRequest(const string &domain, const string &master, bool priority)
43{
44  Lock l(&d_lock);
45 
46  SuckRequest sr;
47  sr.domain = domain;
48  sr.master = master;
49  pair<UniQueue::iterator, bool>  res;
50  if(priority) {
51    res=d_suckdomains.push_front(sr);
52  }
53  else {
54    res=d_suckdomains.push_back(sr);
55  }
56 
57  if(res.second) {
58  d_suck_sem.post();
59  }
60}
61
62void CommunicatorClass::suck(const string &domain,const string &remote)
63{
64  L<<Logger::Error<<"Initiating transfer of '"<<domain<<"' from remote '"<<remote<<"'"<<endl;
65  uint32_t domain_id;
66  PacketHandler P;
67
68  DomainInfo di;
69  di.backend=0;
70  bool first=true;   
71  try {
72    Resolver resolver;
73    resolver.axfr(remote, domain.c_str());
74
75    UeberBackend *B=dynamic_cast<UeberBackend *>(P.getBackend());
76    NSEC3PARAMRecordContent ns3pr;
77    bool narrow;
78    DNSSECKeeper dk;
79    bool dnssecZone = false;
80    bool haveNSEC3=false;
81    if(dk.isSecuredZone(domain)) {
82      dnssecZone=true;
83      haveNSEC3=dk.getNSEC3PARAM(domain, &ns3pr, &narrow);
84      string hashed;
85      if(!haveNSEC3) 
86        cerr<<"Adding NSEC ordering information"<<endl;
87      else if(!narrow)
88        cerr<<"Adding NSEC3 hashed ordering information for '"<<domain<<"'"<<endl;
89      else 
90        cerr<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl;
91    }
92
93    if(!B->getDomainInfo(domain, di) || !di.backend) {
94      L<<Logger::Error<<"Can't determine backend for domain '"<<domain<<"'"<<endl;
95      return;
96    }
97    domain_id=di.id;
98
99    Resolver::res_t recs;
100    set<string> nsset, qnames;
101    while(resolver.axfrChunk(recs)) {
102      if(first) {
103        L<<Logger::Error<<"AXFR started for '"<<domain<<"', transaction started"<<endl;
104        di.backend->startTransaction(domain, domain_id);
105        first=false;
106      }
107     
108      for(Resolver::res_t::iterator i=recs.begin();i!=recs.end();++i) {
109        if(!endsOn(i->qname, domain)) { 
110          L<<Logger::Error<<"Remote "<<remote<<" tried to sneak in out-of-zone data '"<<i->qname<<"' during AXFR of zone '"<<domain<<"', ignoring"<<endl;
111          continue;
112        }
113        if(dnssecZone) {
114          if(i->qtype.getCode() == QType::NS && !pdns_iequals(i->qname, domain)) 
115            nsset.insert(i->qname);
116          qnames.insert(i->qname);
117        } 
118        i->domain_id=domain_id;
119        if(i->qtype.getCode()>=1024)
120          throw DBException("Database can't store unknown record type "+lexical_cast<string>(i->qtype.getCode()-1024));
121
122        di.backend->feedRecord(*i);
123      }
124    }
125    if(dnssecZone) {
126      string hashed;
127      BOOST_FOREACH(const string& qname, qnames)
128      {
129        string shorter(qname);
130        bool auth=true;
131        do {
132          if(nsset.count(shorter)) { 
133            auth=false;
134            break;
135          }
136        }while(chopOff(shorter));
137     
138        if(!haveNSEC3) // NSEC
139          di.backend->updateDNSSECOrderAndAuth(domain_id, domain, qname, auth);
140        else {
141          if(!narrow) {
142            hashed=toLower(toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, qname)));
143            cerr<<"'"<<qname<<"' -> '"<< hashed <<"'"<<endl;
144          }
145          di.backend->updateDNSSECOrderAndAuthAbsolute(domain_id, qname, hashed, auth);
146        }
147      }
148    }
149   
150    di.backend->commitTransaction();
151    di.backend->setFresh(domain_id);
152    L<<Logger::Error<<"AXFR done for '"<<domain<<"', zone committed"<<endl;
153  }
154  catch(DBException &re) {
155    L<<Logger::Error<<"Unable to feed record during incoming AXFR of '"+domain+"': "<<re.reason<<endl;
156    if(di.backend && !first) {
157      L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
158      di.backend->abortTransaction();
159    }
160  }
161  catch(ResolverException &re) {
162    L<<Logger::Error<<"Unable to AXFR zone '"+domain+"' from remote '"<<remote<<"': "<<re.reason<<endl;
163    if(di.backend && !first) {
164      L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
165      di.backend->abortTransaction();
166    }
167  }
168}
169struct QueryInfo
170  {
171    struct timeval query_ttd;
172    uint16_t id;
173  };
174
175struct SlaveSenderReceiver
176{
177  typedef pair<string, uint16_t> Identifier;
178  typedef uint32_t Answer;
179 
180  map<uint32_t, uint32_t> d_serials;
181 
182  SlaveSenderReceiver()
183  {
184    d_resolver.makeUDPSocket();
185  }
186 
187  void deliverTimeout(const Identifier& i)
188  {}
189 
190  Identifier send(DomainInfo& di)
191  {
192    random_shuffle(di.masters.begin(), di.masters.end());
193    return make_pair(di.zone, d_resolver.sendResolve(*di.masters.begin(), di.zone.c_str(), QType::SOA));
194  }
195 
196  bool receive(Identifier& id, Answer& a)
197  {
198    if(d_resolver.tryGetSOASerial(&id.first, &a, &id.second)) {
199      return 1;
200    }
201    return 0;
202  }
203 
204  void deliverAnswer(DomainInfo& i, uint32_t serial, unsigned int usec)
205  {
206    d_serials[i.id]=serial;
207  }
208 
209  Resolver d_resolver;
210
211};
212
213void CommunicatorClass::slaveRefresh(PacketHandler *P)
214{
215  UeberBackend *B=dynamic_cast<UeberBackend *>(P->getBackend());
216  vector<DomainInfo> sdomains, rdomains;
217  B->getUnfreshSlaveInfos(&rdomains);
218 
219  {
220    Lock l(&d_lock);
221    typedef UniQueue::index<IDTag>::type domains_by_name_t;
222    domains_by_name_t& nameindex=boost::multi_index::get<IDTag>(d_suckdomains);
223
224   
225    BOOST_FOREACH(DomainInfo& di, rdomains) {
226      SuckRequest sr;
227      sr.domain=di.zone;
228      if(di.masters.empty()) // slave domains w/o masters are ignored
229        continue;
230      // remove unfresh domains already queued for AXFR, no sense polling them again
231      sr.master=*di.masters.begin();
232      if(nameindex.count(sr))
233        continue;
234      sdomains.push_back(di);
235    }
236//    cerr<<rdomains.size() - sdomains.size()<<" prevented"<<endl; 
237  }
238 
239  if(sdomains.empty())
240  {
241    if(d_slaveschanged) {
242      Lock l(&d_lock);
243      L<<Logger::Warning<<"No new unfresh slave domains, "<<d_suckdomains.size()<<" queued for AXFR already"<<endl;
244    }
245    d_slaveschanged = !rdomains.empty();
246    return;
247  }
248  else {
249    Lock l(&d_lock);
250    L<<Logger::Warning<<sdomains.size()<<" slave domain"<<(sdomains.size()>1 ? "s" : "")<<" need"<<
251      (sdomains.size()>1 ? "" : "s")<<
252      " checking, "<<d_suckdomains.size()<<" queued for AXFR"<<endl;
253  }
254     
255  SlaveSenderReceiver ssr;
256  Inflighter<vector<DomainInfo>, SlaveSenderReceiver> ifl(sdomains, ssr);
257 
258  ifl.d_maxInFlight = 200;
259
260  for(;;) {
261    try {
262      ifl.run();
263      break;
264    }
265    catch(std::exception& e) {
266      L<<Logger::Error<<"While checking domain freshness: " << e.what()<<endl;
267    }
268    catch(AhuException &re) { 
269      L<<Logger::Error<<"While checking domain freshness: " << re.reason<<endl;
270    }
271  }
272  L<<Logger::Warning<<"Received serial number updates for "<<ssr.d_serials.size()<<" zones"<<endl;
273
274  BOOST_FOREACH(DomainInfo& di, sdomains) {
275    if(!ssr.d_serials.count(di.id)) 
276      continue;
277    uint32_t theirserial = ssr.d_serials[di.id], ourserial = di.serial;
278   
279    if(theirserial < ourserial) {
280      L<<Logger::Error<<"Domain "<<di.zone<<" more recent than master, our serial " << ourserial << " > their serial "<< theirserial << endl;
281      di.backend->setFresh(di.id);
282    }
283    else if(theirserial == ourserial) {
284      L<<Logger::Warning<<"Domain "<< di.zone<<" is fresh"<<endl;
285      di.backend->setFresh(di.id);
286    }
287    else {
288      L<<Logger::Warning<<"Domain "<< di.zone<<" is stale, master serial "<<theirserial<<", our serial "<< ourserial <<endl;
289      addSuckRequest(di.zone, *di.masters.begin());
290    }
291  }
292} 
293
Note: See TracBrowser for help on using the browser.