From: Otto Moerbeek Date: Fri, 1 Oct 2021 11:42:14 +0000 (+0200) Subject: Use structured logging, move loading to separate thread (a la RPZ) X-Git-Tag: auth-4.6.0-alpha1~5^2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=48defb61b1520c0f09564ff10de79311b0d9fff9;p=thirdparty%2Fpdns.git Use structured logging, move loading to separate thread (a la RPZ) --- diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 1eef4713dc..0011b55180 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -115,7 +115,6 @@ #include "rec-protozero.hh" #include "xpf.hh" -#include "rec-zonetocache.hh" typedef map tcpClientCounts_t; @@ -3702,8 +3701,6 @@ static void houseKeeping(void *) static thread_local int cleanCounter=0; static thread_local bool s_running; // houseKeeping can get suspended in secpoll, and be restarted, which makes us do duplicate work static time_t last_RC_prune = 0; - static time_t last_zone_to_cache = 0; - static time_t zones_to_cache_interval = 0; auto luaconfsLocal = g_luaconfs.getLocal(); if (last_trustAnchorUpdate == 0 && !luaconfsLocal->trustAnchorFileInfo.fname.empty() && luaconfsLocal->trustAnchorFileInfo.interval != 0) { @@ -3775,11 +3772,6 @@ static void houseKeeping(void *) g_log<zonesToCacheConfig.empty() && now.tv_sec - last_zone_to_cache > zones_to_cache_interval * 9 / 10) { - last_zone_to_cache = now.tv_sec; - zones_to_cache_interval = RecZoneToCache::ZonesToCache(luaconfsLocal->zonesToCacheConfig); - } } if(now.tv_sec - last_secpoll >= 3600) { diff --git a/pdns/rec-lua-conf.cc b/pdns/rec-lua-conf.cc index 6bcd0b897b..4d3f10dd94 100644 --- a/pdns/rec-lua-conf.cc +++ b/pdns/rec-lua-conf.cc @@ -400,17 +400,17 @@ void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& de typedef std::unordered_map> zoneToCacheOptions_t; - Lua.writeFunction("zoneToCache", [&lci](const string& zoneName, const string& method, const boost::variant>>& primaries, boost::optional options) { + Lua.writeFunction("zoneToCache", [&delayedThreads](const string& zoneName, const string& method, const boost::variant>>& srcs, boost::optional options) { try { RecZoneToCache::Config conf; conf.d_zone = zoneName; conf.d_method = method; - if (primaries.type() == typeid(string)) { - conf.d_sources.push_back(boost::get(primaries)); + if (srcs.type() == typeid(std::string)) { + conf.d_sources.push_back(boost::get(srcs)); } else { - for (const auto& primary : boost::get>>(primaries)) { - conf.d_sources.push_back(primary.second); + for (const auto& src : boost::get>>(srcs)) { + conf.d_sources.push_back(src.second); } } if (options) { @@ -432,9 +432,15 @@ void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& de if (have.count("localAddress")) { conf.d_local = ComboAddress(boost::get(have["localAddress"])); } + if (have.count("refreshPeriod")) { + conf.d_refreshPeriod = boost::get(have["refreshPeriod"]); + } + if (have.count("retryOnErrorPeriod")) { + conf.d_retryOnError = boost::get(have["retryOnErrorPeriod"]); + } } - lci.zonesToCacheConfig.push_back(conf); + delayedThreads.ztcConfigs.push_back(conf); } catch (const std::exception&e ) { g_log< zonesToCacheConfig; /* we need to increment this every time the configuration is reloaded, so we know if we need to reload the protobuf remote loggers */ @@ -88,6 +87,7 @@ struct luaConfigDelayedThreads { // Please make sure that the tuple below only contains value types since they are used as parameters in a thread ct std::vector, boost::optional, bool, uint32_t, size_t, TSIGTriplet, size_t, ComboAddress, uint16_t, uint32_t, std::shared_ptr, std::string> > rpzPrimaryThreads; + std::vector ztcConfigs; }; void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& delayedThreads); diff --git a/pdns/recursordist/rec-zonetocache.cc b/pdns/recursordist/rec-zonetocache.cc index 053ddbcb56..bfff560d21 100644 --- a/pdns/recursordist/rec-zonetocache.cc +++ b/pdns/recursordist/rec-zonetocache.cc @@ -20,7 +20,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include #include "rec-zonetocache.hh" #include "syncres.hh" @@ -28,50 +27,31 @@ #include "query-local-address.hh" #include "axfr-retriever.hh" #include "validate-recursor.hh" +#include "logging.hh" +#include "threadname.hh" +#include "rec-lua-conf.hh" #ifdef HAVE_LIBCURL #include "minicurl.hh" #endif -time_t RecZoneToCache::ZonesToCache(const std::vector& cfgs) -{ - // By default and max once per 24 hours - time_t refresh = 24 * 3600; - - for (const auto& config : cfgs) { - const string msg = "zones-to-cache error while loading " + config.d_zone + ": "; - try { - refresh = std::min(refresh, ZoneToCache(config)); - } - catch (const PDNSException& e) { - g_log << Logger::Error << msg << e.reason << endl; - } - catch (const std::runtime_error& e) { - g_log << Logger::Error << msg << e.what() << endl; - } - catch (...) { - g_log << Logger::Error << msg << "unexpected exception" << endl; - } - - // We do not want to refresh more than once per hour - refresh = std::max(refresh, static_cast(3600)); - } - - return refresh; -} +#include struct ZoneData { + ZoneData(shared_ptr& log) : d_log(log) {} + std::map, vector> d_all; std::map, vector>> d_sigs; std::set d_delegations; - time_t d_refresh; time_t d_now; DNSName d_zone; + shared_ptr& d_log; bool isRRSetAuth(const DNSName& qname, QType qtype); void parseDRForCache(DNSRecord& dr); void getByAXFR(const RecZoneToCache::Config&); + time_t ZoneToCache(const RecZoneToCache::Config& config); }; bool ZoneData::isRRSetAuth(const DNSName& qname, QType qtype) @@ -97,7 +77,6 @@ void ZoneData::parseDRForCache(DNSRecord& dr) { const auto key = make_pair(dr.d_name, dr.d_type); - d_refresh = std::min(d_refresh, static_cast(dr.d_ttl)); dr.d_ttl += d_now; switch (dr.d_type) { @@ -148,12 +127,13 @@ void ZoneData::getByAXFR(const RecZoneToCache::Config& config) if (local == ComboAddress()) { local = pdns::getQueryLocalAddress(primary.sin4.sin_family, 0); } + AXFRRetriever axfr(primary, d_zone, tt, &local, maxReceivedBytes, axfrTimeout); Resolver::res_t nop; vector chunk; time_t axfrStart = time(nullptr); time_t axfrNow = time(nullptr); - shared_ptr sr; + while (axfr.getChunk(nop, &chunk, (axfrStart + axfrTimeout - axfrNow))) { for (auto& dr : chunk) { parseDRForCache(dr); @@ -187,6 +167,10 @@ static std::vector getURL(const RecZoneToCache::Config& config) MiniCurl mc; ComboAddress local = config.d_local; std::string reply = mc.getURL(config.d_sources.at(0), nullptr, local == ComboAddress() ? nullptr : &local, config.d_timeout, false, true); + if (config.d_maxReceivedBytes > 0 && reply.size() > config.d_maxReceivedBytes) { + // We should actually detect this *during* the GET + throw PDNSException("Retrieved data exceeds maxReceivedBytes"); + } std::istringstream stream(reply); string line; while (std::getline(stream, line)) { @@ -196,15 +180,13 @@ static std::vector getURL(const RecZoneToCache::Config& config) return lines; } -time_t RecZoneToCache::ZoneToCache(const Config& config) +time_t ZoneData::ZoneToCache(const RecZoneToCache::Config& config) { if (config.d_sources.size() > 1) { - // XXX Warning + d_log->info("Multiple sources not yet supported, using first"); } - ZoneData data; - data.d_refresh = std::numeric_limits::max(); - data.d_zone = DNSName(config.d_zone); - data.d_now = time(nullptr); + d_zone = DNSName(config.d_zone); + d_now = time(nullptr); // We do not do validation, it will happen on-demand if an Indeterminate record is encountered when the caches are queried // First scan all records collecting info about delegations ans sigs @@ -212,29 +194,33 @@ time_t RecZoneToCache::ZoneToCache(const Config& config) // they could be entered in into the (neg)cache. if (config.d_method == "axfr") { - data.getByAXFR(config); + d_log->info("Getting zone by AXFR"); + getByAXFR(config); } else { vector lines; if (config.d_method == "url") { + d_log->info("Getting zone by URL"); lines = getURL(config); } else if (config.d_method == "file") { + d_log->info("Getting zone from file"); lines = getLinesFromFile(config); } DNSResourceRecord drr; - ZoneParserTNG zpt(lines, data.d_zone); + ZoneParserTNG zpt(lines, d_zone); zpt.setMaxGenerateSteps(0); while (zpt.get(drr)) { DNSRecord dr(drr); - data.parseDRForCache(dr); + parseDRForCache(dr); } } // Rerun, now inserting the rrsets into the cache with associated sigs - data.d_now = time(nullptr); - for (const auto& [key, v] : data.d_all) { + d_now = time(nullptr); + time_t refreshFromSoa = 0; + for (const auto& [key, v] : d_all) { const auto& [qname, qtype] = key; switch (qtype) { case QType::NSEC: @@ -243,23 +229,68 @@ time_t RecZoneToCache::ZoneToCache(const Config& config) case QType::RRSIG: break; default: { + if (qtype == QType::SOA) { + const auto& rr = getRR(v[0]); + refreshFromSoa = rr->d_st.refresh; + } + vector> sigsrr; - auto it = data.d_sigs.find(key); - if (it != data.d_sigs.end()) { + auto it = d_sigs.find(key); + if (it != d_sigs.end()) { for (const auto& sig : it->second) { sigsrr.push_back(sig); } } - bool auth = data.isRRSetAuth(qname, qtype); + bool auth = isRRSetAuth(qname, qtype); // Same decision as updateCacheFromRecords() (we do not test for NSEC since we skip those completely) if (auth || (qtype == QType::NS || qtype == QType::A || qtype == QType::AAAA || qtype == QType::DS)) { - g_recCache->replace(data.d_now, qname, qtype, v, sigsrr, - std::vector>(), auth, data.d_zone); + g_recCache->replace(d_now, qname, qtype, v, sigsrr, + std::vector>(), auth, d_zone); } break; } } } - return data.d_refresh; + return refreshFromSoa; +} + +// Config must be a copy, so call by value! +void RecZoneToCache::ZoneToCache(RecZoneToCache::Config config, uint64_t configGeneration) +{ + setThreadName("pdns-r/ztc/" + config.d_zone); + auto luaconfsLocal = g_luaconfs.getLocal(); + + auto log = g_slog->withName("ztc")->withValues("zone", Logging::Loggable(config.d_zone)); + + while (true) { + if (luaconfsLocal->generation != configGeneration) { + /* the configuration has been reloaded, meaning that a new thread + has been started to handle that zone and we are now obsolete. + */ + log->info("A more recent configuration has been found, stopping the old update thread"); + return; + } + + time_t refresh = config.d_retryOnError; + try { + ZoneData data(log); + time_t refreshFromSoa = data.ZoneToCache(config); + refresh = config.d_refreshPeriod; + if (refresh == 0) { + refresh = refreshFromSoa; + } + log->info("Loaded zone into cache", "refresh", Logging::Loggable(refresh)); + } + catch (const PDNSException &e) { + log->info("Unable to load zone into cache, will retry", "exception", Logging::Loggable(e.reason), "refresh", Logging::Loggable(refresh)); + } + catch (const std::runtime_error& e) { + log->info("Unable to load zone into cache, will retry", "exception", Logging::Loggable(e.what()), "refresh", Logging::Loggable(refresh)); + } + catch (...) { + log->info("Unable to load zone into cache, will retry", "exception", Logging::Loggable("unknown"), "refresh", Logging::Loggable(refresh)); + } + sleep(refresh); + } } diff --git a/pdns/recursordist/rec-zonetocache.hh b/pdns/recursordist/rec-zonetocache.hh index 8d80913e56..81e86cf2c2 100644 --- a/pdns/recursordist/rec-zonetocache.hh +++ b/pdns/recursordist/rec-zonetocache.hh @@ -37,10 +37,9 @@ public: ComboAddress d_local; // local address TSIGTriplet d_tt; // Authentication data size_t d_maxReceivedBytes{0}; // Maximum size - uint32_t d_timeout{20}; // timeout in seconds + time_t d_retryOnError{60}; // Retry on error + time_t d_refreshPeriod{0}; // Take from SOA by default + uint32_t d_timeout{30}; // timeout in seconds }; - static time_t ZonesToCache(const std::vector&); - -private: - static time_t ZoneToCache(const Config& config); + static void ZoneToCache(Config config, uint64_t gen); };