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