]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/rpzloader.cc
updated KSK and ZSK Rollover procedures, small fixes in Algorithm Rollover procedure
[thirdparty/pdns.git] / pdns / rpzloader.cc
1 #include "arguments.hh"
2 #include "dnsparser.hh"
3 #include "dnsrecords.hh"
4 #include "ixfr.hh"
5 #include "syncres.hh"
6 #include "axfr-retriever.hh"
7 #include "lock.hh"
8 #include "logger.hh"
9 #include "logging.hh"
10 #include "rec-lua-conf.hh"
11 #include "rpzloader.hh"
12 #include "zoneparser-tng.hh"
13 #include "threadname.hh"
14 #include "query-local-address.hh"
15
16 Netmask makeNetmaskFromRPZ(const DNSName& name)
17 {
18 auto parts = name.getRawLabels();
19 /*
20 * why 2?, the minimally valid IPv6 address that can be encoded in an RPZ is
21 * $NETMASK.zz (::/$NETMASK)
22 * Terrible right?
23 */
24 if(parts.size() < 2 || parts.size() > 9)
25 throw PDNSException("Invalid IP address in RPZ: "+name.toLogString());
26
27 bool isV6 = (stoi(parts[0]) > 32);
28 bool hadZZ = false;
29
30 for (auto &part : parts) {
31 // Check if we have an IPv4 octet
32 for (auto c : part)
33 if (!isdigit(c))
34 isV6 = true;
35
36 if (pdns_iequals(part,"zz")) {
37 if (hadZZ)
38 throw PDNSException("more than one 'zz' label found in RPZ name"+name.toLogString());
39 part = "";
40 isV6 = true;
41 hadZZ = true;
42 }
43 }
44
45 if (isV6 && parts.size() < 9 && !hadZZ)
46 throw PDNSException("No 'zz' label found in an IPv6 RPZ name shorter than 9 elements: "+name.toLogString());
47
48 if (parts.size() == 5 && !isV6)
49 return Netmask(parts[4]+"."+parts[3]+"."+parts[2]+"."+parts[1]+"/"+parts[0]);
50
51 string v6;
52
53 if (parts[parts.size()-1] == "") {
54 v6 += ":";
55 }
56 for (uint8_t i = parts.size()-1 ; i > 0; i--) {
57 v6 += parts[i];
58 if (i > 1 || (i == 1 && parts[i] == "")) {
59 v6 += ":";
60 }
61 }
62 v6 += "/" + parts[0];
63
64 return Netmask(v6);
65 }
66
67 static void RPZRecordToPolicy(const DNSRecord& dr, std::shared_ptr<DNSFilterEngine::Zone> zone, bool addOrRemove, const boost::optional<DNSFilterEngine::Policy>& defpol, bool defpolOverrideLocal, uint32_t maxTTL)
68 {
69 static const DNSName drop("rpz-drop."), truncate("rpz-tcp-only."), noaction("rpz-passthru.");
70 static const DNSName rpzClientIP("rpz-client-ip"), rpzIP("rpz-ip"),
71 rpzNSDname("rpz-nsdname"), rpzNSIP("rpz-nsip.");
72 static const std::string rpzPrefix("rpz-");
73
74 DNSFilterEngine::Policy pol;
75 bool defpolApplied = false;
76
77 if(dr.d_class != QClass::IN) {
78 return;
79 }
80
81 if(dr.d_type == QType::CNAME) {
82 auto crc = getRR<CNAMERecordContent>(dr);
83 if (!crc) {
84 return;
85 }
86 auto crcTarget=crc->getTarget();
87 if(defpol) {
88 pol=*defpol;
89 defpolApplied = true;
90 }
91 else if(crcTarget.isRoot()) {
92 // cerr<<"Wants NXDOMAIN for "<<dr.d_name<<": ";
93 pol.d_kind = DNSFilterEngine::PolicyKind::NXDOMAIN;
94 } else if(crcTarget==g_wildcarddnsname) {
95 // cerr<<"Wants NODATA for "<<dr.d_name<<": ";
96 pol.d_kind = DNSFilterEngine::PolicyKind::NODATA;
97 }
98 else if(crcTarget==drop) {
99 // cerr<<"Wants DROP for "<<dr.d_name<<": ";
100 pol.d_kind = DNSFilterEngine::PolicyKind::Drop;
101 }
102 else if(crcTarget==truncate) {
103 // cerr<<"Wants TRUNCATE for "<<dr.d_name<<": ";
104 pol.d_kind = DNSFilterEngine::PolicyKind::Truncate;
105 }
106 else if(crcTarget==noaction) {
107 // cerr<<"Wants NOACTION for "<<dr.d_name<<": ";
108 pol.d_kind = DNSFilterEngine::PolicyKind::NoAction;
109 }
110 /* "The special RPZ encodings which are not to be taken as Local Data are
111 CNAMEs with targets that are:
112 + "." (NXDOMAIN action),
113 + "*." (NODATA action),
114 + a top level domain starting with "rpz-",
115 + a child of a top level domain starting with "rpz-".
116 */
117 else if(!crcTarget.empty() && !crcTarget.isRoot() && crcTarget.getRawLabel(crcTarget.countLabels() - 1).compare(0, rpzPrefix.length(), rpzPrefix) == 0) {
118 /* this is very likely an higher format number or a configuration error,
119 let's just ignore it. */
120 g_log<<Logger::Info<<"Discarding unsupported RPZ entry "<<crcTarget<<" for "<<dr.d_name<<endl;
121 return;
122 }
123 else {
124 pol.d_kind = DNSFilterEngine::PolicyKind::Custom;
125 pol.d_custom.emplace_back(dr.d_content);
126 // cerr<<"Wants custom "<<crcTarget<<" for "<<dr.d_name<<": ";
127 }
128 }
129 else {
130 if (defpol && defpolOverrideLocal) {
131 pol=*defpol;
132 defpolApplied = true;
133 }
134 else {
135 pol.d_kind = DNSFilterEngine::PolicyKind::Custom;
136 pol.d_custom.emplace_back(dr.d_content);
137 // cerr<<"Wants custom "<<dr.d_content->getZoneRepresentation()<<" for "<<dr.d_name<<": ";
138 }
139 }
140
141 if (!defpolApplied || defpol->d_ttl < 0) {
142 pol.d_ttl = static_cast<int32_t>(std::min(maxTTL, dr.d_ttl));
143 } else {
144 pol.d_ttl = static_cast<int32_t>(std::min(maxTTL, static_cast<uint32_t>(pol.d_ttl)));
145 }
146
147 // now to DO something with that
148
149 if(dr.d_name.isPartOf(rpzNSDname)) {
150 DNSName filt=dr.d_name.makeRelative(rpzNSDname);
151 if(addOrRemove)
152 zone->addNSTrigger(filt, std::move(pol), defpolApplied);
153 else
154 zone->rmNSTrigger(filt, std::move(pol));
155 } else if(dr.d_name.isPartOf(rpzClientIP)) {
156 DNSName filt=dr.d_name.makeRelative(rpzClientIP);
157 auto nm=makeNetmaskFromRPZ(filt);
158 if(addOrRemove)
159 zone->addClientTrigger(nm, std::move(pol), defpolApplied);
160 else
161 zone->rmClientTrigger(nm, std::move(pol));
162
163 } else if(dr.d_name.isPartOf(rpzIP)) {
164 // cerr<<"Should apply answer content IP policy: "<<dr.d_name<<endl;
165 DNSName filt=dr.d_name.makeRelative(rpzIP);
166 auto nm=makeNetmaskFromRPZ(filt);
167 if(addOrRemove)
168 zone->addResponseTrigger(nm, std::move(pol), defpolApplied);
169 else
170 zone->rmResponseTrigger(nm, std::move(pol));
171 } else if(dr.d_name.isPartOf(rpzNSIP)) {
172 DNSName filt=dr.d_name.makeRelative(rpzNSIP);
173 auto nm=makeNetmaskFromRPZ(filt);
174 if(addOrRemove)
175 zone->addNSIPTrigger(nm, std::move(pol), defpolApplied);
176 else
177 zone->rmNSIPTrigger(nm, std::move(pol));
178 } else {
179 if(addOrRemove) {
180 /* if we did override the existing policy with the default policy,
181 we might turn two A or AAAA into a CNAME, which would trigger
182 an exception. Let's just ignore it. */
183 zone->addQNameTrigger(dr.d_name, std::move(pol), defpolApplied);
184 }
185 else {
186 zone->rmQNameTrigger(dr.d_name, std::move(pol));
187 }
188 }
189 }
190
191 static shared_ptr<SOARecordContent> loadRPZFromServer(const shared_ptr<Logr::Logger>& plogger, const ComboAddress& primary, const DNSName& zoneName, std::shared_ptr<DNSFilterEngine::Zone> zone, const boost::optional<DNSFilterEngine::Policy>& defpol, bool defpolOverrideLocal, uint32_t maxTTL, const TSIGTriplet& tt, size_t maxReceivedBytes, const ComboAddress& localAddress, uint16_t axfrTimeout)
192 {
193
194 auto logger = plogger->withValues("primary", Logging::Loggable(primary));
195 SLOG(g_log<<Logger::Warning<<"Loading RPZ zone '"<<zoneName<<"' from "<<primary.toStringWithPort()<<endl,
196 logger->info(Logr::Info, "Loading RPZ from nameserver"));
197 logger = logger->v(1);
198 if(!tt.name.empty()) {
199 SLOG(g_log<<Logger::Warning<<"With TSIG key '"<<tt.name<<"' of algorithm '"<<tt.algo<<"'"<<endl,
200 logger->info(Logr::Info, "Using TSIG key for authentication", "tsig_key_name", Logging::Loggable(tt.name), "tsig_key_algorithm", Logging::Loggable(tt.algo)));
201 }
202
203 ComboAddress local(localAddress);
204 if (local == ComboAddress())
205 local = pdns::getQueryLocalAddress(primary.sin4.sin_family, 0);
206
207 AXFRRetriever axfr(primary, zoneName, tt, &local, maxReceivedBytes, axfrTimeout);
208 unsigned int nrecords=0;
209 Resolver::res_t nop;
210 vector<DNSRecord> chunk;
211 time_t last=0;
212 time_t axfrStart = time(nullptr);
213 time_t axfrNow = time(nullptr);
214 shared_ptr<SOARecordContent> sr;
215 while(axfr.getChunk(nop, &chunk, (axfrStart + axfrTimeout - axfrNow))) {
216 for(auto& dr : chunk) {
217 if(dr.d_type==QType::NS || dr.d_type==QType::TSIG) {
218 continue;
219 }
220
221 dr.d_name.makeUsRelative(zoneName);
222 if(dr.d_type==QType::SOA) {
223 sr = getRR<SOARecordContent>(dr);
224 continue;
225 }
226
227 RPZRecordToPolicy(dr, zone, true, defpol, defpolOverrideLocal, maxTTL);
228 nrecords++;
229 }
230 axfrNow = time(nullptr);
231 if (axfrNow < axfrStart || axfrNow - axfrStart > axfrTimeout) {
232 throw PDNSException("Total AXFR time exceeded!");
233 }
234 if(last != time(0)) {
235 SLOG(g_log<<Logger::Info<<"Loaded & indexed "<<nrecords<<" policy records so far for RPZ zone '"<<zoneName<<"'"<<endl,
236 logger->info(Logr::Info, "RPZ load in progress", "nrecords", Logging::Loggable(nrecords)));
237 last=time(0);
238 }
239 }
240 SLOG(g_log<<Logger::Info<<"Done: "<<nrecords<<" policy records active, SOA: "<<sr->getZoneRepresentation()<<endl,
241 logger->info(Logr::Info, "RPZ load completed", "nrecords", Logging::Loggable(nrecords), "soa", Logging::Loggable(sr->getZoneRepresentation())));
242 return sr;
243 }
244
245 static LockGuarded<std::unordered_map<std::string, shared_ptr<rpzStats> > > s_rpzStats;
246
247 shared_ptr<rpzStats> getRPZZoneStats(const std::string& zone)
248 {
249 auto stats = s_rpzStats.lock();
250 auto it = stats->find(zone);
251 if (it == stats->end()) {
252 auto stat = std::make_shared<rpzStats>();
253 (*stats)[zone] = stat;
254 return stat;
255 }
256 return it->second;
257 }
258
259 static void incRPZFailedTransfers(const std::string& zone)
260 {
261 auto stats = getRPZZoneStats(zone);
262 if (stats != nullptr)
263 stats->d_failedTransfers++;
264 }
265
266 static void setRPZZoneNewState(const std::string& zone, uint32_t serial, uint64_t numberOfRecords, bool fromFile, bool wasAXFR)
267 {
268 auto stats = getRPZZoneStats(zone);
269 if (stats == nullptr) {
270 return;
271 }
272 if (!fromFile) {
273 stats->d_successfulTransfers++;
274 if (wasAXFR) {
275 stats->d_fullTransfers++;
276 }
277 }
278 stats->d_lastUpdate = time(nullptr);
279 stats->d_serial = serial;
280 stats->d_numberOfRecords = numberOfRecords;
281 }
282
283 // this function is silent - you do the logging
284 std::shared_ptr<SOARecordContent> loadRPZFromFile(const std::string& fname, std::shared_ptr<DNSFilterEngine::Zone> zone, const boost::optional<DNSFilterEngine::Policy>& defpol, bool defpolOverrideLocal, uint32_t maxTTL)
285 {
286 shared_ptr<SOARecordContent> sr = nullptr;
287 ZoneParserTNG zpt(fname);
288 zpt.setMaxGenerateSteps(::arg().asNum("max-generate-steps"));
289 zpt.setMaxIncludes(::arg().asNum("max-include-depth"));
290 DNSResourceRecord drr;
291 DNSName domain;
292 while(zpt.get(drr)) {
293 try {
294 if(drr.qtype.getCode() == QType::CNAME && drr.content.empty())
295 drr.content=".";
296 DNSRecord dr(drr);
297 if(dr.d_type == QType::SOA) {
298 sr = getRR<SOARecordContent>(dr);
299 domain = dr.d_name;
300 zone->setDomain(domain);
301 }
302 else if(dr.d_type == QType::NS) {
303 continue;
304 }
305 else {
306 dr.d_name=dr.d_name.makeRelative(domain);
307 RPZRecordToPolicy(dr, zone, true, defpol, defpolOverrideLocal, maxTTL);
308 }
309 }
310 catch(const PDNSException& pe) {
311 throw PDNSException("Issue parsing '"+drr.qname.toLogString()+"' '"+drr.content+"' at "+zpt.getLineOfFile()+": "+pe.reason);
312 }
313 }
314
315 if (sr != nullptr) {
316 zone->setRefresh(sr->d_st.refresh);
317 setRPZZoneNewState(zone->getName(), sr->d_st.serial, zone->size(), true, false);
318 }
319 return sr;
320 }
321
322 static bool dumpZoneToDisk(const shared_ptr<Logr::Logger>& plogger, const DNSName& zoneName, const std::shared_ptr<DNSFilterEngine::Zone>& newZone, const std::string& dumpZoneFileName)
323 {
324 auto logger = plogger->v(1);
325 logger->info("Dumping zone to disk", "destination_file", Logging::Loggable(dumpZoneFileName));
326 std::string temp = dumpZoneFileName + "XXXXXX";
327 int fd = mkstemp(&temp.at(0));
328 if (fd < 0) {
329 SLOG(g_log<<Logger::Warning<<"Unable to open a file to dump the content of the RPZ zone "<<zoneName<<endl,
330 logger->error(Logr::Warning, errno, "Unable to create temporary file"));
331 return false;
332 }
333
334 auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fdopen(fd, "w+"), fclose);
335 if (!fp) {
336 int err = errno;
337 close(fd);
338 SLOG(g_log<<Logger::Warning<<"Unable to open a file pointer to dump the content of the RPZ zone "<<zoneName<<endl,
339 logger->error(Logr::Warning, err, "Unable to open file pointer"));
340 return false;
341 }
342 fd = -1;
343
344 try {
345 newZone->dump(fp.get());
346 }
347 catch(const std::exception& e) {
348 SLOG(g_log<<Logger::Warning<<"Error while dumping the content of the RPZ zone "<<zoneName<<": "<<e.what()<<endl,
349 logger->error(Logr::Warning, e.what(), "Error while dumping the content of the RPZ"));
350 return false;
351 }
352
353 if (fflush(fp.get()) != 0) {
354 SLOG(g_log<<Logger::Warning<<"Error while flushing the content of the RPZ zone "<<zoneName<<" to the dump file: "<<stringerror()<<endl,
355 logger->error(Logr::Warning, errno, "Error while flushing the content of the RPZ"));
356 return false;
357 }
358
359 if (fsync(fileno(fp.get())) != 0) {
360 SLOG(g_log<<Logger::Warning<<"Error while syncing the content of the RPZ zone "<<zoneName<<" to the dump file: "<<stringerror()<<endl,
361 logger->error(Logr::Warning, errno, "Error while syncing the content of the RPZ"));
362 return false;
363 }
364
365 if (fclose(fp.release()) != 0) {
366 SLOG(g_log<<Logger::Warning<<"Error while writing the content of the RPZ zone "<<zoneName<<" to the dump file: "<<stringerror()<<endl,
367 logger->error(Logr::Warning, errno, "Error while writing the content of the RPZ"));
368 return false;
369 }
370
371 if (rename(temp.c_str(), dumpZoneFileName.c_str()) != 0) {
372 SLOG(g_log<<Logger::Warning<<"Error while moving the content of the RPZ zone "<<zoneName<<" to the dump file: "<<stringerror()<<endl,
373 logger->error(Logr::Warning, errno, "Error while moving the content of the RPZ", "destination_file", Logging::Loggable(dumpZoneFileName)));
374 return false;
375 }
376
377 return true;
378 }
379
380 void RPZIXFRTracker(const std::vector<ComboAddress>& primaries, const boost::optional<DNSFilterEngine::Policy>& defpol, bool defpolOverrideLocal, uint32_t maxTTL, size_t zoneIdx, const TSIGTriplet& tt, size_t maxReceivedBytes, const ComboAddress& localAddress, const uint16_t axfrTimeout, const uint32_t refreshFromConf, std::shared_ptr<SOARecordContent> sr, const std::string& dumpZoneFileName, uint64_t configGeneration)
381 {
382 setThreadName("pdns-r/RPZIXFR");
383 bool isPreloaded = sr != nullptr;
384 auto luaconfsLocal = g_luaconfs.getLocal();
385
386 auto logger = g_slog->withName("rpz")->v(1);
387
388 /* we can _never_ modify this zone directly, we need to do a full copy then replace the existing zone */
389 std::shared_ptr<DNSFilterEngine::Zone> oldZone = luaconfsLocal->dfe.getZone(zoneIdx);
390 if (!oldZone) {
391 SLOG(g_log<<Logger::Error<<"Unable to retrieve RPZ zone with index "<<zoneIdx<<" from the configuration, exiting"<<endl,
392 logger->error(Logr::Error, "Unable to retrieve RPZ zone from configuration", "index", Logging::Loggable(zoneIdx)));
393 return;
394 }
395
396 // If oldZone failed to load its getRefresh() returns 0, protect against that
397 uint32_t refresh = std::max(refreshFromConf ? refreshFromConf : oldZone->getRefresh(), 10U);
398 DNSName zoneName = oldZone->getDomain();
399 std::string polName = !oldZone->getName().empty() ? oldZone->getName() : zoneName.toStringNoDot();
400
401 // Now that we know the name, set it in the logger
402 logger = logger->withValues("zone", Logging::Loggable(zoneName));
403
404 while (!sr) {
405 /* if we received an empty sr, the zone was not really preloaded */
406
407 /* full copy, as promised */
408 std::shared_ptr<DNSFilterEngine::Zone> newZone = std::make_shared<DNSFilterEngine::Zone>(*oldZone);
409 for (const auto& primary : primaries) {
410 try {
411 sr = loadRPZFromServer(logger, primary, zoneName, newZone, defpol, defpolOverrideLocal, maxTTL, tt, maxReceivedBytes, localAddress, axfrTimeout);
412 newZone->setSerial(sr->d_st.serial);
413 newZone->setRefresh(sr->d_st.refresh);
414 refresh = std::max(refreshFromConf ? refreshFromConf : newZone->getRefresh(), 1U);
415 setRPZZoneNewState(polName, sr->d_st.serial, newZone->size(), false, true);
416
417 g_luaconfs.modify([zoneIdx, &newZone](LuaConfigItems& lci) {
418 lci.dfe.setZone(zoneIdx, newZone);
419 });
420
421 if (!dumpZoneFileName.empty()) {
422 dumpZoneToDisk(logger, zoneName, newZone, dumpZoneFileName);
423 }
424
425 /* no need to try another primary */
426 break;
427 }
428 catch(const std::exception& e) {
429 SLOG(g_log<<Logger::Warning<<"Unable to load RPZ zone '"<<zoneName<<"' from '"<<primary<<"': '"<<e.what()<<"'. (Will try again in "<<refresh<<" seconds...)"<<endl,
430 logger->info(Logr::Warning, "Unable to load RPZ zone, will retry", "from", Logging::Loggable(primary), "exception", Logging::Loggable(e.what()), "refresh", Logging::Loggable(refresh)));
431 incRPZFailedTransfers(polName);
432 }
433 catch(const PDNSException& e) {
434 SLOG(g_log<<Logger::Warning<<"Unable to load RPZ zone '"<<zoneName<<"' from '"<<primary<<"': '"<<e.reason<<"'. (Will try again in "<<refresh<<" seconds...)"<<endl,
435 logger->info(Logr::Warning, "Unable to load RPZ zone, will retry", "from", Logging::Loggable(primary), "exception", Logging::Loggable(e.reason), "refresh", Logging::Loggable(refresh)));
436 incRPZFailedTransfers(polName);
437 }
438 }
439
440 if (!sr) {
441 sleep(refresh);
442 }
443 }
444
445 bool skipRefreshDelay = isPreloaded;
446
447 for(;;) {
448 DNSRecord dr;
449 dr.d_content=sr;
450
451 if (skipRefreshDelay) {
452 skipRefreshDelay = false;
453 }
454 else {
455 sleep(refresh);
456 }
457
458 if (luaconfsLocal->generation != configGeneration) {
459 /* the configuration has been reloaded, meaning that a new thread
460 has been started to handle that zone and we are now obsolete.
461 */
462 g_log<<Logger::Info<<"A more recent configuration has been found, stopping the existing RPZ update thread for "<<zoneName<<endl;
463 return;
464 }
465
466 vector<pair<vector<DNSRecord>, vector<DNSRecord> > > deltas;
467 for (const auto& primary : primaries) {
468 g_log<<Logger::Info<<"Getting IXFR deltas for "<<zoneName<<" from "<<primary.toStringWithPort()<<", our serial: "<<getRR<SOARecordContent>(dr)->d_st.serial<<endl;
469
470 ComboAddress local(localAddress);
471 if (local == ComboAddress()) {
472 local = pdns::getQueryLocalAddress(primary.sin4.sin_family, 0);
473 }
474
475 try {
476 deltas = getIXFRDeltas(primary, zoneName, dr, tt, &local, maxReceivedBytes);
477
478 /* no need to try another primary */
479 break;
480 } catch(const std::runtime_error& e ){
481 g_log<<Logger::Warning<<e.what()<<endl;
482 incRPZFailedTransfers(polName);
483 continue;
484 }
485 }
486
487 if(deltas.empty()) {
488 continue;
489 }
490
491 try {
492 g_log<<Logger::Info<<"Processing "<<deltas.size()<<" delta"<<addS(deltas)<<" for RPZ "<<zoneName<<endl;
493
494 if (luaconfsLocal->generation != configGeneration) {
495 g_log<<Logger::Info<<"A more recent configuration has been found, stopping the existing RPZ update thread for "<<zoneName<<endl;
496 return;
497 }
498 oldZone = luaconfsLocal->dfe.getZone(zoneIdx);
499 if (!oldZone || oldZone->getDomain() != zoneName) {
500 g_log<<Logger::Info<<"This policy is no more, stopping the existing RPZ update thread for "<<zoneName << endl;
501 return;
502 }
503 /* we need to make a _full copy_ of the zone we are going to work on */
504 std::shared_ptr<DNSFilterEngine::Zone> newZone = std::make_shared<DNSFilterEngine::Zone>(*oldZone);
505 /* initialize the current serial to the last one */
506 std::shared_ptr<SOARecordContent> currentSR = sr;
507
508 int totremove=0, totadd=0;
509 bool fullUpdate = false;
510 for(const auto& delta : deltas) {
511 const auto& remove = delta.first;
512 const auto& add = delta.second;
513 if(remove.empty()) {
514 g_log<<Logger::Warning<<"IXFR update is a whole new zone"<<endl;
515 newZone->clear();
516 fullUpdate = true;
517 }
518 for(const auto& rr : remove) { // should always contain the SOA
519 if(rr.d_type == QType::NS)
520 continue;
521 if(rr.d_type == QType::SOA) {
522 auto oldsr = getRR<SOARecordContent>(rr);
523 if (oldsr && oldsr->d_st.serial == currentSR->d_st.serial) {
524 // cout<<"Got good removal of SOA serial "<<oldsr->d_st.serial<<endl;
525 }
526 else {
527 if (!oldsr) {
528 throw std::runtime_error("Unable to extract serial from SOA record while processing the removal part of an update");
529 }
530 else {
531 throw std::runtime_error("Received an unexpected serial (" + std::to_string(oldsr->d_st.serial) + ", expecting " + std::to_string(currentSR->d_st.serial) + ") from SOA record while processing the removal part of an update");
532 }
533 }
534 }
535 else {
536 totremove++;
537 g_log<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had removal of "<<rr.d_name<<" from RPZ zone "<<zoneName<<endl;
538 RPZRecordToPolicy(rr, newZone, false, defpol, defpolOverrideLocal, maxTTL);
539 }
540 }
541
542 for(const auto& rr : add) { // should always contain the new SOA
543 if(rr.d_type == QType::NS)
544 continue;
545 if(rr.d_type == QType::SOA) {
546 auto tempSR = getRR<SOARecordContent>(rr);
547 // g_log<<Logger::Info<<"New SOA serial for "<<zoneName<<": "<<currentSR->d_st.serial<<endl;
548 if (tempSR) {
549 currentSR = tempSR;
550 }
551 }
552 else {
553 totadd++;
554 g_log<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had addition of "<<rr.d_name<<" to RPZ zone "<<zoneName<<endl;
555 RPZRecordToPolicy(rr, newZone, true, defpol, defpolOverrideLocal, maxTTL);
556 }
557 }
558 }
559
560 /* only update sr now that all changes have been converted */
561 if (currentSR) {
562 sr = currentSR;
563 }
564 g_log<<Logger::Info<<"Had "<<totremove<<" RPZ removal"<<addS(totremove)<<", "<<totadd<<" addition"<<addS(totadd)<<" for "<<zoneName<<" New serial: "<<sr->d_st.serial<<endl;
565 newZone->setSerial(sr->d_st.serial);
566 newZone->setRefresh(sr->d_st.refresh);
567 setRPZZoneNewState(polName, sr->d_st.serial, newZone->size(), false, fullUpdate);
568
569 /* we need to replace the existing zone with the new one,
570 but we don't want to touch anything else, especially other zones,
571 since they might have been updated by another RPZ IXFR tracker thread.
572 */
573 if (luaconfsLocal->generation != configGeneration) {
574 g_log<<Logger::Info<<"A more recent configuration has been found, stopping the existing RPZ update thread for "<<zoneName<<endl;
575 return;
576 }
577 g_luaconfs.modify([zoneIdx, &newZone](LuaConfigItems& lci) {
578 lci.dfe.setZone(zoneIdx, newZone);
579 });
580
581 if (!dumpZoneFileName.empty()) {
582 dumpZoneToDisk(logger, zoneName, newZone, dumpZoneFileName);
583 }
584 refresh = std::max(refreshFromConf ? refreshFromConf : newZone->getRefresh(), 1U);
585 }
586 catch (const std::exception& e) {
587 g_log << Logger::Error << "Error while applying the update received over XFR for "<<zoneName<<", skipping the update: "<< e.what() <<endl;
588 }
589 }
590 }