]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/slavecommunicator.cc
Merge pull request #7940 from omoerbeek/include-branch-in-cherry-pick-name
[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
d622042f 52void CommunicatorClass::addSuckRequest(const DNSName &domain, const ComboAddress& 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
e6a9dde5 95 g_log<<Logger::Error<<"Can't determine backend for domain '"<<domain<<"'"<<endl;
cd189f24 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;
668624c8 139 B.lookup(QType(g.first.second), g.first.first+domain, 0, di.id);
abcd36a1 140 while(B.get(zrr)) {
668624c8 141 zrr.dr.d_name.makeUsRelative(domain);
abcd36a1
CH
142 rrset.push_back(zrr.dr);
143 }
3e7dcee6 144 }
145 // O(N^2)!
146 rrset.erase(remove_if(rrset.begin(), rrset.end(),
147 [&g](const DNSRecord& dr) {
148 return count(g.second.first.cbegin(),
149 g.second.first.cend(), dr);
150 }), rrset.end());
151 // the DNSRecord== operator compares on name, type, class and lowercase content representation
152
153 for(const auto& x : g.second.second) {
154 rrset.push_back(x);
155 }
cd189f24 156
3e7dcee6 157 vector<DNSResourceRecord> replacement;
4950b140
CH
158 for(const auto& dr : rrset) {
159 auto rr = DNSResourceRecord::fromWire(dr);
160 rr.qname += domain;
161 rr.domain_id = di.id;
162 if(dr.d_type == QType::SOA) {
3e7dcee6 163 // cout<<"New SOA: "<<x.d_content->getZoneRepresentation()<<endl;
4950b140 164 auto sr = getRR<SOARecordContent>(dr);
3e7dcee6 165 zs.soa_serial=sr->d_st.serial;
166 }
167
4950b140 168 replacement.push_back(rr);
3e7dcee6 169 }
d3ee36f2 170
3e7dcee6 171 di.backend->replaceRRSet(di.id, g.first.first+domain, QType(g.first.second), replacement);
172 }
cd189f24 173 di.backend->commitTransaction();
174 }
cd189f24 175 }
176 catch(std::exception& p) {
e6a9dde5 177 g_log<<Logger::Error<<"Got exception during IXFR: "<<p.what()<<endl;
3e7dcee6 178 throw;
cd189f24 179 }
180 catch(PDNSException& p) {
e6a9dde5 181 g_log<<Logger::Error<<"Got exception during IXFR: "<<p.reason<<endl;
3e7dcee6 182 throw;
183 }
184}
cd189f24 185
3e7dcee6 186
187static bool processRecordForZS(const DNSName& domain, bool& firstNSEC3, DNSResourceRecord& rr, ZoneStatus& zs)
188{
189 switch(rr.qtype.getCode()) {
190 case QType::NSEC3PARAM:
191 zs.ns3pr = NSEC3PARAMRecordContent(rr.content);
192 zs.isDnssecZone = zs.isNSEC3 = true;
193 zs.isNarrow = false;
e4fb8488 194 return false;
3e7dcee6 195 case QType::NSEC3: {
196 NSEC3RecordContent ns3rc(rr.content);
197 if (firstNSEC3) {
198 zs.isDnssecZone = zs.isPresigned = true;
199 firstNSEC3 = false;
200 } else if (zs.optOutFlag != (ns3rc.d_flags & 1))
201 throw PDNSException("Zones with a mixture of Opt-Out NSEC3 RRs and non-Opt-Out NSEC3 RRs are not supported.");
202 zs.optOutFlag = ns3rc.d_flags & 1;
27d4a65b 203 if (ns3rc.isSet(QType::NS) && !(rr.qname==domain)) {
92c6c467 204 DNSName hashPart = rr.qname.makeRelative(domain);
3e7dcee6 205 zs.secured.insert(hashPart);
206 }
e4fb8488 207 return false;
3e7dcee6 208 }
209
210 case QType::NSEC:
211 zs.isDnssecZone = zs.isPresigned = true;
e4fb8488 212 return false;
3e7dcee6 213
214 case QType::NS:
215 if(rr.qname!=domain)
216 zs.nsset.insert(rr.qname);
bd9602fa 217 break;
3e7dcee6 218 }
219
220 zs.qnames.insert(rr.qname);
221
222 rr.domain_id=zs.domain_id;
223 return true;
cd189f24 224}
225
d3ee36f2 226/* So this code does a number of things.
227 1) It will AXFR a domain from a master
228 The code can retrieve the current serial number in the database itself.
229 It may attempt an IXFR
230 2) It will filter the zone through a lua *filter* script
231 3) The code walks through the zone records do determine DNSSEC status (secured, nsec/nsec3, optout)
232 4) It inserts the zone into the database
233 With the right 'ordername' fields
234 5) It updates the Empty Non Terminals
235*/
cd189f24 236
18b433ac 237static vector<DNSResourceRecord> doAxfr(const ComboAddress& raddr, const DNSName& domain, const TSIGTriplet& tt, const ComboAddress& laddr, scoped_ptr<AuthLua4>& pdl, ZoneStatus& zs)
d3ee36f2 238{
239 vector<DNSResourceRecord> rrs;
db8f9152 240 AXFRRetriever retriever(raddr, domain, tt, (laddr.sin4.sin_family == 0) ? NULL : &laddr, ((size_t) ::arg().asNum("xfr-max-received-mbytes")) * 1024 * 1024);
d3ee36f2 241 Resolver::res_t recs;
242 bool first=true;
243 bool firstNSEC3{true};
244 bool soa_received {false};
245 while(retriever.getChunk(recs)) {
246 if(first) {
e6a9dde5 247 g_log<<Logger::Error<<"AXFR started for '"<<domain<<"'"<<endl;
d3ee36f2 248 first=false;
249 }
250
251 for(Resolver::res_t::iterator i=recs.begin();i!=recs.end();++i) {
92c6c467 252 i->qname.makeUsLowerCase();
d3ee36f2 253 if(i->qtype.getCode() == QType::OPT || i->qtype.getCode() == QType::TSIG) // ignore EDNS0 & TSIG
254 continue;
255
256 if(!i->qname.isPartOf(domain)) {
e6a9dde5 257 g_log<<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;
d3ee36f2 258 continue;
259 }
260
261 vector<DNSResourceRecord> out;
262 if(!pdl || !pdl->axfrfilter(raddr, domain, *i, out)) {
3e7dcee6 263 out.push_back(*i); // if axfrfilter didn't do anything, we put our record in 'out' ourselves
d3ee36f2 264 }
265
266 for(DNSResourceRecord& rr : out) {
50b867eb 267 if(!rr.qname.isPartOf(domain)) {
e6a9dde5 268 g_log<<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;
50b867eb
KM
269 continue;
270 }
3ce97f4a 271 if(!processRecordForZS(domain, firstNSEC3, rr, zs))
272 continue;
3e7dcee6 273 if(rr.qtype.getCode() == QType::SOA) {
d3ee36f2 274 if(soa_received)
275 continue; //skip the last SOA
276 SOAData sd;
277 fillSOAData(rr.content,sd);
278 zs.soa_serial = sd.serial;
279 soa_received = true;
d3ee36f2 280 }
281
d3ee36f2 282 rrs.push_back(rr);
3e7dcee6 283
d3ee36f2 284 }
285 }
286 }
287 return rrs;
288}
289
3e7dcee6 290
d622042f 291void CommunicatorClass::suck(const DNSName &domain, const ComboAddress& remote)
cd189f24 292{
3e7dcee6 293 {
294 Lock l(&d_lock);
295 if(d_inprogress.count(domain)) {
296 return;
297 }
298 d_inprogress.insert(domain);
299 }
300 RemoveSentinel rs(domain, this); // this removes us from d_inprogress when we go out of scope
301
9b0f144f 302 g_log<<Logger::Error<<"Initiating transfer of '"<<domain<<"' from remote '"<<remote<<"'"<<endl;
295c4a00 303 UeberBackend B; // fresh UeberBackend
3696224d
BH
304
305 DomainInfo di;
306 di.backend=0;
376ec278 307 bool transaction=false;
3696224d 308 try {
295c4a00 309 DNSSECKeeper dk (&B); // reuse our UeberBackend copy for DNSSECKeeper
a1282cdd 310
889500d7 311 if(!B.getDomainInfo(domain, di) || !di.backend || di.kind != DomainInfo::Slave) { // di.backend and B are mostly identical
e6a9dde5 312 g_log<<Logger::Error<<"Can't determine backend for domain '"<<domain<<"'"<<endl;
3696224d
BH
313 return;
314 }
d3ee36f2 315 ZoneStatus zs;
316 zs.domain_id=di.id;
53568203 317
98c9ec39 318 TSIGTriplet tt;
319 if(dk.getTSIGForAccess(domain, remote, &tt.name)) {
29b92d6f 320 string tsigsecret64;
98c9ec39 321 if(B.getTSIGKey(tt.name, &tt.algo, &tsigsecret64)) {
322 if(B64Decode(tsigsecret64, tt.secret)) {
e6a9dde5 323 g_log<<Logger::Error<<"Unable to Base-64 decode TSIG key '"<<tt.name<<"' for domain '"<<domain<<"' not found"<<endl;
98c9ec39 324 return;
325 }
53568203 326 } else {
e6a9dde5 327 g_log<<Logger::Error<<"TSIG key '"<<tt.name<<"' for domain '"<<domain<<"' not found"<<endl;
53568203 328 return;
a1467662 329 }
29b92d6f 330 }
53568203
KM
331
332
18b433ac 333 scoped_ptr<AuthLua4> pdl;
e23622a7 334 vector<string> scripts;
86177332 335 string script=::arg()["lua-axfr-script"];
295c4a00 336 if(B.getDomainMetadata(domain, "LUA-AXFR-SCRIPT", scripts) && !scripts.empty()) {
86177332
KM
337 if (pdns_iequals(scripts[0], "NONE")) {
338 script.clear();
339 } else {
340 script=scripts[0];
341 }
342 }
343 if(!script.empty()){
b2aefbf9 344 try {
7c99293d
AT
345 pdl.reset(new AuthLua4());
346 pdl->loadFile(script);
e6a9dde5 347 g_log<<Logger::Info<<"Loaded Lua script '"<<script<<"' to edit the incoming AXFR of '"<<domain<<"'"<<endl;
b2aefbf9
BH
348 }
349 catch(std::exception& e) {
e6a9dde5 350 g_log<<Logger::Error<<"Failed to load Lua editing script '"<<script<<"' for incoming AXFR of '"<<domain<<"': "<<e.what()<<endl;
b2aefbf9
BH
351 return;
352 }
e23622a7 353 }
53568203 354
fc396d56
BH
355 vector<string> localaddr;
356 ComboAddress laddr;
d622042f 357
295c4a00 358 if(B.getDomainMetadata(domain, "AXFR-SOURCE", localaddr) && !localaddr.empty()) {
fc396d56
BH
359 try {
360 laddr = ComboAddress(localaddr[0]);
e6a9dde5 361 g_log<<Logger::Info<<"AXFR source for domain '"<<domain<<"' set to "<<localaddr[0]<<endl;
fc396d56
BH
362 }
363 catch(std::exception& e) {
e6a9dde5 364 g_log<<Logger::Error<<"Failed to load AXFR source '"<<localaddr[0]<<"' for incoming AXFR of '"<<domain<<"': "<<e.what()<<endl;
fc396d56
BH
365 return;
366 }
9d36589a 367 } else {
d622042f 368 if(remote.sin4.sin_family == AF_INET && !::arg()["query-local-address"].empty()) {
9d36589a 369 laddr = ComboAddress(::arg()["query-local-address"]);
d622042f 370 } else if(remote.sin4.sin_family == AF_INET6 && !::arg()["query-local-address6"].empty()) {
9d36589a
PL
371 laddr = ComboAddress(::arg()["query-local-address6"]);
372 } else {
d622042f 373 bool isv6 = remote.sin4.sin_family == AF_INET6;
e6a9dde5 374 g_log<<Logger::Error<<"Unable to AXFR, destination address is IPv" << (isv6 ? "6" : "4") << ", but query-local-address"<< (isv6 ? "6" : "") << " is unset!"<<endl;
9d36589a
PL
375 return;
376 }
8c949c52 377 }
fc396d56 378
53568203
KM
379 bool hadDnssecZone = false;
380 bool hadPresigned = false;
381 bool hadNSEC3 = false;
d3ee36f2 382 NSEC3PARAMRecordContent hadNs3pr;
383 bool hadNarrow=false;
384
edb693f0 385
3e7dcee6 386 vector<DNSResourceRecord> rrs;
53568203
KM
387 if(dk.isSecuredZone(domain)) {
388 hadDnssecZone=true;
389 hadPresigned=dk.isPresigned(domain);
d3ee36f2 390 if (dk.getNSEC3PARAM(domain, &zs.ns3pr, &zs.isNarrow)) {
53568203 391 hadNSEC3 = true;
d3ee36f2 392 hadNs3pr = zs.ns3pr;
393 hadNarrow = zs.isNarrow;
53568203
KM
394 }
395 }
d3ee36f2 396 else if(di.serial) {
397 vector<string> meta;
398 B.getDomainMetadata(domain, "IXFR", meta);
3e7dcee6 399 if(!meta.empty() && meta[0]=="1") {
400 vector<DNSRecord> axfr;
9b0f144f 401 g_log<<Logger::Warning<<"Starting IXFR of '"<<domain<<"' from remote "<<remote<<endl;
d622042f 402 ixfrSuck(domain, tt, laddr, remote, pdl, zs, &axfr);
3e7dcee6 403 if(!axfr.empty()) {
9b0f144f 404 g_log<<Logger::Warning<<"IXFR of '"<<domain<<"' from remote '"<<remote<<"' turned into an AXFR"<<endl;
3e7dcee6 405 bool firstNSEC3=true;
406 rrs.reserve(axfr.size());
407 for(const auto& dr : axfr) {
4950b140 408 auto rr = DNSResourceRecord::fromWire(dr);
92c6c467 409 (rr.qname += domain).makeUsLowerCase();
3e7dcee6 410 rr.domain_id = zs.domain_id;
3ce97f4a 411 if(!processRecordForZS(domain, firstNSEC3, rr, zs))
412 continue;
3e7dcee6 413 if(dr.d_type == QType::SOA) {
414 auto sd = getRR<SOARecordContent>(dr);
415 zs.soa_serial = sd->d_st.serial;
416 }
417 rrs.push_back(rr);
418 }
419 }
420 else {
e6a9dde5 421 g_log<<Logger::Warning<<"Done with IXFR of '"<<domain<<"' from remote '"<<remote<<"', got "<<zs.numDeltas<<" delta"<<addS(zs.numDeltas)<<", serial now "<<zs.soa_serial<<endl;
1d0995bc 422 purgeAuthCaches(domain.toString()+"$");
3e7dcee6 423 return;
424 }
425 }
3696224d 426 }
68fa524b 427
3e7dcee6 428 if(rrs.empty()) {
9b0f144f 429 g_log<<Logger::Warning<<"Starting AXFR of '"<<domain<<"' from remote "<<remote<<endl;
d622042f 430 rrs = doAxfr(remote, domain, tt, laddr, pdl, zs);
9b0f144f 431 g_log<<Logger::Warning<<"AXFR of '"<<domain<<"' from remote "<<remote<<" done"<<endl;
3e7dcee6 432 }
d3ee36f2 433
434 if(zs.isNSEC3) {
435 zs.ns3pr.d_flags = zs.optOutFlag ? 1 : 0;
53568203 436 }
ff473027 437
d3ee36f2 438 if(!zs.isPresigned) {
53568203
KM
439 DNSSECKeeper::keyset_t keys = dk.getKeys(domain);
440 if(!keys.empty()) {
d3ee36f2 441 zs.isDnssecZone = true;
442 zs.isNSEC3 = hadNSEC3;
443 zs.ns3pr = hadNs3pr;
444 zs.optOutFlag = (hadNs3pr.d_flags & 1);
445 zs.isNarrow = hadNarrow;
53568203
KM
446 }
447 }
448
d3ee36f2 449 if(zs.isDnssecZone) {
450 if(!zs.isNSEC3)
e6a9dde5 451 g_log<<Logger::Info<<"Adding NSEC ordering information"<<endl;
d3ee36f2 452 else if(!zs.isNarrow)
e6a9dde5 453 g_log<<Logger::Info<<"Adding NSEC3 hashed ordering information for '"<<domain<<"'"<<endl;
f9cf6d92 454 else
e6a9dde5 455 g_log<<Logger::Info<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl;
f9cf6d92
KM
456 }
457
b5baefaf 458
d3ee36f2 459 transaction=di.backend->startTransaction(domain, zs.domain_id);
e6a9dde5 460 g_log<<Logger::Error<<"Backend transaction started for '"<<domain<<"' storage"<<endl;
f9cf6d92 461
7f2aa497 462 // update the presigned flag and NSEC3PARAM
d3ee36f2 463 if (zs.isDnssecZone) {
53568203 464 // update presigned if there was a change
d3ee36f2 465 if (zs.isPresigned && !hadPresigned) {
7f2aa497
KM
466 // zone is now presigned
467 dk.setPresigned(domain);
d3ee36f2 468 } else if (hadPresigned && !zs.isPresigned) {
53568203
KM
469 // zone is no longer presigned
470 dk.unsetPresigned(domain);
7f2aa497 471 }
53568203 472 // update NSEC3PARAM
d3ee36f2 473 if (zs.isNSEC3) {
53568203 474 // zone is NSEC3, only update if there was a change
d3ee36f2 475 if (!hadNSEC3 || (hadNarrow != zs.isNarrow) ||
476 (zs.ns3pr.d_algorithm != hadNs3pr.d_algorithm) ||
477 (zs.ns3pr.d_flags != hadNs3pr.d_flags) ||
478 (zs.ns3pr.d_iterations != hadNs3pr.d_iterations) ||
479 (zs.ns3pr.d_salt != hadNs3pr.d_salt)) {
480 dk.setNSEC3PARAM(domain, zs.ns3pr, zs.isNarrow);
7f2aa497 481 }
53568203
KM
482 } else if (hadNSEC3 ) {
483 // zone is no longer NSEC3
484 dk.unsetNSEC3PARAM(domain);
485 }
486 } else if (hadDnssecZone) {
487 // zone is no longer signed
488 if (hadPresigned) {
489 // remove presigned
490 dk.unsetPresigned(domain);
491 }
492 if (hadNSEC3) {
493 // unset NSEC3PARAM
494 dk.unsetNSEC3PARAM(domain);
7f2aa497 495 }
7f2aa497
KM
496 }
497
f9cf6d92 498 bool doent=true;
b5baefaf 499 uint32_t maxent = ::arg().asNum("max-ent-entries");
c9b43446 500 DNSName shorter, ordername;
7abbc40f
PD
501 set<DNSName> rrterm;
502 map<DNSName,bool> nonterm;
b5baefaf 503
53568203 504
ef7cd021 505 for(DNSResourceRecord& rr : rrs) {
d3ee36f2 506 if(!zs.isPresigned) {
53568203
KM
507 if (rr.qtype.getCode() == QType::RRSIG)
508 continue;
d3ee36f2 509 if(zs.isDnssecZone && rr.qtype.getCode() == QType::DNSKEY && !::arg().mustDo("direct-dnskey"))
53568203
KM
510 continue;
511 }
512
f9cf6d92
KM
513 // Figure out auth and ents
514 rr.auth=true;
515 shorter=rr.qname;
516 rrterm.clear();
517 do {
518 if(doent) {
d3ee36f2 519 if (!zs.qnames.count(shorter))
f9cf6d92 520 rrterm.insert(shorter);
d7652f3a 521 }
d3ee36f2 522 if(zs.nsset.count(shorter) && rr.qtype.getCode() != QType::DS)
f9cf6d92 523 rr.auth=false;
e5c7239c 524
e325f20c 525 if (shorter==domain) // stop at apex
f9cf6d92 526 break;
7abbc40f 527 }while(shorter.chopOff());
b8adb30d 528
e5c7239c
KM
529 // Insert ents
530 if(doent && !rrterm.empty()) {
531 bool auth;
532 if (!rr.auth && rr.qtype.getCode() == QType::NS) {
d3ee36f2 533 if (zs.isNSEC3)
c9b43446
KM
534 ordername=DNSName(toBase32Hex(hashQNameWithSalt(zs.ns3pr, rr.qname)));
535 auth=(!zs.isNSEC3 || !zs.optOutFlag || zs.secured.count(ordername));
e5c7239c
KM
536 } else
537 auth=rr.auth;
538
7abbc40f 539 for(const auto &nt: rrterm){
e5c7239c 540 if (!nonterm.count(nt))
7abbc40f 541 nonterm.insert(pair<DNSName, bool>(nt, auth));
e5c7239c
KM
542 else if (auth)
543 nonterm[nt]=true;
544 }
545
f9cf6d92 546 if(nonterm.size() > maxent) {
e6a9dde5 547 g_log<<Logger::Error<<"AXFR zone "<<domain<<" has too many empty non terminals."<<endl;
f9cf6d92
KM
548 nonterm.clear();
549 doent=false;
27045410 550 }
d7652f3a 551 }
f9cf6d92
KM
552
553 // RRSIG is always auth, even inside a delegation
554 if (rr.qtype.getCode() == QType::RRSIG)
555 rr.auth=true;
556
557 // Add ordername and insert record
d3ee36f2 558 if (zs.isDnssecZone && rr.qtype.getCode() != QType::RRSIG) {
559 if (zs.isNSEC3) {
f9cf6d92 560 // NSEC3
c9b43446
KM
561 ordername=DNSName(toBase32Hex(hashQNameWithSalt(zs.ns3pr, rr.qname)));
562 if(!zs.isNarrow && (rr.auth || (rr.qtype.getCode() == QType::NS && (!zs.optOutFlag || zs.secured.count(ordername))))) {
3bb3f561 563 di.backend->feedRecord(rr, ordername, true);
f9cf6d92 564 } else
c9b43446 565 di.backend->feedRecord(rr, DNSName());
f9cf6d92
KM
566 } else {
567 // NSEC
568 if (rr.auth || rr.qtype.getCode() == QType::NS) {
c9b43446
KM
569 ordername=rr.qname.makeRelative(domain);
570 di.backend->feedRecord(rr, ordername);
f9cf6d92 571 } else
c9b43446 572 di.backend->feedRecord(rr, DNSName());
f9cf6d92
KM
573 }
574 } else
c9b43446 575 di.backend->feedRecord(rr, DNSName());
d7652f3a 576 }
b5baefaf 577
f9cf6d92
KM
578 // Insert empty non-terminals
579 if(doent && !nonterm.empty()) {
d3ee36f2 580 if (zs.isNSEC3) {
581 di.backend->feedEnts3(zs.domain_id, domain, nonterm, zs.ns3pr, zs.isNarrow);
f9cf6d92 582 } else
d3ee36f2 583 di.backend->feedEnts(zs.domain_id, nonterm);
b5baefaf
PD
584 }
585
14b7e03b 586 di.backend->commitTransaction();
376ec278 587 transaction = false;
d3ee36f2 588 di.backend->setFresh(zs.domain_id);
38fdb531 589 purgeAuthCaches(domain.toString()+"$");
14b7e03b 590
a1282cdd 591
e6a9dde5 592 g_log<<Logger::Error<<"AXFR done for '"<<domain<<"', zone committed with serial number "<<zs.soa_serial<<endl;
8de9c054
BH
593 if(::arg().mustDo("slave-renotify"))
594 notifyDomain(domain);
3696224d
BH
595 }
596 catch(DBException &re) {
e6a9dde5 597 g_log<<Logger::Error<<"Unable to feed record during incoming AXFR of '" << domain<<"': "<<re.reason<<endl;
376ec278 598 if(di.backend && transaction) {
e6a9dde5 599 g_log<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
3696224d
BH
600 di.backend->abortTransaction();
601 }
602 }
16ce7f18
JS
603 catch(const MOADNSException &mde) {
604 g_log<<Logger::Error<<"Unable to parse record during incoming AXFR of '"<<domain<<"' (MOADNSException): "<<mde.what()<<endl;
376ec278 605 if(di.backend && transaction) {
e6a9dde5 606 g_log<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
2714db17
BH
607 di.backend->abortTransaction();
608 }
609 }
610 catch(std::exception &re) {
e6a9dde5 611 g_log<<Logger::Error<<"Unable to parse record during incoming AXFR of '"<<domain<<"' (std::exception): "<<re.what()<<endl;
376ec278 612 if(di.backend && transaction) {
e6a9dde5 613 g_log<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
75ccb5b9
BH
614 di.backend->abortTransaction();
615 }
616 }
3696224d 617 catch(ResolverException &re) {
dd73b264
KD
618 {
619 Lock l(&d_lock);
620 // The AXFR probably failed due to a problem on the master server. If SOA-checks against this master
621 // still succeed, we would constantly try to AXFR the zone. To avoid this, we add the zone to the list of
622 // failed slave-checks. This will suspend slave-checks (and subsequent AXFR) for this zone for some time.
623 uint64_t newCount = 1;
624 time_t now = time(0);
625 const auto failedEntry = d_failedSlaveRefresh.find(domain);
626 if (failedEntry != d_failedSlaveRefresh.end())
627 newCount = d_failedSlaveRefresh[domain].first + 1;
628 time_t nextCheck = now + std::min(newCount * d_tickinterval, (uint64_t)::arg().asNum("soa-retry-default"));
629 d_failedSlaveRefresh[domain] = {newCount, nextCheck};
630 g_log<<Logger::Error<<"Unable to AXFR zone '"<<domain<<"' from remote '"<<remote<<"' (resolver): "<<re.reason<<" (This was the "<<(newCount == 1 ? "first" : std::to_string(newCount) + "th")<<" time. Excluding zone from slave-checks until "<<nextCheck<<")"<<endl;
631 }
376ec278 632 if(di.backend && transaction) {
e6a9dde5 633 g_log<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
7f450125
PD
634 di.backend->abortTransaction();
635 }
636 }
3f81d239 637 catch(PDNSException &ae) {
e6a9dde5 638 g_log<<Logger::Error<<"Unable to AXFR zone '"<<domain<<"' from remote '"<<remote<<"' (PDNSException): "<<ae.reason<<endl;
376ec278 639 if(di.backend && transaction) {
e6a9dde5 640 g_log<<Logger::Error<<"Aborting possible open transaction for domain '"<<domain<<"' AXFR"<<endl;
3696224d
BH
641 di.backend->abortTransaction();
642 }
643 }
644}
7597b3cf 645namespace {
7597b3cf
BH
646struct DomainNotificationInfo
647{
648 DomainInfo di;
649 bool dnssecOk;
95302209 650 ComboAddress localaddr;
561434a6
PD
651 DNSName tsigkeyname, tsigalgname;
652 string tsigsecret;
7597b3cf
BH
653};
654}
655
3696224d
BH
656
657struct SlaveSenderReceiver
658{
40c9a111 659 typedef std::tuple<DNSName, ComboAddress, uint16_t> Identifier;
53568203 660
54b2edb4
BH
661 struct Answer {
662 uint32_t theirSerial;
663 uint32_t theirInception;
664 uint32_t theirExpire;
665 };
53568203 666
54b2edb4 667 map<uint32_t, Answer> d_freshness;
53568203 668
3696224d
BH
669 SlaveSenderReceiver()
670 {
3696224d 671 }
53568203 672
c0a5fc34 673 void deliverTimeout(const Identifier& i)
0c01dd7c
BH
674 {
675 }
53568203 676
7597b3cf 677 Identifier send(DomainNotificationInfo& dni)
3696224d 678 {
7597b3cf 679 random_shuffle(dni.di.masters.begin(), dni.di.masters.end());
0c01dd7c 680 try {
40c9a111 681 return std::make_tuple(dni.di.zone,
d622042f 682 *dni.di.masters.begin(),
683 d_resolver.sendResolve(*dni.di.masters.begin(),
40c9a111
RG
684 dni.localaddr,
685 dni.di.zone,
686 QType::SOA,
687 nullptr,
688 dni.dnssecOk, dni.tsigkeyname, dni.tsigalgname, dni.tsigsecret)
689 );
0c01dd7c 690 }
3f81d239 691 catch(PDNSException& e) {
86f1af1c 692 throw runtime_error("While attempting to query freshness of '"+dni.di.zone.toLogString()+"': "+e.reason);
0c01dd7c 693 }
3696224d 694 }
68dae32c 695
3696224d
BH
696 bool receive(Identifier& id, Answer& a)
697 {
40c9a111 698 if(d_resolver.tryGetSOASerial(&(std::get<0>(id)), &(std::get<1>(id)), &a.theirSerial, &a.theirInception, &a.theirExpire, &(std::get<2>(id)))) {
3696224d
BH
699 return 1;
700 }
701 return 0;
702 }
68dae32c 703
7597b3cf 704 void deliverAnswer(DomainNotificationInfo& dni, const Answer& a, unsigned int usec)
3696224d 705 {
7597b3cf 706 d_freshness[dni.di.id]=a;
3696224d 707 }
68dae32c 708
3696224d 709 Resolver d_resolver;
3696224d
BH
710};
711
7f3d870e
BH
712void CommunicatorClass::addSlaveCheckRequest(const DomainInfo& di, const ComboAddress& remote)
713{
714 Lock l(&d_lock);
90c9a70e
BH
715 DomainInfo ours = di;
716 ours.backend = 0;
d381d4a4
OM
717
718 // When adding a check, if the remote addr from which notification was
719 // received is a master, clear all other masters so we can be sure the
720 // query goes to that one.
a9abb26d
KM
721 for (const auto& master : di.masters) {
722 if (ComboAddress::addressOnlyEqual()(remote, master)) {
d381d4a4 723 ours.masters.clear();
a9abb26d 724 ours.masters.push_back(master);
d381d4a4
OM
725 break;
726 }
727 }
728 d_tocheck.erase(di);
90c9a70e 729 d_tocheck.insert(ours);
7f3d870e
BH
730 d_any_sem.post(); // kick the loop!
731}
732
7108e055
PD
733void CommunicatorClass::addTrySuperMasterRequest(DNSPacket *p)
734{
735 Lock l(&d_lock);
736 DNSPacket ours = *p;
02980dc2 737 if(d_potentialsupermasters.insert(ours).second)
738 d_any_sem.post(); // kick the loop!
7108e055
PD
739}
740
3696224d
BH
741void CommunicatorClass::slaveRefresh(PacketHandler *P)
742{
56a4f068
AT
743 // not unless we are slave
744 if (!::arg().mustDo("slave")) return;
745
3971cf53 746 UeberBackend *B=P->getBackend();
16e654fa 747 vector<DomainInfo> rdomains;
02980dc2 748 vector<DomainNotificationInfo> sdomains;
749 set<DNSPacket, cmp> trysuperdomains;
7f3d870e
BH
750 {
751 Lock l(&d_lock);
7d4ac70c
PD
752 set<DomainInfo> requeue;
753 for(const auto& di: d_tocheck) {
754 if(d_inprogress.count(di.zone)) {
755 g_log<<Logger::Debug<<"Got NOTIFY for "<<di.zone<<" while AXFR in progress, requeueing SOA check"<<endl;
756 requeue.insert(di);
757 }
758 else {
f1a7ff7a
KD
759 // We received a NOTIFY for a zone. This means at least one of the zone's master server is working.
760 // Therefore we delete the zone from the list of failed slave-checks to allow immediate checking.
761 const auto wasFailedDomain = d_failedSlaveRefresh.find(di.zone);
762 if (wasFailedDomain != d_failedSlaveRefresh.end()) {
763 g_log<<Logger::Debug<<"Got NOTIFY for "<<di.zone<<", removing zone from list of failed slave-checks and going to check SOA serial"<<endl;
764 d_failedSlaveRefresh.erase(di.zone);
765 } else {
766 g_log<<Logger::Debug<<"Got NOTIFY for "<<di.zone<<", going to check SOA serial"<<endl;
767 }
7d4ac70c
PD
768 rdomains.push_back(di);
769 }
770 }
771 d_tocheck.swap(requeue);
02980dc2 772
773 trysuperdomains = d_potentialsupermasters;
7108e055 774 d_potentialsupermasters.clear();
7f3d870e 775 }
68dae32c 776
02980dc2 777 for(const DNSPacket& dp : trysuperdomains) {
7731e32c
AT
778 // get the TSIG key name
779 TSIGRecordContent trc;
780 DNSName tsigkeyname;
ea3816cf 781 dp.getTSIGDetails(&trc, &tsigkeyname);
e8090386 782 P->trySuperMasterSynchronous(&dp, tsigkeyname); // FIXME could use some error loging
7108e055 783 }
3e7dcee6 784 if(rdomains.empty()) { // if we have priority domains, check them first
7f3d870e 785 B->getUnfreshSlaveInfos(&rdomains);
3e7dcee6 786 }
936eb34a 787 DNSSECKeeper dk(B); // NOW HEAR THIS! This DK uses our B backend, so no interleaved access!
dbcb3066
BH
788 {
789 Lock l(&d_lock);
dbcb3066 790 domains_by_name_t& nameindex=boost::multi_index::get<IDTag>(d_suckdomains);
b239dfba 791 time_t now = time(0);
dbcb3066 792
ef7cd021 793 for(DomainInfo& di : rdomains) {
b239dfba 794 const auto failed = d_failedSlaveRefresh.find(di.zone);
f1a7ff7a 795 if (failed != d_failedSlaveRefresh.end() && now < failed->second.second ) {
b239dfba 796 // If the domain has failed before and the time before the next check has not expired, skip this domain
f1a7ff7a 797 g_log<<Logger::Debug<<"Zone '"<<di.zone<<"' is on the list of failed SOA checks. Skipping SOA checks until "<< failed->second.second<<endl;
b239dfba 798 continue;
f1a7ff7a 799 }
95302209 800 std::vector<std::string> localaddr;
dbcb3066
BH
801 SuckRequest sr;
802 sr.domain=di.zone;
803 if(di.masters.empty()) // slave domains w/o masters are ignored
804 continue;
805 // remove unfresh domains already queued for AXFR, no sense polling them again
806 sr.master=*di.masters.begin();
3e7dcee6 807 if(nameindex.count(sr)) { // this does NOT however protect us against AXFRs already in progress!
dbcb3066 808 continue;
a71bee29 809 }
3e7dcee6 810 if(d_inprogress.count(sr.domain)) // this does
811 continue;
812
7597b3cf
BH
813 DomainNotificationInfo dni;
814 dni.di=di;
7fa35c07 815 dni.dnssecOk = dk.doesDNSSEC();
68dae32c 816
7597b3cf
BH
817 if(dk.getTSIGForAccess(di.zone, sr.master, &dni.tsigkeyname)) {
818 string secret64;
40920298 819 if(!B->getTSIGKey(dni.tsigkeyname, &dni.tsigalgname, &secret64)) {
e6a9dde5 820 g_log<<Logger::Error<<"TSIG key '"<<dni.tsigkeyname<<"' for domain '"<<di.zone<<"' not found, can not AXFR."<<endl;
40920298
PL
821 continue;
822 }
823 if (B64Decode(secret64, dni.tsigsecret) == -1) {
e6a9dde5 824 g_log<<Logger::Error<<"Unable to Base-64 decode TSIG key '"<<dni.tsigkeyname<<"' for domain '"<<di.zone<<"', can not AXFR."<<endl;
40920298
PL
825 continue;
826 }
7597b3cf 827 }
95302209
AT
828
829 localaddr.clear();
830 // check for AXFR-SOURCE
831 if(B->getDomainMetadata(di.zone, "AXFR-SOURCE", localaddr) && !localaddr.empty()) {
832 try {
833 dni.localaddr = ComboAddress(localaddr[0]);
e6a9dde5 834 g_log<<Logger::Info<<"Freshness check source (AXFR-SOURCE) for domain '"<<di.zone<<"' set to "<<localaddr[0]<<endl;
95302209
AT
835 }
836 catch(std::exception& e) {
e6a9dde5 837 g_log<<Logger::Error<<"Failed to load freshness check source '"<<localaddr[0]<<"' for '"<<di.zone<<"': "<<e.what()<<endl;
95302209
AT
838 return;
839 }
840 } else {
841 dni.localaddr.sin4.sin_family = 0;
842 }
843
7597b3cf 844 sdomains.push_back(dni);
dbcb3066 845 }
dbcb3066 846 }
3696224d
BH
847 if(sdomains.empty())
848 {
dbcb3066
BH
849 if(d_slaveschanged) {
850 Lock l(&d_lock);
e6a9dde5 851 g_log<<Logger::Warning<<"No new unfresh slave domains, "<<d_suckdomains.size()<<" queued for AXFR already, "<<d_inprogress.size()<<" in progress"<<endl;
dbcb3066
BH
852 }
853 d_slaveschanged = !rdomains.empty();
3696224d
BH
854 return;
855 }
dbcb3066
BH
856 else {
857 Lock l(&d_lock);
e6a9dde5 858 g_log<<Logger::Warning<<sdomains.size()<<" slave domain"<<(sdomains.size()>1 ? "s" : "")<<" need"<<
3696224d 859 (sdomains.size()>1 ? "" : "s")<<
dbcb3066
BH
860 " checking, "<<d_suckdomains.size()<<" queued for AXFR"<<endl;
861 }
68dae32c 862
3696224d 863 SlaveSenderReceiver ssr;
68dae32c 864
7597b3cf 865 Inflighter<vector<DomainNotificationInfo>, SlaveSenderReceiver> ifl(sdomains, ssr);
68dae32c 866
3696224d
BH
867 ifl.d_maxInFlight = 200;
868
869 for(;;) {
870 try {
871 ifl.run();
872 break;
873 }
dbcb3066 874 catch(std::exception& e) {
e6a9dde5 875 g_log<<Logger::Error<<"While checking domain freshness: " << e.what()<<endl;
3696224d 876 }
68dae32c 877 catch(PDNSException &re) {
e6a9dde5 878 g_log<<Logger::Error<<"While checking domain freshness: " << re.reason<<endl;
3696224d
BH
879 }
880 }
e6a9dde5 881 g_log<<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 882
7597b3cf 883 typedef DomainNotificationInfo val_t;
b239dfba 884 time_t now = time(0);
ef7cd021 885 for(val_t& val : sdomains) {
7597b3cf 886 DomainInfo& di(val.di);
d381d4a4 887 DomainInfo tempdi;
f9fa0e2d 888 // might've come from the packethandler
d381d4a4
OM
889 // Please do not overwrite received DI just to make sure it exists in backend.
890 if(!di.backend) {
891 if (!B->getDomainInfo(di.zone, tempdi)) {
e6a9dde5 892 g_log<<Logger::Warning<<"Ignore domain "<< di.zone<<" since it has been removed from our backend"<<endl;
232f0877 893 continue;
d381d4a4
OM
894 }
895 // Backend for di still doesn't exist and this might cause us to
896 // SEGFAULT on the setFresh command later on
897 di.backend = tempdi.backend;
f9fa0e2d 898 }
68dae32c 899
b239dfba
PL
900 if(!ssr.d_freshness.count(di.id)) { // If we don't have an answer for the domain
901 uint64_t newCount = 1;
f1a7ff7a 902 Lock l(&d_lock);
b239dfba
PL
903 const auto failedEntry = d_failedSlaveRefresh.find(di.zone);
904 if (failedEntry != d_failedSlaveRefresh.end())
905 newCount = d_failedSlaveRefresh[di.zone].first + 1;
906 time_t nextCheck = now + std::min(newCount * d_tickinterval, (uint64_t)::arg().asNum("soa-retry-default"));
907 d_failedSlaveRefresh[di.zone] = {newCount, nextCheck};
f1a7ff7a
KD
908 if (newCount == 1) {
909 g_log<<Logger::Warning<<"Unable to retrieve SOA for "<<di.zone<<
910 ", this was the first time. NOTE: For every subsequent failed SOA check the domain will be suspended from freshness checks for 'num-errors x "<<
911 d_tickinterval<<" seconds', with a maximum of "<<(uint64_t)::arg().asNum("soa-retry-default")<<" seconds. Skipping SOA checks until "<<nextCheck<<endl;
912 } else if (newCount % 10 == 0) {
913 g_log<<Logger::Warning<<"Unable to retrieve SOA for "<<di.zone<<", this was the "<<std::to_string(newCount)<<"th time. Skipping SOA checks until "<<nextCheck<<endl;
914 }
3696224d 915 continue;
b239dfba
PL
916 }
917
f1a7ff7a
KD
918 {
919 Lock l(&d_lock);
920 const auto wasFailedDomain = d_failedSlaveRefresh.find(di.zone);
921 if (wasFailedDomain != d_failedSlaveRefresh.end())
922 d_failedSlaveRefresh.erase(di.zone);
923 }
b239dfba 924
b76c5f3a
KM
925 bool hasSOA = false;
926 SOAData sd;
927 try{
928 hasSOA = B->getSOA(di.zone, sd);
929 }
930 catch(...) {}
931
932 uint32_t theirserial = ssr.d_freshness[di.id].theirSerial, ourserial = sd.serial;
68dae32c 933
0f76617d 934 if(rfc1982LessThan(theirserial, ourserial) && ourserial != 0 && !::arg().mustDo("axfr-lower-serial")) {
e6a9dde5 935 g_log<<Logger::Error<<"Domain '"<<di.zone<<"' more recent than master, our serial " << ourserial << " > their serial "<< theirserial << endl;
3696224d
BH
936 di.backend->setFresh(di.id);
937 }
b76c5f3a 938 else if(hasSOA && theirserial == ourserial) {
7fa35c07
KM
939 uint32_t maxExpire=0, maxInception=0;
940 if(dk.isPresigned(di.zone)) {
936eb34a 941 B->lookup(QType(QType::RRSIG), di.zone); // can't use DK before we are done with this lookup!
abcd36a1 942 DNSZoneRecord zr;
abcd36a1
CH
943 while(B->get(zr)) {
944 auto rrsig = getRR<RRSIGRecordContent>(zr.dr);
945 if(rrsig->d_type == QType::SOA) {
946 maxInception = std::max(maxInception, rrsig->d_siginception);
947 maxExpire = std::max(maxExpire, rrsig->d_sigexpire);
54b2edb4
BH
948 }
949 }
7fa35c07
KM
950 }
951 if(! maxInception && ! ssr.d_freshness[di.id].theirInception) {
b3d982f3 952 g_log<<Logger::Info<<"Domain '"<< di.zone<<"' is fresh (no DNSSEC), serial is "<<ourserial<<endl;
7fa35c07
KM
953 di.backend->setFresh(di.id);
954 }
955 else if(maxInception == ssr.d_freshness[di.id].theirInception && maxExpire == ssr.d_freshness[di.id].theirExpire) {
b3d982f3 956 g_log<<Logger::Info<<"Domain '"<< di.zone<<"' is fresh and SOA RRSIGs match, serial is "<<ourserial<<endl;
7fa35c07
KM
957 di.backend->setFresh(di.id);
958 }
959 else if(maxExpire >= now && ! ssr.d_freshness[di.id].theirInception ) {
b3d982f3 960 g_log<<Logger::Info<<"Domain '"<< di.zone<<"' is fresh, master is no longer signed but (some) signatures are still vallid, serial is "<<ourserial<<endl;
7fa35c07
KM
961 di.backend->setFresh(di.id);
962 }
963 else if(maxInception && ! ssr.d_freshness[di.id].theirInception ) {
b3d982f3 964 g_log<<Logger::Warning<<"Domain '"<< di.zone<<"' is stale, master is no longer signed and all signatures have expired, serial is "<<ourserial<<endl;
7fa35c07
KM
965 addSuckRequest(di.zone, *di.masters.begin());
966 }
967 else if(dk.doesDNSSEC() && ! maxInception && ssr.d_freshness[di.id].theirInception) {
b3d982f3 968 g_log<<Logger::Warning<<"Domain '"<< di.zone<<"' is stale, master has signed, serial is "<<ourserial<<endl;
7fa35c07
KM
969 addSuckRequest(di.zone, *di.masters.begin());
970 }
971 else {
b3d982f3 972 g_log<<Logger::Warning<<"Domain '"<< di.zone<<"' is fresh, but RRSIGs differ, so DNSSEC is stale, serial is "<<ourserial<<endl;
7fa35c07 973 addSuckRequest(di.zone, *di.masters.begin());
54b2edb4 974 }
3696224d
BH
975 }
976 else {
b76c5f3a
KM
977 if(hasSOA) {
978 g_log<<Logger::Warning<<"Domain '"<< di.zone<<"' is stale, master serial "<<theirserial<<", our serial "<< ourserial <<endl;
979 }
980 else {
981 g_log<<Logger::Warning<<"Domain '"<< di.zone<<"' is empty, master serial "<<theirserial<<endl;
982 }
d3ee36f2 983 addSuckRequest(di.zone, *di.masters.begin());
3696224d
BH
984 }
985 }
68dae32c 986}