]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/slavecommunicator.cc
implement TSIG for IXFR slaving, make ixplore use that infrastructure. Todo: hook...
[thirdparty/pdns.git] / pdns / slavecommunicator.cc
CommitLineData
3696224d
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
fc396d56 3 Copyright (C) 2002-2012 PowerDNS.COM BV
3696224d
BH
4
5 This program is free software; you can redistribute it and/or modify
68dae32c
KM
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation;
3696224d 8
f782fe38
MH
9 Additionally, the license of this program contains a special
10 exception which allows to distribute the program in binary form when
11 it is linked against OpenSSL.
12
3696224d
BH
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21*/
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
3696224d
BH
25#include "packetcache.hh"
26#include "utility.hh"
d7652f3a
BH
27#include "dnssecinfra.hh"
28#include "dnsseckeeper.hh"
29#include "base32.hh"
3696224d
BH
30#include <errno.h>
31#include "communicator.hh"
32#include <set>
33#include <boost/utility.hpp>
34#include "dnsbackend.hh"
35#include "ueberbackend.hh"
36#include "packethandler.hh"
37#include "resolver.hh"
38#include "logger.hh"
39#include "dns.hh"
40#include "arguments.hh"
3696224d 41#include "packetcache.hh"
fa8fd4d2 42
29b92d6f 43#include "base64.hh"
3696224d 44#include "inflighter.cc"
5704e107 45#include "lua-auth.hh"
3696224d 46#include "namespaces.hh"
7108e055 47#include "common_startup.hh"
e23622a7
BH
48#include <boost/scoped_ptr.hpp>
49using boost::scoped_ptr;
3696224d 50
fab71044 51
7abbc40f 52void CommunicatorClass::addSuckRequest(const DNSName &domain, const string &master)
3696224d
BH
53{
54 Lock l(&d_lock);
3696224d
BH
55 SuckRequest sr;
56 sr.domain = domain;
57 sr.master = master;
dbcb3066 58 pair<UniQueue::iterator, bool> res;
a71bee29
PD
59
60 res=d_suckdomains.push_back(sr);
68dae32c 61
dbcb3066 62 if(res.second) {
7f3d870e 63 d_suck_sem.post();
dbcb3066 64 }
3696224d
BH
65}
66
7abbc40f 67void CommunicatorClass::suck(const DNSName &domain,const string &remote)
3696224d 68{
f43c4448 69 L<<Logger::Error<<"Initiating transfer of '"<<domain<<"' from remote '"<<remote<<"'"<<endl;
295c4a00 70 UeberBackend B; // fresh UeberBackend
3696224d
BH
71
72 DomainInfo di;
73 di.backend=0;
376ec278 74 bool transaction=false;
3696224d 75 try {
295c4a00 76 DNSSECKeeper dk (&B); // reuse our UeberBackend copy for DNSSECKeeper
a1282cdd 77
295c4a00 78 if(!B.getDomainInfo(domain, di) || !di.backend) { // di.backend and B are mostly identical
f43c4448 79 L<<Logger::Error<<"Can't determine backend for domain '"<<domain<<"'"<<endl;
3696224d
BH
80 return;
81 }
53568203
KM
82 uint32_t domain_id=di.id;
83
3696224d 84
7abbc40f
PD
85 DNSName tsigkeyname, tsigalgorithm;
86 string tsigsecret;
7597b3cf 87 if(dk.getTSIGForAccess(domain, remote, &tsigkeyname)) {
29b92d6f 88 string tsigsecret64;
295c4a00 89 if(B.getTSIGKey(tsigkeyname, &tsigalgorithm, &tsigsecret64)) {
a1467662 90 B64Decode(tsigsecret64, tsigsecret);
53568203 91 } else {
f43c4448 92 L<<Logger::Error<<"TSIG key '"<<tsigkeyname<<"' for domain '"<<domain<<"' not found"<<endl;
53568203 93 return;
a1467662 94 }
29b92d6f 95 }
53568203
KM
96
97
5704e107 98 scoped_ptr<AuthLua> pdl;
e23622a7 99 vector<string> scripts;
295c4a00 100 if(B.getDomainMetadata(domain, "LUA-AXFR-SCRIPT", scripts) && !scripts.empty()) {
b2aefbf9 101 try {
5704e107 102 pdl.reset(new AuthLua(scripts[0]));
f43c4448 103 L<<Logger::Info<<"Loaded Lua script '"<<scripts[0]<<"' to edit the incoming AXFR of '"<<domain<<"'"<<endl;
b2aefbf9
BH
104 }
105 catch(std::exception& e) {
f43c4448 106 L<<Logger::Error<<"Failed to load Lua editing script '"<<scripts[0]<<"' for incoming AXFR of '"<<domain<<"': "<<e.what()<<endl;
b2aefbf9
BH
107 return;
108 }
e23622a7 109 }
53568203 110
fc396d56
BH
111 vector<string> localaddr;
112 ComboAddress laddr;
295c4a00 113 if(B.getDomainMetadata(domain, "AXFR-SOURCE", localaddr) && !localaddr.empty()) {
fc396d56
BH
114 try {
115 laddr = ComboAddress(localaddr[0]);
f43c4448 116 L<<Logger::Info<<"AXFR source for domain '"<<domain<<"' set to "<<localaddr[0]<<endl;
fc396d56
BH
117 }
118 catch(std::exception& e) {
f43c4448 119 L<<Logger::Error<<"Failed to load AXFR source '"<<localaddr[0]<<"' for incoming AXFR of '"<<domain<<"': "<<e.what()<<endl;
fc396d56
BH
120 return;
121 }
122 } else {
53568203 123 laddr.sin4.sin_family = 0;
8c949c52 124 }
fc396d56 125
53568203
KM
126 bool hadDnssecZone = false;
127 bool hadPresigned = false;
128 bool hadNSEC3 = false;
129 NSEC3PARAMRecordContent ns3pr, hadNs3pr;
130 bool isNarrow, hadNarrow=false;
131
132 if(dk.isSecuredZone(domain)) {
133 hadDnssecZone=true;
134 hadPresigned=dk.isPresigned(domain);
135 if (dk.getNSEC3PARAM(domain, &ns3pr, &isNarrow)) {
136 hadNSEC3 = true;
137 hadNs3pr = ns3pr;
138 hadNarrow = isNarrow;
139 }
140 }
141
53568203
KM
142 bool isDnssecZone = false;
143 bool isPresigned = false;
144 bool isNSEC3 = false;
145 bool optOutFlag = false;
146
376ec278 147 bool first=true;
82fb5386 148 bool firstNSEC3=true;
53568203 149 unsigned int soa_serial = 0;
7abbc40f 150 set<DNSName> nsset, qnames, secured;
53568203
KM
151 vector<DNSResourceRecord> rrs;
152
153 ComboAddress raddr(remote, 53);
7abbc40f 154 AXFRRetriever retriever(raddr, domain, tsigkeyname, tsigalgorithm, tsigsecret, (laddr.sin4.sin_family == 0) ? NULL : &laddr);
53568203 155 Resolver::res_t recs;
0c01dd7c 156 while(retriever.getChunk(recs)) {
3696224d 157 if(first) {
f43c4448 158 L<<Logger::Error<<"AXFR started for '"<<domain<<"'"<<endl;
4957a608 159 first=false;
3696224d 160 }
f9cf6d92 161
3696224d 162 for(Resolver::res_t::iterator i=recs.begin();i!=recs.end();++i) {
7597b3cf 163 if(i->qtype.getCode() == QType::OPT || i->qtype.getCode() == QType::TSIG) // ignore EDNS0 & TSIG
25aeec36 164 continue;
f9cf6d92 165
7abbc40f 166 if(!i->qname.isPartOf(domain)) {
f43c4448 167 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;
f9cf6d92 168 continue;
207f9ea1 169 }
f9cf6d92 170
68fa524b
KM
171 vector<DNSResourceRecord> out;
172 if(!pdl || !pdl->axfrfilter(raddr, domain, *i, out)) {
173 out.push_back(*i);
a1282cdd 174 }
b5baefaf 175
ef7cd021 176 for(DNSResourceRecord& rr : out) {
68fa524b
KM
177 switch(rr.qtype.getCode()) {
178 case QType::NSEC3PARAM: {
179 ns3pr = NSEC3PARAMRecordContent(rr.content);
180 isDnssecZone = isNSEC3 = true;
181 isNarrow = false;
182 continue;
183 }
184 case QType::NSEC3: {
185 NSEC3RecordContent ns3rc(rr.content);
186 if (firstNSEC3) {
187 isDnssecZone = isPresigned = true;
188 firstNSEC3 = false;
189 } else if (optOutFlag != (ns3rc.d_flags & 1))
190 throw PDNSException("Zones with a mixture of Opt-Out NSEC3 RRs and non-Opt-Out NSEC3 RRs are not supported.");
191 optOutFlag = ns3rc.d_flags & 1;
e325f20c 192 if (ns3rc.d_set.count(QType::NS) && !(rr.qname==domain))
290a083d 193 secured.insert(DNSName(toLower(makeRelative(rr.qname.toString(), domain.toString())))); // XXX DNSName pain
68fa524b
KM
194 continue;
195 }
196 case QType::NSEC: {
197 isDnssecZone = isPresigned = true;
198 continue;
199 }
200 case QType::SOA: {
201 if(soa_serial != 0)
202 continue; //skip the last SOA
203 SOAData sd;
204 fillSOAData(rr.content,sd);
205 soa_serial = sd.serial;
206 break;
207 }
208 case QType::NS: {
e325f20c 209 if(rr.qname!=domain)
68fa524b
KM
210 nsset.insert(rr.qname);
211 break;
212 }
213 default:
214 break;
215 }
f9cf6d92 216
68fa524b 217 qnames.insert(rr.qname);
f9cf6d92 218
68fa524b
KM
219 rr.domain_id=domain_id;
220 rrs.push_back(rr);
e23622a7 221 }
3696224d
BH
222 }
223 }
68fa524b 224
53568203
KM
225 if(isNSEC3) {
226 ns3pr.d_flags = optOutFlag ? 1 : 0;
227 }
ff473027 228
53568203
KM
229
230 if(!isPresigned) {
231 DNSSECKeeper::keyset_t keys = dk.getKeys(domain);
232 if(!keys.empty()) {
233 isDnssecZone = true;
234 isNSEC3 = hadNSEC3;
235 ns3pr = hadNs3pr;
236 optOutFlag = (hadNs3pr.d_flags & 1);
237 isNarrow = hadNarrow;
238 }
239 }
240
241
242 if(isDnssecZone) {
243 if(!isNSEC3)
f9cf6d92 244 L<<Logger::Info<<"Adding NSEC ordering information"<<endl;
53568203 245 else if(!isNarrow)
f43c4448 246 L<<Logger::Info<<"Adding NSEC3 hashed ordering information for '"<<domain<<"'"<<endl;
f9cf6d92
KM
247 else
248 L<<Logger::Info<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl;
249 }
250
b5baefaf 251
53568203 252 transaction=di.backend->startTransaction(domain, domain_id);
f43c4448 253 L<<Logger::Error<<"Transaction started for '"<<domain<<"'"<<endl;
f9cf6d92 254
7f2aa497 255 // update the presigned flag and NSEC3PARAM
53568203
KM
256 if (isDnssecZone) {
257 // update presigned if there was a change
258 if (isPresigned && !hadPresigned) {
7f2aa497
KM
259 // zone is now presigned
260 dk.setPresigned(domain);
53568203
KM
261 } else if (hadPresigned && !isPresigned) {
262 // zone is no longer presigned
263 dk.unsetPresigned(domain);
7f2aa497 264 }
53568203
KM
265 // update NSEC3PARAM
266 if (isNSEC3) {
267 // zone is NSEC3, only update if there was a change
268 if (!hadNSEC3 || (hadNarrow != isNarrow) ||
269 (ns3pr.d_algorithm != hadNs3pr.d_algorithm) ||
270 (ns3pr.d_flags != hadNs3pr.d_flags) ||
271 (ns3pr.d_iterations != hadNs3pr.d_iterations) ||
272 (ns3pr.d_salt != hadNs3pr.d_salt)) {
273 dk.setNSEC3PARAM(domain, ns3pr, isNarrow);
7f2aa497 274 }
53568203
KM
275 } else if (hadNSEC3 ) {
276 // zone is no longer NSEC3
277 dk.unsetNSEC3PARAM(domain);
278 }
279 } else if (hadDnssecZone) {
280 // zone is no longer signed
281 if (hadPresigned) {
282 // remove presigned
283 dk.unsetPresigned(domain);
284 }
285 if (hadNSEC3) {
286 // unset NSEC3PARAM
287 dk.unsetNSEC3PARAM(domain);
7f2aa497 288 }
7f2aa497
KM
289 }
290
f9cf6d92 291 bool doent=true;
b5baefaf 292 uint32_t maxent = ::arg().asNum("max-ent-entries");
7abbc40f
PD
293 string ordername;
294 DNSName shorter;
295 set<DNSName> rrterm;
296 map<DNSName,bool> nonterm;
b5baefaf 297
53568203 298
ef7cd021 299 for(DNSResourceRecord& rr : rrs) {
27045410 300
53568203
KM
301 if(!isPresigned) {
302 if (rr.qtype.getCode() == QType::RRSIG)
303 continue;
304 if(isDnssecZone && rr.qtype.getCode() == QType::DNSKEY && !::arg().mustDo("direct-dnskey"))
305 continue;
306 }
307
f9cf6d92
KM
308 // Figure out auth and ents
309 rr.auth=true;
310 shorter=rr.qname;
311 rrterm.clear();
312 do {
313 if(doent) {
e5c7239c 314 if (!qnames.count(shorter))
f9cf6d92 315 rrterm.insert(shorter);
d7652f3a 316 }
e5c7239c 317 if(nsset.count(shorter) && rr.qtype.getCode() != QType::DS)
f9cf6d92 318 rr.auth=false;
e5c7239c 319
e325f20c 320 if (shorter==domain) // stop at apex
f9cf6d92 321 break;
7abbc40f 322 }while(shorter.chopOff());
b8adb30d 323
e5c7239c
KM
324 // Insert ents
325 if(doent && !rrterm.empty()) {
326 bool auth;
327 if (!rr.auth && rr.qtype.getCode() == QType::NS) {
26752fd7 328 if (isNSEC3)
28e2e78e 329 ordername=toBase32Hex(hashQNameWithSalt(ns3pr, rr.qname));
290a083d 330 auth=(!isNSEC3 || !optOutFlag || secured.count(DNSName(ordername)));
e5c7239c
KM
331 } else
332 auth=rr.auth;
333
7abbc40f 334 for(const auto &nt: rrterm){
e5c7239c 335 if (!nonterm.count(nt))
7abbc40f 336 nonterm.insert(pair<DNSName, bool>(nt, auth));
e5c7239c
KM
337 else if (auth)
338 nonterm[nt]=true;
339 }
340
f9cf6d92 341 if(nonterm.size() > maxent) {
f43c4448 342 L<<Logger::Error<<"AXFR zone "<<domain<<" has too many empty non terminals."<<endl;
f9cf6d92
KM
343 nonterm.clear();
344 doent=false;
27045410 345 }
d7652f3a 346 }
f9cf6d92
KM
347
348 // RRSIG is always auth, even inside a delegation
349 if (rr.qtype.getCode() == QType::RRSIG)
350 rr.auth=true;
351
352 // Add ordername and insert record
53568203
KM
353 if (isDnssecZone && rr.qtype.getCode() != QType::RRSIG) {
354 if (isNSEC3) {
f9cf6d92 355 // NSEC3
28e2e78e 356 ordername=toBase32Hex(hashQNameWithSalt(ns3pr, rr.qname));
290a083d 357 if(!isNarrow && (rr.auth || (rr.qtype.getCode() == QType::NS && (!optOutFlag || secured.count(DNSName(ordername)))))) {
f9cf6d92
KM
358 di.backend->feedRecord(rr, &ordername);
359 } else
360 di.backend->feedRecord(rr);
361 } else {
362 // NSEC
363 if (rr.auth || rr.qtype.getCode() == QType::NS) {
7abbc40f 364 ordername=toLower(labelReverse(makeRelative(rr.qname.toString(), domain.toString())));
f9cf6d92
KM
365 di.backend->feedRecord(rr, &ordername);
366 } else
367 di.backend->feedRecord(rr);
368 }
369 } else
370 di.backend->feedRecord(rr);
d7652f3a 371 }
b5baefaf 372
f9cf6d92
KM
373 // Insert empty non-terminals
374 if(doent && !nonterm.empty()) {
53568203 375 if (isNSEC3) {
28e2e78e 376 di.backend->feedEnts3(domain_id, domain, nonterm, ns3pr, isNarrow);
f9cf6d92
KM
377 } else
378 di.backend->feedEnts(domain_id, nonterm);
b5baefaf
PD
379 }
380
14b7e03b 381 di.backend->commitTransaction();
376ec278 382 transaction = false;
14b7e03b 383 di.backend->setFresh(domain_id);
7abbc40f 384 PC.purge(domain.toString()+"$");
14b7e03b 385
a1282cdd 386
207f9ea1 387 L<<Logger::Error<<"AXFR done for '"<<domain<<"', zone committed with serial number "<<soa_serial<<endl;
8de9c054
BH
388 if(::arg().mustDo("slave-renotify"))
389 notifyDomain(domain);
3696224d
BH
390 }
391 catch(DBException &re) {
290a083d 392 L<<Logger::Error<<"Unable to feed record during incoming AXFR of '" << domain<<"': "<<re.reason<<endl;
376ec278 393 if(di.backend && transaction) {
3696224d
BH
394 L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
395 di.backend->abortTransaction();
396 }
397 }
75ccb5b9 398 catch(MOADNSException &re) {
290a083d 399 L<<Logger::Error<<"Unable to parse record during incoming AXFR of '"<<domain<<"' (MOADNSException): "<<re.what()<<endl;
376ec278 400 if(di.backend && transaction) {
2714db17
BH
401 L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
402 di.backend->abortTransaction();
403 }
404 }
405 catch(std::exception &re) {
290a083d 406 L<<Logger::Error<<"Unable to parse record during incoming AXFR of '"<<domain<<"' (std::exception): "<<re.what()<<endl;
376ec278 407 if(di.backend && transaction) {
75ccb5b9
BH
408 L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
409 di.backend->abortTransaction();
410 }
411 }
3696224d 412 catch(ResolverException &re) {
290a083d 413 L<<Logger::Error<<"Unable to AXFR zone '"<<domain<<"' from remote '"<<remote<<"' (resolver): "<<re.reason<<endl;
376ec278 414 if(di.backend && transaction) {
3696224d 415 L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
7f450125
PD
416 di.backend->abortTransaction();
417 }
418 }
3f81d239 419 catch(PDNSException &ae) {
290a083d 420 L<<Logger::Error<<"Unable to AXFR zone '"<<domain<<"' from remote '"<<remote<<"' (PDNSException): "<<ae.reason<<endl;
376ec278 421 if(di.backend && transaction) {
7f450125 422 L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
3696224d
BH
423 di.backend->abortTransaction();
424 }
425 }
426}
7597b3cf 427namespace {
3696224d 428struct QueryInfo
7597b3cf
BH
429{
430 struct timeval query_ttd;
431 uint16_t id;
432};
433
434struct DomainNotificationInfo
435{
436 DomainInfo di;
437 bool dnssecOk;
95302209 438 ComboAddress localaddr;
561434a6
PD
439 DNSName tsigkeyname, tsigalgname;
440 string tsigsecret;
7597b3cf
BH
441};
442}
443
3696224d
BH
444
445struct SlaveSenderReceiver
446{
561434a6 447 typedef pair<DNSName, uint16_t> Identifier;
53568203 448
54b2edb4
BH
449 struct Answer {
450 uint32_t theirSerial;
451 uint32_t theirInception;
452 uint32_t theirExpire;
453 };
53568203 454
54b2edb4 455 map<uint32_t, Answer> d_freshness;
53568203 456
3696224d
BH
457 SlaveSenderReceiver()
458 {
3696224d 459 }
53568203 460
c0a5fc34 461 void deliverTimeout(const Identifier& i)
0c01dd7c
BH
462 {
463 }
53568203 464
7597b3cf 465 Identifier send(DomainNotificationInfo& dni)
3696224d 466 {
7597b3cf 467 random_shuffle(dni.di.masters.begin(), dni.di.masters.end());
0c01dd7c 468 try {
7597b3cf 469 ComboAddress remote(*dni.di.masters.begin());
95302209 470 if (dni.localaddr.sin4.sin_family == 0) {
53568203
KM
471 return make_pair(dni.di.zone,
472 d_resolver.sendResolve(ComboAddress(*dni.di.masters.begin(), 53),
7abbc40f 473 dni.di.zone,
53568203 474 QType::SOA,
95302209
AT
475 dni.dnssecOk, dni.tsigkeyname, dni.tsigalgname, dni.tsigsecret)
476 );
477 } else {
478 return make_pair(dni.di.zone,
479 d_resolver.sendResolve(ComboAddress(*dni.di.masters.begin(), 53), dni.localaddr,
7abbc40f 480 dni.di.zone,
95302209
AT
481 QType::SOA,
482 dni.dnssecOk, dni.tsigkeyname, dni.tsigalgname, dni.tsigsecret)
483 );
484 }
0c01dd7c 485 }
3f81d239 486 catch(PDNSException& e) {
7abbc40f 487 throw runtime_error("While attempting to query freshness of '"+dni.di.zone.toString()+"': "+e.reason);
0c01dd7c 488 }
3696224d 489 }
68dae32c 490
3696224d
BH
491 bool receive(Identifier& id, Answer& a)
492 {
54b2edb4 493 if(d_resolver.tryGetSOASerial(&id.first, &a.theirSerial, &a.theirInception, &a.theirExpire, &id.second)) {
3696224d
BH
494 return 1;
495 }
496 return 0;
497 }
68dae32c 498
7597b3cf 499 void deliverAnswer(DomainNotificationInfo& dni, const Answer& a, unsigned int usec)
3696224d 500 {
7597b3cf 501 d_freshness[dni.di.id]=a;
3696224d 502 }
68dae32c 503
3696224d 504 Resolver d_resolver;
3696224d
BH
505};
506
7f3d870e
BH
507void CommunicatorClass::addSlaveCheckRequest(const DomainInfo& di, const ComboAddress& remote)
508{
509 Lock l(&d_lock);
90c9a70e
BH
510 DomainInfo ours = di;
511 ours.backend = 0;
512 d_tocheck.insert(ours);
7f3d870e
BH
513 d_any_sem.post(); // kick the loop!
514}
515
7108e055
PD
516void CommunicatorClass::addTrySuperMasterRequest(DNSPacket *p)
517{
518 Lock l(&d_lock);
519 DNSPacket ours = *p;
520 d_potentialsupermasters.push_back(ours);
521 d_any_sem.post(); // kick the loop!
522}
523
3696224d
BH
524void CommunicatorClass::slaveRefresh(PacketHandler *P)
525{
56a4f068
AT
526 // not unless we are slave
527 if (!::arg().mustDo("slave")) return;
528
3971cf53 529 UeberBackend *B=P->getBackend();
16e654fa 530 vector<DomainInfo> rdomains;
a71bee29 531 vector<DomainNotificationInfo> sdomains; // the bool is for 'presigned'
7108e055 532 vector<DNSPacket> trysuperdomains;
68dae32c 533
7f3d870e
BH
534 {
535 Lock l(&d_lock);
536 rdomains.insert(rdomains.end(), d_tocheck.begin(), d_tocheck.end());
537 d_tocheck.clear();
7108e055
PD
538 trysuperdomains.insert(trysuperdomains.end(), d_potentialsupermasters.begin(), d_potentialsupermasters.end());
539 d_potentialsupermasters.clear();
7f3d870e 540 }
68dae32c 541
ef7cd021 542 for(DNSPacket& dp : trysuperdomains) {
7108e055
PD
543 int res;
544 res=P->trySuperMasterSynchronous(&dp);
545 if(res>=0) {
546 DNSPacket *r=dp.replyPacket();
547 r->setRcode(res);
548 r->setOpcode(Opcode::Notify);
549 N->send(r);
550 delete r;
551 }
552 }
553
7f3d870e
BH
554 if(rdomains.empty()) // if we have priority domains, check them first
555 B->getUnfreshSlaveInfos(&rdomains);
68dae32c 556
936eb34a 557 DNSSECKeeper dk(B); // NOW HEAR THIS! This DK uses our B backend, so no interleaved access!
dbcb3066
BH
558 {
559 Lock l(&d_lock);
dbcb3066
BH
560 domains_by_name_t& nameindex=boost::multi_index::get<IDTag>(d_suckdomains);
561
ef7cd021 562 for(DomainInfo& di : rdomains) {
95302209 563 std::vector<std::string> localaddr;
dbcb3066
BH
564 SuckRequest sr;
565 sr.domain=di.zone;
566 if(di.masters.empty()) // slave domains w/o masters are ignored
567 continue;
568 // remove unfresh domains already queued for AXFR, no sense polling them again
569 sr.master=*di.masters.begin();
a71bee29 570 if(nameindex.count(sr)) {
dbcb3066 571 continue;
a71bee29 572 }
7597b3cf
BH
573 DomainNotificationInfo dni;
574 dni.di=di;
575 dni.dnssecOk = dk.isPresigned(di.zone);
68dae32c 576
7597b3cf
BH
577 if(dk.getTSIGForAccess(di.zone, sr.master, &dni.tsigkeyname)) {
578 string secret64;
579 B->getTSIGKey(dni.tsigkeyname, &dni.tsigalgname, &secret64);
580 B64Decode(secret64, dni.tsigsecret);
581 }
95302209
AT
582
583 localaddr.clear();
584 // check for AXFR-SOURCE
585 if(B->getDomainMetadata(di.zone, "AXFR-SOURCE", localaddr) && !localaddr.empty()) {
586 try {
587 dni.localaddr = ComboAddress(localaddr[0]);
588 L<<Logger::Info<<"Freshness check source (AXFR-SOURCE) for domain '"<<di.zone<<"' set to "<<localaddr[0]<<endl;
589 }
590 catch(std::exception& e) {
591 L<<Logger::Error<<"Failed to load freshness check source '"<<localaddr[0]<<"' for '"<<di.zone<<"': "<<e.what()<<endl;
592 return;
593 }
594 } else {
595 dni.localaddr.sin4.sin_family = 0;
596 }
597
7597b3cf 598 sdomains.push_back(dni);
dbcb3066 599 }
dbcb3066 600 }
68dae32c 601
3696224d
BH
602 if(sdomains.empty())
603 {
dbcb3066
BH
604 if(d_slaveschanged) {
605 Lock l(&d_lock);
606 L<<Logger::Warning<<"No new unfresh slave domains, "<<d_suckdomains.size()<<" queued for AXFR already"<<endl;
607 }
608 d_slaveschanged = !rdomains.empty();
3696224d
BH
609 return;
610 }
dbcb3066
BH
611 else {
612 Lock l(&d_lock);
3696224d
BH
613 L<<Logger::Warning<<sdomains.size()<<" slave domain"<<(sdomains.size()>1 ? "s" : "")<<" need"<<
614 (sdomains.size()>1 ? "" : "s")<<
dbcb3066
BH
615 " checking, "<<d_suckdomains.size()<<" queued for AXFR"<<endl;
616 }
68dae32c 617
3696224d 618 SlaveSenderReceiver ssr;
68dae32c 619
7597b3cf 620 Inflighter<vector<DomainNotificationInfo>, SlaveSenderReceiver> ifl(sdomains, ssr);
68dae32c 621
3696224d
BH
622 ifl.d_maxInFlight = 200;
623
624 for(;;) {
625 try {
626 ifl.run();
627 break;
628 }
dbcb3066 629 catch(std::exception& e) {
3696224d
BH
630 L<<Logger::Error<<"While checking domain freshness: " << e.what()<<endl;
631 }
68dae32c 632 catch(PDNSException &re) {
3696224d
BH
633 L<<Logger::Error<<"While checking domain freshness: " << re.reason<<endl;
634 }
635 }
0c01dd7c 636 L<<Logger::Warning<<"Received serial number updates for "<<ssr.d_freshness.size()<<" zones, had "<<ifl.getTimeouts()<<" timeouts"<<endl;
16e654fa 637
7597b3cf 638 typedef DomainNotificationInfo val_t;
ef7cd021 639 for(val_t& val : sdomains) {
7597b3cf 640 DomainInfo& di(val.di);
f9fa0e2d
PD
641 // might've come from the packethandler
642 if(!di.backend && !B->getDomainInfo(di.zone, di)) {
f43c4448 643 L<<Logger::Warning<<"Ignore domain "<< di.zone<<" since it has been removed from our backend"<<endl;
232f0877 644 continue;
f9fa0e2d 645 }
68dae32c
KM
646
647 if(!ssr.d_freshness.count(di.id))
3696224d 648 continue;
54b2edb4 649 uint32_t theirserial = ssr.d_freshness[di.id].theirSerial, ourserial = di.serial;
68dae32c 650
da1a6059 651 if(rfc1982LessThan(theirserial, ourserial) && ourserial != 0) {
8e90e428 652 L<<Logger::Error<<"Domain '"<<di.zone<<"' more recent than master, our serial " << ourserial << " > their serial "<< theirserial << endl;
3696224d
BH
653 di.backend->setFresh(di.id);
654 }
655 else if(theirserial == ourserial) {
54b2edb4 656 if(!dk.isPresigned(di.zone)) {
1d3fae1b 657 L<<Logger::Info<<"Domain '"<< di.zone<<"' is fresh (not presigned, no RRSIG check)"<<endl;
54b2edb4
BH
658 di.backend->setFresh(di.id);
659 }
660 else {
936eb34a 661 B->lookup(QType(QType::RRSIG), di.zone); // can't use DK before we are done with this lookup!
54b2edb4
BH
662 DNSResourceRecord rr;
663 uint32_t maxExpire=0, maxInception=0;
664 while(B->get(rr)) {
665 RRSIGRecordContent rrc(rr.content);
666 if(rrc.d_type == QType::SOA) {
667 maxInception = std::max(maxInception, rrc.d_siginception);
668 maxExpire = std::max(maxExpire, rrc.d_sigexpire);
669 }
670 }
671 if(maxInception == ssr.d_freshness[di.id].theirInception && maxExpire == ssr.d_freshness[di.id].theirExpire) {
f43c4448 672 L<<Logger::Info<<"Domain '"<< di.zone<<"' is fresh and apex RRSIGs match"<<endl;
54b2edb4
BH
673 di.backend->setFresh(di.id);
674 }
675 else {
8e90e428 676 L<<Logger::Warning<<"Domain '"<< di.zone<<"' is fresh, but RRSIGS differ, so DNSSEC stale"<<endl;
54b2edb4
BH
677 addSuckRequest(di.zone, *di.masters.begin());
678 }
679 }
3696224d
BH
680 }
681 else {
8e90e428 682 L<<Logger::Warning<<"Domain '"<< di.zone<<"' is stale, master serial "<<theirserial<<", our serial "<< ourserial <<endl;
3696224d 683 addSuckRequest(di.zone, *di.masters.begin());
3696224d
BH
684 }
685 }
68dae32c 686}
3696224d 687
bd53ea9d
PD
688// stub for PowerDNSLua linking
689int directResolve(const std::string& qname, const QType& qtype, int qclass, vector<DNSResourceRecord>& ret)
690{
691 return -1;
692}
693
694