]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/slavecommunicator.cc
nsecrecords: fix var shadowing
[thirdparty/pdns.git] / pdns / slavecommunicator.cc
CommitLineData
3696224d
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
cd189f24 3 Copyright (C) 2002-2016 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"
3e7dcee6 48
cd189f24 49#include "ixfr.hh"
e23622a7 50using boost::scoped_ptr;
3696224d 51
fab71044 52
d3ee36f2 53void CommunicatorClass::addSuckRequest(const DNSName &domain, const string &master)
3696224d
BH
54{
55 Lock l(&d_lock);
3696224d
BH
56 SuckRequest sr;
57 sr.domain = domain;
58 sr.master = master;
dbcb3066 59 pair<UniQueue::iterator, bool> res;
a71bee29
PD
60
61 res=d_suckdomains.push_back(sr);
dbcb3066 62 if(res.second) {
7f3d870e 63 d_suck_sem.post();
dbcb3066 64 }
3e7dcee6 65
3696224d
BH
66}
67
d3ee36f2 68struct ZoneStatus
69{
70 bool isDnssecZone{false};
71 bool isPresigned{false};
72 bool isNSEC3 {false};
73 bool optOutFlag {false};
74 NSEC3PARAMRecordContent ns3pr;
75
76 bool isNarrow{false};
77 unsigned int soa_serial{0};
78 set<DNSName> nsset, qnames, secured;
79 uint32_t domain_id;
3e7dcee6 80 int numDeltas{0};
d3ee36f2 81};
82
83
3e7dcee6 84void CommunicatorClass::ixfrSuck(const DNSName &domain, const TSIGTriplet& tt, const ComboAddress& laddr, const ComboAddress& remote, scoped_ptr<AuthLua>& pdl,
85 ZoneStatus& zs, vector<DNSRecord>* axfr)
3696224d 86{
cd189f24 87 UeberBackend B; // fresh UeberBackend
88
89 DomainInfo di;
90 di.backend=0;
91 // bool transaction=false;
92 try {
93 DNSSECKeeper dk (&B); // reuse our UeberBackend copy for DNSSECKeeper
94
95 if(!B.getDomainInfo(domain, di) || !di.backend) { // di.backend and B are mostly identical
96 L<<Logger::Error<<"Can't determine backend for domain '"<<domain<<"'"<<endl;
97 return;
98 }
cd189f24 99
100 soatimes st;
101 memset(&st, 0, sizeof(st));
d3ee36f2 102 st.serial=di.serial;
cd189f24 103
104 DNSRecord dr;
12c06211 105 dr.d_content = std::make_shared<SOARecordContent>(g_rootdnsname, g_rootdnsname, st);
db8f9152 106 auto deltas = getIXFRDeltas(remote, domain, dr, tt, laddr.sin4.sin_family ? &laddr : 0, ((size_t) ::arg().asNum("xfr-max-received-mbytes")) * 1024 * 1024);
3e7dcee6 107 zs.numDeltas=deltas.size();
108 // cout<<"Got "<<deltas.size()<<" deltas from serial "<<di.serial<<", applying.."<<endl;
cd189f24 109
110 for(const auto& d : deltas) {
3e7dcee6 111 const auto& remove = d.first;
112 const auto& add = d.second;
113 // cout<<"Delta sizes: "<<remove.size()<<", "<<add.size()<<endl;
cd189f24 114
3e7dcee6 115 if(remove.empty()) { // we got passed an AXFR!
116 *axfr = add;
117 return;
118 }
119
120
cd189f24 121 // our hammer is 'replaceRRSet(domain_id, qname, qt, vector<DNSResourceRecord>& rrset)
3e7dcee6 122 // which thinks in terms of RRSETs
123 // however, IXFR does not, and removes and adds *records* (bummer)
124 // this means that we must group updates by {qname,qtype}, retrieve the RRSET, apply
125 // the add/remove updates, and replaceRRSet the whole thing.
cd189f24 126
3e7dcee6 127
128 map<pair<DNSName,uint16_t>, pair<vector<DNSRecord>, vector<DNSRecord> > > grouped;
129
130 for(const auto& x: remove)
131 grouped[{x.d_name, x.d_type}].first.push_back(x);
132 for(const auto& x: add)
133 grouped[{x.d_name, x.d_type}].second.push_back(x);
cd189f24 134
135 di.backend->startTransaction(domain, -1);
3e7dcee6 136 for(const auto g : grouped) {
137 DNSResourceRecord rr;
138 vector<DNSRecord> rrset;
139 B.lookup(QType(g.first.second), g.first.first, 0, di.id);
140 while(B.get(rr)) {
141 rrset.push_back(DNSRecord{rr});
142 }
143 // O(N^2)!
144 rrset.erase(remove_if(rrset.begin(), rrset.end(),
145 [&g](const DNSRecord& dr) {
146 return count(g.second.first.cbegin(),
147 g.second.first.cend(), dr);
148 }), rrset.end());
149 // the DNSRecord== operator compares on name, type, class and lowercase content representation
150
151 for(const auto& x : g.second.second) {
152 rrset.push_back(x);
153 }
cd189f24 154
3e7dcee6 155 vector<DNSResourceRecord> replacement;
156 for(const auto& x : rrset) {
157 DNSResourceRecord dr(x);
158 dr.qname += domain;
159 dr.domain_id = di.id;
160 if(x.d_type == QType::SOA) {
161 // cout<<"New SOA: "<<x.d_content->getZoneRepresentation()<<endl;
162 auto sr = getRR<SOARecordContent>(x);
163 zs.soa_serial=sr->d_st.serial;
164 }
165
166 replacement.push_back(dr);
167 }
d3ee36f2 168
3e7dcee6 169 di.backend->replaceRRSet(di.id, g.first.first+domain, QType(g.first.second), replacement);
170 }
cd189f24 171 di.backend->commitTransaction();
172 }
cd189f24 173 }
174 catch(std::exception& p) {
3e7dcee6 175 L<<Logger::Error<<"Got exception during IXFR: "<<p.what()<<endl;
176 throw;
cd189f24 177 }
178 catch(PDNSException& p) {
3e7dcee6 179 L<<Logger::Error<<"Got exception during IXFR: "<<p.reason<<endl;
180 throw;
181 }
182}
cd189f24 183
3e7dcee6 184
185static bool processRecordForZS(const DNSName& domain, bool& firstNSEC3, DNSResourceRecord& rr, ZoneStatus& zs)
186{
187 switch(rr.qtype.getCode()) {
188 case QType::NSEC3PARAM:
189 zs.ns3pr = NSEC3PARAMRecordContent(rr.content);
190 zs.isDnssecZone = zs.isNSEC3 = true;
191 zs.isNarrow = false;
e4fb8488 192 return false;
3e7dcee6 193 case QType::NSEC3: {
194 NSEC3RecordContent ns3rc(rr.content);
195 if (firstNSEC3) {
196 zs.isDnssecZone = zs.isPresigned = true;
197 firstNSEC3 = false;
198 } else if (zs.optOutFlag != (ns3rc.d_flags & 1))
199 throw PDNSException("Zones with a mixture of Opt-Out NSEC3 RRs and non-Opt-Out NSEC3 RRs are not supported.");
200 zs.optOutFlag = ns3rc.d_flags & 1;
201 if (ns3rc.d_set.count(QType::NS) && !(rr.qname==domain)) {
202 DNSName hashPart = DNSName(toLower(rr.qname.makeRelative(domain).toString()));
203 zs.secured.insert(hashPart);
204 }
e4fb8488 205 return false;
3e7dcee6 206 }
207
208 case QType::NSEC:
209 zs.isDnssecZone = zs.isPresigned = true;
e4fb8488 210 return false;
3e7dcee6 211
212 case QType::NS:
213 if(rr.qname!=domain)
214 zs.nsset.insert(rr.qname);
bd9602fa 215 break;
3e7dcee6 216 }
217
218 zs.qnames.insert(rr.qname);
219
220 rr.domain_id=zs.domain_id;
221 return true;
cd189f24 222}
223
d3ee36f2 224/* So this code does a number of things.
225 1) It will AXFR a domain from a master
226 The code can retrieve the current serial number in the database itself.
227 It may attempt an IXFR
228 2) It will filter the zone through a lua *filter* script
229 3) The code walks through the zone records do determine DNSSEC status (secured, nsec/nsec3, optout)
230 4) It inserts the zone into the database
231 With the right 'ordername' fields
232 5) It updates the Empty Non Terminals
233*/
cd189f24 234
d3ee36f2 235vector<DNSResourceRecord> doAxfr(const ComboAddress& raddr, const DNSName& domain, const TSIGTriplet& tt, const ComboAddress& laddr, scoped_ptr<AuthLua>& pdl, ZoneStatus& zs)
236{
237 vector<DNSResourceRecord> rrs;
db8f9152 238 AXFRRetriever retriever(raddr, domain, tt, (laddr.sin4.sin_family == 0) ? NULL : &laddr, ((size_t) ::arg().asNum("xfr-max-received-mbytes")) * 1024 * 1024);
d3ee36f2 239 Resolver::res_t recs;
240 bool first=true;
241 bool firstNSEC3{true};
242 bool soa_received {false};
243 while(retriever.getChunk(recs)) {
244 if(first) {
245 L<<Logger::Error<<"AXFR started for '"<<domain<<"'"<<endl;
246 first=false;
247 }
248
249 for(Resolver::res_t::iterator i=recs.begin();i!=recs.end();++i) {
250 if(i->qtype.getCode() == QType::OPT || i->qtype.getCode() == QType::TSIG) // ignore EDNS0 & TSIG
251 continue;
252
253 if(!i->qname.isPartOf(domain)) {
254 L<<Logger::Error<<"Remote "<<raddr.toStringWithPort()<<" tried to sneak in out-of-zone data '"<<i->qname<<"'|"<<i->qtype.getName()<<" during AXFR of zone '"<<domain<<"', ignoring"<<endl;
255 continue;
256 }
257
258 vector<DNSResourceRecord> out;
259 if(!pdl || !pdl->axfrfilter(raddr, domain, *i, out)) {
3e7dcee6 260 out.push_back(*i); // if axfrfilter didn't do anything, we put our record in 'out' ourselves
d3ee36f2 261 }
262
263 for(DNSResourceRecord& rr : out) {
3ce97f4a 264 if(!processRecordForZS(domain, firstNSEC3, rr, zs))
265 continue;
3e7dcee6 266 if(rr.qtype.getCode() == QType::SOA) {
d3ee36f2 267 if(soa_received)
268 continue; //skip the last SOA
269 SOAData sd;
270 fillSOAData(rr.content,sd);
271 zs.soa_serial = sd.serial;
272 soa_received = true;
d3ee36f2 273 }
274
d3ee36f2 275 rrs.push_back(rr);
3e7dcee6 276
d3ee36f2 277 }
278 }
279 }
280 return rrs;
281}
282
3e7dcee6 283
d3ee36f2 284void CommunicatorClass::suck(const DNSName &domain, const string &remote)
cd189f24 285{
3e7dcee6 286 {
287 Lock l(&d_lock);
288 if(d_inprogress.count(domain)) {
289 return;
290 }
291 d_inprogress.insert(domain);
292 }
293 RemoveSentinel rs(domain, this); // this removes us from d_inprogress when we go out of scope
294
f43c4448 295 L<<Logger::Error<<"Initiating transfer of '"<<domain<<"' from remote '"<<remote<<"'"<<endl;
295c4a00 296 UeberBackend B; // fresh UeberBackend
3696224d
BH
297
298 DomainInfo di;
299 di.backend=0;
376ec278 300 bool transaction=false;
3696224d 301 try {
295c4a00 302 DNSSECKeeper dk (&B); // reuse our UeberBackend copy for DNSSECKeeper
a1282cdd 303
295c4a00 304 if(!B.getDomainInfo(domain, di) || !di.backend) { // di.backend and B are mostly identical
f43c4448 305 L<<Logger::Error<<"Can't determine backend for domain '"<<domain<<"'"<<endl;
3696224d
BH
306 return;
307 }
d3ee36f2 308 ZoneStatus zs;
309 zs.domain_id=di.id;
53568203 310
98c9ec39 311 TSIGTriplet tt;
312 if(dk.getTSIGForAccess(domain, remote, &tt.name)) {
29b92d6f 313 string tsigsecret64;
98c9ec39 314 if(B.getTSIGKey(tt.name, &tt.algo, &tsigsecret64)) {
315 if(B64Decode(tsigsecret64, tt.secret)) {
316 L<<Logger::Error<<"Unable to Base-64 decode TSIG key '"<<tt.name<<"' for domain '"<<domain<<"' not found"<<endl;
317 return;
318 }
53568203 319 } else {
98c9ec39 320 L<<Logger::Error<<"TSIG key '"<<tt.name<<"' for domain '"<<domain<<"' not found"<<endl;
53568203 321 return;
a1467662 322 }
29b92d6f 323 }
53568203
KM
324
325
5704e107 326 scoped_ptr<AuthLua> pdl;
e23622a7 327 vector<string> scripts;
295c4a00 328 if(B.getDomainMetadata(domain, "LUA-AXFR-SCRIPT", scripts) && !scripts.empty()) {
b2aefbf9 329 try {
5704e107 330 pdl.reset(new AuthLua(scripts[0]));
f43c4448 331 L<<Logger::Info<<"Loaded Lua script '"<<scripts[0]<<"' to edit the incoming AXFR of '"<<domain<<"'"<<endl;
b2aefbf9
BH
332 }
333 catch(std::exception& e) {
f43c4448 334 L<<Logger::Error<<"Failed to load Lua editing script '"<<scripts[0]<<"' for incoming AXFR of '"<<domain<<"': "<<e.what()<<endl;
b2aefbf9
BH
335 return;
336 }
e23622a7 337 }
53568203 338
fc396d56
BH
339 vector<string> localaddr;
340 ComboAddress laddr;
edb693f0 341 ComboAddress raddr(remote, 53);
295c4a00 342 if(B.getDomainMetadata(domain, "AXFR-SOURCE", localaddr) && !localaddr.empty()) {
fc396d56
BH
343 try {
344 laddr = ComboAddress(localaddr[0]);
f43c4448 345 L<<Logger::Info<<"AXFR source for domain '"<<domain<<"' set to "<<localaddr[0]<<endl;
fc396d56
BH
346 }
347 catch(std::exception& e) {
f43c4448 348 L<<Logger::Error<<"Failed to load AXFR source '"<<localaddr[0]<<"' for incoming AXFR of '"<<domain<<"': "<<e.what()<<endl;
fc396d56
BH
349 return;
350 }
edb693f0 351 } else {
352 if(raddr.sin4.sin_family == AF_INET)
353 laddr=ComboAddress(::arg()["query-local-address"]);
354 else if(!::arg()["query-local-address6"].empty())
355 laddr=ComboAddress(::arg()["query-local-address6"]);
356 else
357 laddr.sin4.sin_family = 0;
8c949c52 358 }
fc396d56 359
53568203
KM
360 bool hadDnssecZone = false;
361 bool hadPresigned = false;
362 bool hadNSEC3 = false;
d3ee36f2 363 NSEC3PARAMRecordContent hadNs3pr;
364 bool hadNarrow=false;
365
edb693f0 366
3e7dcee6 367 vector<DNSResourceRecord> rrs;
53568203
KM
368 if(dk.isSecuredZone(domain)) {
369 hadDnssecZone=true;
370 hadPresigned=dk.isPresigned(domain);
d3ee36f2 371 if (dk.getNSEC3PARAM(domain, &zs.ns3pr, &zs.isNarrow)) {
53568203 372 hadNSEC3 = true;
d3ee36f2 373 hadNs3pr = zs.ns3pr;
374 hadNarrow = zs.isNarrow;
53568203
KM
375 }
376 }
d3ee36f2 377 else if(di.serial) {
378 vector<string> meta;
379 B.getDomainMetadata(domain, "IXFR", meta);
3e7dcee6 380 if(!meta.empty() && meta[0]=="1") {
381 vector<DNSRecord> axfr;
edb693f0 382 L<<Logger::Warning<<"Starting IXFR of '"<<domain<<"' from remote "<<raddr.toStringWithPort()<<endl;
3e7dcee6 383 ixfrSuck(domain, tt, laddr, raddr, pdl, zs, &axfr);
384 if(!axfr.empty()) {
385 L<<Logger::Warning<<"IXFR of '"<<domain<<"' from remote '"<<raddr.toStringWithPort()<<"' turned into an AXFR"<<endl;
386 bool firstNSEC3=true;
387 rrs.reserve(axfr.size());
388 for(const auto& dr : axfr) {
389 DNSResourceRecord rr(dr);
390 rr.qname += domain;
391 rr.domain_id = zs.domain_id;
3ce97f4a 392 if(!processRecordForZS(domain, firstNSEC3, rr, zs))
393 continue;
3e7dcee6 394 if(dr.d_type == QType::SOA) {
395 auto sd = getRR<SOARecordContent>(dr);
396 zs.soa_serial = sd->d_st.serial;
397 }
398 rrs.push_back(rr);
399 }
400 }
401 else {
402 L<<Logger::Warning<<"Done with IXFR of '"<<domain<<"' from remote '"<<remote<<"', got "<<zs.numDeltas<<" delta"<<addS(zs.numDeltas)<<", serial now "<<zs.soa_serial<<endl;
403 return;
404 }
405 }
3696224d 406 }
68fa524b 407
3e7dcee6 408 if(rrs.empty()) {
409 L<<Logger::Warning<<"Starting AXFR of '"<<domain<<"' from remote "<<raddr.toStringWithPort()<<endl;
410 rrs = doAxfr(raddr, domain, tt, laddr, pdl, zs);
411 L<<Logger::Warning<<"AXFR of '"<<domain<<"' from remote "<<raddr.toStringWithPort()<<" done"<<endl;
412 }
d3ee36f2 413
414 if(zs.isNSEC3) {
415 zs.ns3pr.d_flags = zs.optOutFlag ? 1 : 0;
53568203 416 }
ff473027 417
d3ee36f2 418 if(!zs.isPresigned) {
53568203
KM
419 DNSSECKeeper::keyset_t keys = dk.getKeys(domain);
420 if(!keys.empty()) {
d3ee36f2 421 zs.isDnssecZone = true;
422 zs.isNSEC3 = hadNSEC3;
423 zs.ns3pr = hadNs3pr;
424 zs.optOutFlag = (hadNs3pr.d_flags & 1);
425 zs.isNarrow = hadNarrow;
53568203
KM
426 }
427 }
428
d3ee36f2 429 if(zs.isDnssecZone) {
430 if(!zs.isNSEC3)
f9cf6d92 431 L<<Logger::Info<<"Adding NSEC ordering information"<<endl;
d3ee36f2 432 else if(!zs.isNarrow)
f43c4448 433 L<<Logger::Info<<"Adding NSEC3 hashed ordering information for '"<<domain<<"'"<<endl;
f9cf6d92
KM
434 else
435 L<<Logger::Info<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl;
436 }
437
b5baefaf 438
d3ee36f2 439 transaction=di.backend->startTransaction(domain, zs.domain_id);
3e7dcee6 440 L<<Logger::Error<<"Backend transaction started for '"<<domain<<"' storage"<<endl;
f9cf6d92 441
7f2aa497 442 // update the presigned flag and NSEC3PARAM
d3ee36f2 443 if (zs.isDnssecZone) {
53568203 444 // update presigned if there was a change
d3ee36f2 445 if (zs.isPresigned && !hadPresigned) {
7f2aa497
KM
446 // zone is now presigned
447 dk.setPresigned(domain);
d3ee36f2 448 } else if (hadPresigned && !zs.isPresigned) {
53568203
KM
449 // zone is no longer presigned
450 dk.unsetPresigned(domain);
7f2aa497 451 }
53568203 452 // update NSEC3PARAM
d3ee36f2 453 if (zs.isNSEC3) {
53568203 454 // zone is NSEC3, only update if there was a change
d3ee36f2 455 if (!hadNSEC3 || (hadNarrow != zs.isNarrow) ||
456 (zs.ns3pr.d_algorithm != hadNs3pr.d_algorithm) ||
457 (zs.ns3pr.d_flags != hadNs3pr.d_flags) ||
458 (zs.ns3pr.d_iterations != hadNs3pr.d_iterations) ||
459 (zs.ns3pr.d_salt != hadNs3pr.d_salt)) {
460 dk.setNSEC3PARAM(domain, zs.ns3pr, zs.isNarrow);
7f2aa497 461 }
53568203
KM
462 } else if (hadNSEC3 ) {
463 // zone is no longer NSEC3
464 dk.unsetNSEC3PARAM(domain);
465 }
466 } else if (hadDnssecZone) {
467 // zone is no longer signed
468 if (hadPresigned) {
469 // remove presigned
470 dk.unsetPresigned(domain);
471 }
472 if (hadNSEC3) {
473 // unset NSEC3PARAM
474 dk.unsetNSEC3PARAM(domain);
7f2aa497 475 }
7f2aa497
KM
476 }
477
f9cf6d92 478 bool doent=true;
b5baefaf 479 uint32_t maxent = ::arg().asNum("max-ent-entries");
7abbc40f
PD
480 string ordername;
481 DNSName shorter;
482 set<DNSName> rrterm;
483 map<DNSName,bool> nonterm;
b5baefaf 484
53568203 485
ef7cd021 486 for(DNSResourceRecord& rr : rrs) {
d3ee36f2 487 if(!zs.isPresigned) {
53568203
KM
488 if (rr.qtype.getCode() == QType::RRSIG)
489 continue;
d3ee36f2 490 if(zs.isDnssecZone && rr.qtype.getCode() == QType::DNSKEY && !::arg().mustDo("direct-dnskey"))
53568203
KM
491 continue;
492 }
493
f9cf6d92
KM
494 // Figure out auth and ents
495 rr.auth=true;
496 shorter=rr.qname;
497 rrterm.clear();
498 do {
499 if(doent) {
d3ee36f2 500 if (!zs.qnames.count(shorter))
f9cf6d92 501 rrterm.insert(shorter);
d7652f3a 502 }
d3ee36f2 503 if(zs.nsset.count(shorter) && rr.qtype.getCode() != QType::DS)
f9cf6d92 504 rr.auth=false;
e5c7239c 505
e325f20c 506 if (shorter==domain) // stop at apex
f9cf6d92 507 break;
7abbc40f 508 }while(shorter.chopOff());
b8adb30d 509
e5c7239c
KM
510 // Insert ents
511 if(doent && !rrterm.empty()) {
512 bool auth;
513 if (!rr.auth && rr.qtype.getCode() == QType::NS) {
d3ee36f2 514 if (zs.isNSEC3)
515 ordername=toBase32Hex(hashQNameWithSalt(zs.ns3pr, rr.qname));
516 auth=(!zs.isNSEC3 || !zs.optOutFlag || zs.secured.count(DNSName(ordername)));
e5c7239c
KM
517 } else
518 auth=rr.auth;
519
7abbc40f 520 for(const auto &nt: rrterm){
e5c7239c 521 if (!nonterm.count(nt))
7abbc40f 522 nonterm.insert(pair<DNSName, bool>(nt, auth));
e5c7239c
KM
523 else if (auth)
524 nonterm[nt]=true;
525 }
526
f9cf6d92 527 if(nonterm.size() > maxent) {
f43c4448 528 L<<Logger::Error<<"AXFR zone "<<domain<<" has too many empty non terminals."<<endl;
f9cf6d92
KM
529 nonterm.clear();
530 doent=false;
27045410 531 }
d7652f3a 532 }
f9cf6d92
KM
533
534 // RRSIG is always auth, even inside a delegation
535 if (rr.qtype.getCode() == QType::RRSIG)
536 rr.auth=true;
537
538 // Add ordername and insert record
d3ee36f2 539 if (zs.isDnssecZone && rr.qtype.getCode() != QType::RRSIG) {
540 if (zs.isNSEC3) {
f9cf6d92 541 // NSEC3
d3ee36f2 542 ordername=toBase32Hex(hashQNameWithSalt(zs.ns3pr, rr.qname));
543 if(!zs.isNarrow && (rr.auth || (rr.qtype.getCode() == QType::NS && (!zs.optOutFlag || zs.secured.count(DNSName(ordername)))))) {
f9cf6d92
KM
544 di.backend->feedRecord(rr, &ordername);
545 } else
546 di.backend->feedRecord(rr);
547 } else {
548 // NSEC
549 if (rr.auth || rr.qtype.getCode() == QType::NS) {
897e94f3 550 ordername=rr.qname.makeRelative(domain).makeLowerCase().labelReverse().toString(" ", false);
f9cf6d92
KM
551 di.backend->feedRecord(rr, &ordername);
552 } else
553 di.backend->feedRecord(rr);
554 }
555 } else
556 di.backend->feedRecord(rr);
d7652f3a 557 }
b5baefaf 558
f9cf6d92
KM
559 // Insert empty non-terminals
560 if(doent && !nonterm.empty()) {
d3ee36f2 561 if (zs.isNSEC3) {
562 di.backend->feedEnts3(zs.domain_id, domain, nonterm, zs.ns3pr, zs.isNarrow);
f9cf6d92 563 } else
d3ee36f2 564 di.backend->feedEnts(zs.domain_id, nonterm);
b5baefaf
PD
565 }
566
14b7e03b 567 di.backend->commitTransaction();
376ec278 568 transaction = false;
d3ee36f2 569 di.backend->setFresh(zs.domain_id);
7abbc40f 570 PC.purge(domain.toString()+"$");
14b7e03b 571
a1282cdd 572
d3ee36f2 573 L<<Logger::Error<<"AXFR done for '"<<domain<<"', zone committed with serial number "<<zs.soa_serial<<endl;
8de9c054
BH
574 if(::arg().mustDo("slave-renotify"))
575 notifyDomain(domain);
3696224d
BH
576 }
577 catch(DBException &re) {
290a083d 578 L<<Logger::Error<<"Unable to feed record during incoming AXFR of '" << domain<<"': "<<re.reason<<endl;
376ec278 579 if(di.backend && transaction) {
3696224d
BH
580 L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
581 di.backend->abortTransaction();
582 }
583 }
75ccb5b9 584 catch(MOADNSException &re) {
290a083d 585 L<<Logger::Error<<"Unable to parse record during incoming AXFR of '"<<domain<<"' (MOADNSException): "<<re.what()<<endl;
376ec278 586 if(di.backend && transaction) {
2714db17
BH
587 L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
588 di.backend->abortTransaction();
589 }
590 }
591 catch(std::exception &re) {
290a083d 592 L<<Logger::Error<<"Unable to parse record during incoming AXFR of '"<<domain<<"' (std::exception): "<<re.what()<<endl;
376ec278 593 if(di.backend && transaction) {
75ccb5b9
BH
594 L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
595 di.backend->abortTransaction();
596 }
597 }
3696224d 598 catch(ResolverException &re) {
290a083d 599 L<<Logger::Error<<"Unable to AXFR zone '"<<domain<<"' from remote '"<<remote<<"' (resolver): "<<re.reason<<endl;
376ec278 600 if(di.backend && transaction) {
3696224d 601 L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
7f450125
PD
602 di.backend->abortTransaction();
603 }
604 }
3f81d239 605 catch(PDNSException &ae) {
290a083d 606 L<<Logger::Error<<"Unable to AXFR zone '"<<domain<<"' from remote '"<<remote<<"' (PDNSException): "<<ae.reason<<endl;
376ec278 607 if(di.backend && transaction) {
7f450125 608 L<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
3696224d
BH
609 di.backend->abortTransaction();
610 }
611 }
612}
7597b3cf 613namespace {
3696224d 614struct QueryInfo
7597b3cf
BH
615{
616 struct timeval query_ttd;
617 uint16_t id;
618};
619
620struct DomainNotificationInfo
621{
622 DomainInfo di;
623 bool dnssecOk;
95302209 624 ComboAddress localaddr;
561434a6
PD
625 DNSName tsigkeyname, tsigalgname;
626 string tsigsecret;
7597b3cf
BH
627};
628}
629
3696224d
BH
630
631struct SlaveSenderReceiver
632{
561434a6 633 typedef pair<DNSName, uint16_t> Identifier;
53568203 634
54b2edb4
BH
635 struct Answer {
636 uint32_t theirSerial;
637 uint32_t theirInception;
638 uint32_t theirExpire;
639 };
53568203 640
54b2edb4 641 map<uint32_t, Answer> d_freshness;
53568203 642
3696224d
BH
643 SlaveSenderReceiver()
644 {
3696224d 645 }
53568203 646
c0a5fc34 647 void deliverTimeout(const Identifier& i)
0c01dd7c
BH
648 {
649 }
53568203 650
7597b3cf 651 Identifier send(DomainNotificationInfo& dni)
3696224d 652 {
7597b3cf 653 random_shuffle(dni.di.masters.begin(), dni.di.masters.end());
0c01dd7c 654 try {
7597b3cf 655 ComboAddress remote(*dni.di.masters.begin());
95302209 656 if (dni.localaddr.sin4.sin_family == 0) {
53568203
KM
657 return make_pair(dni.di.zone,
658 d_resolver.sendResolve(ComboAddress(*dni.di.masters.begin(), 53),
7abbc40f 659 dni.di.zone,
53568203 660 QType::SOA,
95302209
AT
661 dni.dnssecOk, dni.tsigkeyname, dni.tsigalgname, dni.tsigsecret)
662 );
663 } else {
664 return make_pair(dni.di.zone,
665 d_resolver.sendResolve(ComboAddress(*dni.di.masters.begin(), 53), dni.localaddr,
7abbc40f 666 dni.di.zone,
95302209
AT
667 QType::SOA,
668 dni.dnssecOk, dni.tsigkeyname, dni.tsigalgname, dni.tsigsecret)
669 );
670 }
0c01dd7c 671 }
3f81d239 672 catch(PDNSException& e) {
7abbc40f 673 throw runtime_error("While attempting to query freshness of '"+dni.di.zone.toString()+"': "+e.reason);
0c01dd7c 674 }
3696224d 675 }
68dae32c 676
3696224d
BH
677 bool receive(Identifier& id, Answer& a)
678 {
54b2edb4 679 if(d_resolver.tryGetSOASerial(&id.first, &a.theirSerial, &a.theirInception, &a.theirExpire, &id.second)) {
3696224d
BH
680 return 1;
681 }
682 return 0;
683 }
68dae32c 684
7597b3cf 685 void deliverAnswer(DomainNotificationInfo& dni, const Answer& a, unsigned int usec)
3696224d 686 {
7597b3cf 687 d_freshness[dni.di.id]=a;
3696224d 688 }
68dae32c 689
3696224d 690 Resolver d_resolver;
3696224d
BH
691};
692
7f3d870e
BH
693void CommunicatorClass::addSlaveCheckRequest(const DomainInfo& di, const ComboAddress& remote)
694{
695 Lock l(&d_lock);
90c9a70e
BH
696 DomainInfo ours = di;
697 ours.backend = 0;
698 d_tocheck.insert(ours);
7f3d870e
BH
699 d_any_sem.post(); // kick the loop!
700}
701
7108e055
PD
702void CommunicatorClass::addTrySuperMasterRequest(DNSPacket *p)
703{
704 Lock l(&d_lock);
705 DNSPacket ours = *p;
706 d_potentialsupermasters.push_back(ours);
707 d_any_sem.post(); // kick the loop!
708}
709
3696224d
BH
710void CommunicatorClass::slaveRefresh(PacketHandler *P)
711{
56a4f068
AT
712 // not unless we are slave
713 if (!::arg().mustDo("slave")) return;
714
3971cf53 715 UeberBackend *B=P->getBackend();
16e654fa 716 vector<DomainInfo> rdomains;
3e7dcee6 717 vector<DomainNotificationInfo> sdomains;
7108e055 718 vector<DNSPacket> trysuperdomains;
68dae32c 719
7f3d870e
BH
720 {
721 Lock l(&d_lock);
722 rdomains.insert(rdomains.end(), d_tocheck.begin(), d_tocheck.end());
723 d_tocheck.clear();
7108e055
PD
724 trysuperdomains.insert(trysuperdomains.end(), d_potentialsupermasters.begin(), d_potentialsupermasters.end());
725 d_potentialsupermasters.clear();
7f3d870e 726 }
68dae32c 727
ef7cd021 728 for(DNSPacket& dp : trysuperdomains) {
7731e32c
AT
729 // get the TSIG key name
730 TSIGRecordContent trc;
731 DNSName tsigkeyname;
732 string message;
733 dp.getTSIGDetails(&trc, &tsigkeyname, &message);
7108e055 734 int res;
7731e32c 735 res=P->trySuperMasterSynchronous(&dp, tsigkeyname);
7108e055
PD
736 if(res>=0) {
737 DNSPacket *r=dp.replyPacket();
738 r->setRcode(res);
739 r->setOpcode(Opcode::Notify);
740 N->send(r);
741 delete r;
742 }
743 }
3e7dcee6 744 if(rdomains.empty()) { // if we have priority domains, check them first
7f3d870e 745 B->getUnfreshSlaveInfos(&rdomains);
3e7dcee6 746 }
936eb34a 747 DNSSECKeeper dk(B); // NOW HEAR THIS! This DK uses our B backend, so no interleaved access!
dbcb3066
BH
748 {
749 Lock l(&d_lock);
dbcb3066
BH
750 domains_by_name_t& nameindex=boost::multi_index::get<IDTag>(d_suckdomains);
751
ef7cd021 752 for(DomainInfo& di : rdomains) {
95302209 753 std::vector<std::string> localaddr;
dbcb3066
BH
754 SuckRequest sr;
755 sr.domain=di.zone;
756 if(di.masters.empty()) // slave domains w/o masters are ignored
757 continue;
758 // remove unfresh domains already queued for AXFR, no sense polling them again
759 sr.master=*di.masters.begin();
3e7dcee6 760 if(nameindex.count(sr)) { // this does NOT however protect us against AXFRs already in progress!
dbcb3066 761 continue;
a71bee29 762 }
3e7dcee6 763 if(d_inprogress.count(sr.domain)) // this does
764 continue;
765
7597b3cf
BH
766 DomainNotificationInfo dni;
767 dni.di=di;
768 dni.dnssecOk = dk.isPresigned(di.zone);
68dae32c 769
7597b3cf
BH
770 if(dk.getTSIGForAccess(di.zone, sr.master, &dni.tsigkeyname)) {
771 string secret64;
772 B->getTSIGKey(dni.tsigkeyname, &dni.tsigalgname, &secret64);
773 B64Decode(secret64, dni.tsigsecret);
774 }
95302209
AT
775
776 localaddr.clear();
777 // check for AXFR-SOURCE
778 if(B->getDomainMetadata(di.zone, "AXFR-SOURCE", localaddr) && !localaddr.empty()) {
779 try {
780 dni.localaddr = ComboAddress(localaddr[0]);
781 L<<Logger::Info<<"Freshness check source (AXFR-SOURCE) for domain '"<<di.zone<<"' set to "<<localaddr[0]<<endl;
782 }
783 catch(std::exception& e) {
784 L<<Logger::Error<<"Failed to load freshness check source '"<<localaddr[0]<<"' for '"<<di.zone<<"': "<<e.what()<<endl;
785 return;
786 }
787 } else {
788 dni.localaddr.sin4.sin_family = 0;
789 }
790
7597b3cf 791 sdomains.push_back(dni);
dbcb3066 792 }
dbcb3066 793 }
3696224d
BH
794 if(sdomains.empty())
795 {
dbcb3066
BH
796 if(d_slaveschanged) {
797 Lock l(&d_lock);
3e7dcee6 798 L<<Logger::Warning<<"No new unfresh slave domains, "<<d_suckdomains.size()<<" queued for AXFR already, "<<d_inprogress.size()<<" in progress"<<endl;
dbcb3066
BH
799 }
800 d_slaveschanged = !rdomains.empty();
3696224d
BH
801 return;
802 }
dbcb3066
BH
803 else {
804 Lock l(&d_lock);
3696224d
BH
805 L<<Logger::Warning<<sdomains.size()<<" slave domain"<<(sdomains.size()>1 ? "s" : "")<<" need"<<
806 (sdomains.size()>1 ? "" : "s")<<
dbcb3066
BH
807 " checking, "<<d_suckdomains.size()<<" queued for AXFR"<<endl;
808 }
68dae32c 809
3696224d 810 SlaveSenderReceiver ssr;
68dae32c 811
7597b3cf 812 Inflighter<vector<DomainNotificationInfo>, SlaveSenderReceiver> ifl(sdomains, ssr);
68dae32c 813
3696224d
BH
814 ifl.d_maxInFlight = 200;
815
816 for(;;) {
817 try {
818 ifl.run();
819 break;
820 }
dbcb3066 821 catch(std::exception& e) {
3696224d
BH
822 L<<Logger::Error<<"While checking domain freshness: " << e.what()<<endl;
823 }
68dae32c 824 catch(PDNSException &re) {
3696224d
BH
825 L<<Logger::Error<<"While checking domain freshness: " << re.reason<<endl;
826 }
827 }
3e7dcee6 828 L<<Logger::Warning<<"Received serial number updates for "<<ssr.d_freshness.size()<<" zone"<<addS(ssr.d_freshness.size())<<", had "<<ifl.getTimeouts()<<" timeout"<<addS(ifl.getTimeouts())<<endl;
16e654fa 829
7597b3cf 830 typedef DomainNotificationInfo val_t;
ef7cd021 831 for(val_t& val : sdomains) {
7597b3cf 832 DomainInfo& di(val.di);
f9fa0e2d
PD
833 // might've come from the packethandler
834 if(!di.backend && !B->getDomainInfo(di.zone, di)) {
f43c4448 835 L<<Logger::Warning<<"Ignore domain "<< di.zone<<" since it has been removed from our backend"<<endl;
232f0877 836 continue;
f9fa0e2d 837 }
68dae32c 838
3e7dcee6 839 if(!ssr.d_freshness.count(di.id)) // what does this mean? XXX
3696224d 840 continue;
54b2edb4 841 uint32_t theirserial = ssr.d_freshness[di.id].theirSerial, ourserial = di.serial;
68dae32c 842
da1a6059 843 if(rfc1982LessThan(theirserial, ourserial) && ourserial != 0) {
8e90e428 844 L<<Logger::Error<<"Domain '"<<di.zone<<"' more recent than master, our serial " << ourserial << " > their serial "<< theirserial << endl;
3696224d
BH
845 di.backend->setFresh(di.id);
846 }
847 else if(theirserial == ourserial) {
54b2edb4 848 if(!dk.isPresigned(di.zone)) {
1d3fae1b 849 L<<Logger::Info<<"Domain '"<< di.zone<<"' is fresh (not presigned, no RRSIG check)"<<endl;
54b2edb4
BH
850 di.backend->setFresh(di.id);
851 }
852 else {
936eb34a 853 B->lookup(QType(QType::RRSIG), di.zone); // can't use DK before we are done with this lookup!
54b2edb4
BH
854 DNSResourceRecord rr;
855 uint32_t maxExpire=0, maxInception=0;
856 while(B->get(rr)) {
857 RRSIGRecordContent rrc(rr.content);
858 if(rrc.d_type == QType::SOA) {
859 maxInception = std::max(maxInception, rrc.d_siginception);
860 maxExpire = std::max(maxExpire, rrc.d_sigexpire);
861 }
862 }
863 if(maxInception == ssr.d_freshness[di.id].theirInception && maxExpire == ssr.d_freshness[di.id].theirExpire) {
f43c4448 864 L<<Logger::Info<<"Domain '"<< di.zone<<"' is fresh and apex RRSIGs match"<<endl;
54b2edb4
BH
865 di.backend->setFresh(di.id);
866 }
867 else {
8e90e428 868 L<<Logger::Warning<<"Domain '"<< di.zone<<"' is fresh, but RRSIGS differ, so DNSSEC stale"<<endl;
d3ee36f2 869 addSuckRequest(di.zone, *di.masters.begin());
54b2edb4
BH
870 }
871 }
3696224d
BH
872 }
873 else {
8e90e428 874 L<<Logger::Warning<<"Domain '"<< di.zone<<"' is stale, master serial "<<theirserial<<", our serial "<< ourserial <<endl;
d3ee36f2 875 addSuckRequest(di.zone, *di.masters.begin());
3696224d
BH
876 }
877 }
68dae32c 878}
3696224d 879
bd53ea9d
PD
880// stub for PowerDNSLua linking
881int directResolve(const std::string& qname, const QType& qtype, int qclass, vector<DNSResourceRecord>& ret)
882{
883 return -1;
884}
885
886