"Fossies" - the Fresh Open Source Software Archive

Member "pdns-auth-4.2.0/pdns/rpzloader.cc" (27 Aug 2019, 18406 Bytes) of package /linux/misc/dns/pdns-auth-4.2.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "rpzloader.cc" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.1.13_vs_4.2.0.

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