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