]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/slavecommunicator.cc
documentation commit
[thirdparty/pdns.git] / pdns / slavecommunicator.cc
CommitLineData
3696224d
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
d7652f3a 3 Copyright (C) 2002-2011 PowerDNS.COM BV
3696224d
BH
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"
d7652f3a
BH
20#include "dnssecinfra.hh"
21#include "dnsseckeeper.hh"
22#include "base32.hh"
3696224d
BH
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>
29b92d6f 38#include "base64.hh"
3696224d
BH
39#include "inflighter.cc"
40
41#include "namespaces.hh"
42
43void CommunicatorClass::addSuckRequest(const string &domain, const string &master, bool priority)
44{
45 Lock l(&d_lock);
46
47 SuckRequest sr;
48 sr.domain = domain;
49 sr.master = master;
dbcb3066 50 pair<UniQueue::iterator, bool> res;
3696224d 51 if(priority) {
dbcb3066
BH
52 res=d_suckdomains.push_front(sr);
53 }
54 else {
55 res=d_suckdomains.push_back(sr);
3696224d 56 }
3696224d 57
dbcb3066 58 if(res.second) {
3696224d 59 d_suck_sem.post();
dbcb3066 60 }
3696224d
BH
61}
62
63void CommunicatorClass::suck(const string &domain,const string &remote)
64{
65 L<<Logger::Error<<"Initiating transfer of '"<<domain<<"' from remote '"<<remote<<"'"<<endl;
66 uint32_t domain_id;
67 PacketHandler P;
68
69 DomainInfo di;
70 di.backend=0;
71 bool first=true;
72 try {
3696224d 73 UeberBackend *B=dynamic_cast<UeberBackend *>(P.getBackend());
d7652f3a
BH
74 NSEC3PARAMRecordContent ns3pr;
75 bool narrow;
76 DNSSECKeeper dk;
77 bool dnssecZone = false;
3c873e66 78 bool haveNSEC3=false;
d3e7090c 79 if(dk.isSecuredZone(domain)) {
d7652f3a 80 dnssecZone=true;
3c873e66 81 haveNSEC3=dk.getNSEC3PARAM(domain, &ns3pr, &narrow);
498e0ffa
BH
82 }
83
84 if(dnssecZone) {
3c873e66 85 if(!haveNSEC3)
498e0ffa
BH
86 L<<Logger::Info<<"Adding NSEC ordering information"<<endl;
87 else if(!narrow)
88 L<<Logger::Info<<"Adding NSEC3 hashed ordering information for '"<<domain<<"'"<<endl;
89 else
90 L<<Logger::Info<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl;
91 }
3696224d
BH
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;
d7652f3a 100 set<string> nsset, qnames;
29b92d6f
BH
101
102 ComboAddress raddr(remote, 53);
103
104 string tsigkeyname, tsigalgorithm, tsigsecret;
16e654fa 105
29b92d6f
BH
106 if(dk.getTSIGForAcces(domain, remote, &tsigkeyname)) {
107 string tsigsecret64;
108 B->getTSIGKey(tsigkeyname, &tsigalgorithm, &tsigsecret64);
109 B64Decode(tsigsecret64, tsigsecret);
110 }
16e654fa 111
29b92d6f
BH
112 AXFRRetriever retriever(raddr, domain.c_str(), tsigkeyname, tsigalgorithm, tsigsecret);
113
0c01dd7c 114 while(retriever.getChunk(recs)) {
3696224d 115 if(first) {
4957a608
BH
116 L<<Logger::Error<<"AXFR started for '"<<domain<<"', transaction started"<<endl;
117 di.backend->startTransaction(domain, domain_id);
118 first=false;
3696224d 119 }
d7652f3a 120
3696224d 121 for(Resolver::res_t::iterator i=recs.begin();i!=recs.end();++i) {
25aeec36
BH
122 if(i->qtype.getCode() == QType::OPT) // ignore EDNS0
123 continue;
81c43517
BH
124
125 // we generate NSEC, NSEC3, NSEC3PARAM (sorry Olafur) on the fly, this could only confuse things
126 if(dnssecZone && (i->qtype.getCode() == QType::NSEC || i->qtype.getCode() == QType::NSEC3 ||
127 i->qtype.getCode() == QType::NSEC3PARAM))
128 continue;
129
4957a608 130 if(!endsOn(i->qname, domain)) {
25aeec36 131 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;
4957a608
BH
132 continue;
133 }
498e0ffa
BH
134
135 if(i->qtype.getCode() == QType::NS && !pdns_iequals(i->qname, domain))
136 nsset.insert(i->qname);
81c43517
BH
137 if(i->qtype.getCode() != QType::RRSIG) // this excludes us hashing RRSIGs for NSEC(3)
138 qnames.insert(i->qname);
498e0ffa 139
4957a608 140 i->domain_id=domain_id;
7fefa73a
BH
141#if 0
142 if(i->qtype.getCode()>=60000)
4957a608 143 throw DBException("Database can't store unknown record type "+lexical_cast<string>(i->qtype.getCode()-1024));
7fefa73a 144#endif
4957a608 145 di.backend->feedRecord(*i);
3696224d
BH
146 }
147 }
498e0ffa
BH
148
149 string hashed;
150 BOOST_FOREACH(const string& qname, qnames)
151 {
152 string shorter(qname);
153 bool auth=true;
154 do {
155 if(nsset.count(shorter)) {
156 auth=false;
157 break;
158 }
159 }while(chopOff(shorter));
d7652f3a 160
498e0ffa
BH
161 if(dnssecZone && !haveNSEC3) // NSEC
162 di.backend->updateDNSSECOrderAndAuth(domain_id, domain, qname, auth);
163 else {
164 if(dnssecZone && !narrow) {
165 hashed=toLower(toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, qname)));
d7652f3a 166 }
498e0ffa 167 di.backend->updateDNSSECOrderAndAuthAbsolute(domain_id, qname, hashed, auth); // this should always be done
d7652f3a
BH
168 }
169 }
498e0ffa 170
3696224d
BH
171 di.backend->commitTransaction();
172 di.backend->setFresh(domain_id);
173 L<<Logger::Error<<"AXFR done for '"<<domain<<"', zone committed"<<endl;
8de9c054
BH
174 if(::arg().mustDo("slave-renotify"))
175 notifyDomain(domain);
3696224d
BH
176 }
177 catch(DBException &re) {
178 L<<Logger::Error<<"Unable to feed record during incoming AXFR of '"+domain+"': "<<re.reason<<endl;
179 if(di.backend && !first) {
180 L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
181 di.backend->abortTransaction();
182 }
183 }
184 catch(ResolverException &re) {
185 L<<Logger::Error<<"Unable to AXFR zone '"+domain+"' from remote '"<<remote<<"': "<<re.reason<<endl;
186 if(di.backend && !first) {
187 L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
188 di.backend->abortTransaction();
189 }
190 }
191}
192struct QueryInfo
193 {
194 struct timeval query_ttd;
195 uint16_t id;
196 };
197
198struct SlaveSenderReceiver
199{
200 typedef pair<string, uint16_t> Identifier;
3696224d 201
54b2edb4
BH
202 struct Answer {
203 uint32_t theirSerial;
204 uint32_t theirInception;
205 uint32_t theirExpire;
206 };
207
208 map<uint32_t, Answer> d_freshness;
3696224d
BH
209
210 SlaveSenderReceiver()
211 {
3696224d
BH
212 }
213
c0a5fc34 214 void deliverTimeout(const Identifier& i)
0c01dd7c
BH
215 {
216 }
c0a5fc34 217
16e654fa 218 Identifier send(pair<DomainInfo, bool>& dipair)
3696224d 219 {
16e654fa 220 random_shuffle(dipair.first.masters.begin(), dipair.first.masters.end());
0c01dd7c 221 try {
16e654fa
BH
222 ComboAddress remote(*dipair.first.masters.begin());
223 return make_pair(dipair.first.zone, d_resolver.sendResolve(ComboAddress(*dipair.first.masters.begin(), 53), dipair.first.zone.c_str(), QType::SOA, dipair.second));
0c01dd7c
BH
224 }
225 catch(AhuException& e) {
16e654fa 226 throw runtime_error("While attempting to query freshness of '"+dipair.first.zone+"': "+e.reason);
0c01dd7c 227 }
3696224d
BH
228 }
229
230 bool receive(Identifier& id, Answer& a)
231 {
54b2edb4 232 if(d_resolver.tryGetSOASerial(&id.first, &a.theirSerial, &a.theirInception, &a.theirExpire, &id.second)) {
3696224d
BH
233 return 1;
234 }
235 return 0;
236 }
237
16e654fa 238 void deliverAnswer(pair<DomainInfo, bool>& i, const Answer& a, unsigned int usec)
3696224d 239 {
16e654fa 240 d_freshness[i.first.id]=a;
3696224d
BH
241 }
242
243 Resolver d_resolver;
3696224d
BH
244};
245
246void CommunicatorClass::slaveRefresh(PacketHandler *P)
247{
248 UeberBackend *B=dynamic_cast<UeberBackend *>(P->getBackend());
16e654fa
BH
249 vector<DomainInfo> rdomains;
250 vector<pair<DomainInfo, bool> > sdomains;
dbcb3066 251 B->getUnfreshSlaveInfos(&rdomains);
16e654fa 252 DNSSECKeeper dk;
dbcb3066
BH
253 {
254 Lock l(&d_lock);
255 typedef UniQueue::index<IDTag>::type domains_by_name_t;
256 domains_by_name_t& nameindex=boost::multi_index::get<IDTag>(d_suckdomains);
257
dbcb3066
BH
258 BOOST_FOREACH(DomainInfo& di, rdomains) {
259 SuckRequest sr;
260 sr.domain=di.zone;
261 if(di.masters.empty()) // slave domains w/o masters are ignored
262 continue;
263 // remove unfresh domains already queued for AXFR, no sense polling them again
264 sr.master=*di.masters.begin();
265 if(nameindex.count(sr))
266 continue;
16e654fa
BH
267
268 sdomains.push_back(make_pair(di, dk.isPresigned(di.zone)));
dbcb3066 269 }
dbcb3066
BH
270 }
271
3696224d
BH
272 if(sdomains.empty())
273 {
dbcb3066
BH
274 if(d_slaveschanged) {
275 Lock l(&d_lock);
276 L<<Logger::Warning<<"No new unfresh slave domains, "<<d_suckdomains.size()<<" queued for AXFR already"<<endl;
277 }
278 d_slaveschanged = !rdomains.empty();
3696224d
BH
279 return;
280 }
dbcb3066
BH
281 else {
282 Lock l(&d_lock);
3696224d
BH
283 L<<Logger::Warning<<sdomains.size()<<" slave domain"<<(sdomains.size()>1 ? "s" : "")<<" need"<<
284 (sdomains.size()>1 ? "" : "s")<<
dbcb3066
BH
285 " checking, "<<d_suckdomains.size()<<" queued for AXFR"<<endl;
286 }
3696224d
BH
287
288 SlaveSenderReceiver ssr;
16e654fa 289 Inflighter<vector<pair<DomainInfo, bool> >, SlaveSenderReceiver> ifl(sdomains, ssr);
3696224d
BH
290
291 ifl.d_maxInFlight = 200;
292
293 for(;;) {
294 try {
295 ifl.run();
296 break;
297 }
dbcb3066 298 catch(std::exception& e) {
3696224d
BH
299 L<<Logger::Error<<"While checking domain freshness: " << e.what()<<endl;
300 }
301 catch(AhuException &re) {
302 L<<Logger::Error<<"While checking domain freshness: " << re.reason<<endl;
303 }
304 }
0c01dd7c 305 L<<Logger::Warning<<"Received serial number updates for "<<ssr.d_freshness.size()<<" zones, had "<<ifl.getTimeouts()<<" timeouts"<<endl;
16e654fa
BH
306
307 typedef pair<DomainInfo, bool> val_t;
308 BOOST_FOREACH(val_t& val, sdomains) {
309 DomainInfo& di(val.first);
54b2edb4 310 if(!ssr.d_freshness.count(di.id))
3696224d 311 continue;
54b2edb4 312 uint32_t theirserial = ssr.d_freshness[di.id].theirSerial, ourserial = di.serial;
3696224d
BH
313
314 if(theirserial < ourserial) {
315 L<<Logger::Error<<"Domain "<<di.zone<<" more recent than master, our serial " << ourserial << " > their serial "<< theirserial << endl;
316 di.backend->setFresh(di.id);
317 }
318 else if(theirserial == ourserial) {
54b2edb4
BH
319 if(!dk.isPresigned(di.zone)) {
320 L<<Logger::Warning<<"Domain "<< di.zone<<" is fresh (not presigned, no RRSIG check)"<<endl;
321 di.backend->setFresh(di.id);
322 }
323 else {
324 B->lookup(QType(QType::RRSIG), di.zone);
325 DNSResourceRecord rr;
326 uint32_t maxExpire=0, maxInception=0;
327 while(B->get(rr)) {
328 RRSIGRecordContent rrc(rr.content);
329 if(rrc.d_type == QType::SOA) {
330 maxInception = std::max(maxInception, rrc.d_siginception);
331 maxExpire = std::max(maxExpire, rrc.d_sigexpire);
332 }
333 }
334 if(maxInception == ssr.d_freshness[di.id].theirInception && maxExpire == ssr.d_freshness[di.id].theirExpire) {
335 L<<Logger::Warning<<"Domain "<< di.zone<<" is fresh and apex RRSIGs match"<<endl;
336 di.backend->setFresh(di.id);
337 }
338 else {
339 L<<Logger::Warning<<"Domain "<< di.zone<<" is fresh, but RRSIGS differ, so DNSSEC stale"<<endl;
340 addSuckRequest(di.zone, *di.masters.begin());
341 }
342 }
3696224d
BH
343 }
344 else {
345 L<<Logger::Warning<<"Domain "<< di.zone<<" is stale, master serial "<<theirserial<<", our serial "<< ourserial <<endl;
346 addSuckRequest(di.zone, *di.masters.begin());
3696224d
BH
347 }
348 }
3696224d
BH
349}
350