From: Otto Date: Wed, 17 Mar 2021 09:39:01 +0000 (+0100) Subject: Insert hints as non-auth into cache, so info received from the net is X-Git-Tag: rec-4.5.0-beta1~5^2~4 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=fa9fb323b7e4ebde210ff430b36cd190af4e1272;p=thirdparty%2Fpdns.git Insert hints as non-auth into cache, so info received from the net is recorded in the cache. Also make sure the root NS refresh happens more often if max-cache-ttl is low. This is needed as the records no longer maintain the 1000 hours TTL. In the existing setup, a reprime (with potential outdated info) was done at that point in time since all root-server address records would expire at the same time. Lastly, fix a infinite (caught by depth check) recursion in getBestNSFromCache(). Fixes #10177. --- diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index aadd7a2e7c..a831c95e7b 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -3514,8 +3514,8 @@ static void houseKeeping(void *) } last_RC_prune = now.tv_sec; } - // XXX !!! global - if (now.tv_sec - last_rootupdate > 7200) { + // Divide by 12 to get the original 2 hour cycle if s_maxcachettl is default (1 day) + if (now.tv_sec - last_rootupdate > max(SyncRes::s_maxcachettl / 12, 10U)) { int res = SyncRes::getRootNS(g_now, nullptr, 0); if (!res) { last_rootupdate=now.tv_sec; diff --git a/pdns/reczones.cc b/pdns/reczones.cc index c777b0aa4e..434ac53230 100644 --- a/pdns/reczones.cc +++ b/pdns/reczones.cc @@ -34,7 +34,8 @@ extern char** g_argv; static thread_local set t_rootNSZones; -static void insertIntoRootNSZones(const DNSName &name) { +static void insertIntoRootNSZones(const DNSName& name) +{ // do not insert dot, wiping dot's NS records from the cache in primeRootNSZones() // will cause infinite recursion if (!name.isRoot()) { @@ -50,34 +51,48 @@ bool primeHints(time_t ignored) vector nsset; t_rootNSZones.clear(); - if(::arg()["hint-file"].empty()) { + time_t now = time(nullptr); + + if (::arg()["hint-file"].empty()) { DNSRecord arr, aaaarr, nsrr; - nsrr.d_name=g_rootdnsname; - arr.d_type=QType::A; - aaaarr.d_type=QType::AAAA; - nsrr.d_type=QType::NS; - arr.d_ttl=aaaarr.d_ttl=nsrr.d_ttl=time(0)+3600000; - - for(char c='a';c<='m';++c) { + nsrr.d_name = g_rootdnsname; + arr.d_type = QType::A; + aaaarr.d_type = QType::AAAA; + nsrr.d_type = QType::NS; + + arr.d_ttl = aaaarr.d_ttl = nsrr.d_ttl = now + 3600000; + + for (char c = 'a'; c <= 'm'; ++c) { char templ[40]; - strncpy(templ,"a.root-servers.net.", sizeof(templ) - 1); - templ[sizeof(templ)-1] = '\0'; - *templ=c; - aaaarr.d_name=arr.d_name=DNSName(templ); + strncpy(templ, "a.root-servers.net.", sizeof(templ) - 1); + templ[sizeof(templ) - 1] = '\0'; + *templ = c; + aaaarr.d_name = arr.d_name = DNSName(templ); insertIntoRootNSZones(arr.d_name.getLastLabel()); - nsrr.d_content=std::make_shared(DNSName(templ)); - arr.d_content=std::make_shared(ComboAddress(rootIps4[c-'a'])); + nsrr.d_content = std::make_shared(DNSName(templ)); + arr.d_content = std::make_shared(ComboAddress(rootIps4[c - 'a'])); vector aset; aset.push_back(arr); - g_recCache->replace(time(0), DNSName(templ), QType(QType::A), aset, vector>(), vector>(), true, g_rootdnsname, boost::none, boost::none, validationState, from); // auth, nuke it all - if (rootIps6[c-'a'] != NULL) { - aaaarr.d_content=std::make_shared(ComboAddress(rootIps6[c-'a'])); + /* + * Originally the hint records were inserted with the auth flag set, with the consequence that data from AUTHORITY and + * ADDITIONAL sections (as seen in a ,. NS response) were not used. This (together with the long ttl) caused outdated + * hint to be kept in cache. So insert as non-auth, and the extra sections in the . NS refreshing cause the cached + * records to be updated with up-to-date information received from a real root server. + * + * Note that if a user query is done for one of the root-server.net names, it will be inserted into the cache with the + * auth bit set. Further NS refreshes will not update that entry. If all root names are queried at the same time by a user, + * all root-server.net names will be marked auth and will expired at the same time. A re-prime is then triggered, + * as before, when the records were inserted with the auth bit set and the TTD comes. + */ + g_recCache->replace(now, DNSName(templ), QType(QType::A), aset, vector>(), vector>(), false, g_rootdnsname, boost::none, boost::none, validationState, from); // auth, nuke it all + if (rootIps6[c - 'a'] != NULL) { + aaaarr.d_content = std::make_shared(ComboAddress(rootIps6[c - 'a'])); vector aaaaset; aaaaset.push_back(aaaarr); - g_recCache->replace(time(0), DNSName(templ), QType(QType::AAAA), aaaaset, vector>(), vector>(), true, g_rootdnsname, boost::none, boost::none, validationState, from); + g_recCache->replace(now, DNSName(templ), QType(QType::AAAA), aaaaset, vector>(), vector>(), false, g_rootdnsname, boost::none, boost::none, validationState, from); } - + nsset.push_back(nsrr); } } @@ -89,21 +104,23 @@ bool primeHints(time_t ignored) set seenA; set seenAAAA; - while(zpt.get(rr)) { - rr.ttl+=time(0); - if(rr.qtype.getCode()==QType::A) { + while (zpt.get(rr)) { + rr.ttl += now; + if (rr.qtype.getCode() == QType::A) { seenA.insert(rr.qname); vector aset; aset.push_back(DNSRecord(rr)); - g_recCache->replace(time(0), rr.qname, QType(QType::A), aset, vector>(), vector>(), true, g_rootdnsname, boost::none, boost::none, validationState, from); // auth, etc see above - } else if(rr.qtype.getCode()==QType::AAAA) { + g_recCache->replace(now, rr.qname, QType(QType::A), aset, vector>(), vector>(), true, g_rootdnsname, boost::none, boost::none, validationState, from); // auth, etc see above + } + else if (rr.qtype.getCode() == QType::AAAA) { seenAAAA.insert(rr.qname); vector aaaaset; aaaaset.push_back(DNSRecord(rr)); - g_recCache->replace(time(0), rr.qname, QType(QType::AAAA), aaaaset, vector>(), vector>(), true, g_rootdnsname, boost::none, boost::none, validationState, from); - } else if(rr.qtype.getCode()==QType::NS) { + g_recCache->replace(now, rr.qname, QType(QType::AAAA), aaaaset, vector>(), vector>(), true, g_rootdnsname, boost::none, boost::none, validationState, from); + } + else if (rr.qtype.getCode() == QType::NS) { seenNS.insert(DNSName(rr.content)); - rr.content=toLower(rr.content); + rr.content = toLower(rr.content); nsset.push_back(DNSRecord(rr)); } insertIntoRootNSZones(rr.qname.getLastLabel()); @@ -111,38 +128,37 @@ bool primeHints(time_t ignored) // Check reachability of A and AAAA records bool reachableA = false, reachableAAAA = false; - for (auto const& r: seenA) { + for (auto const& r : seenA) { if (seenNS.count(r)) { reachableA = true; break; } } - for (auto const& r: seenAAAA) { + for (auto const& r : seenAAAA) { if (seenNS.count(r)) { reachableAAAA = true; break; } } if (SyncRes::s_doIPv4 && !SyncRes::s_doIPv6 && !reachableA) { - g_log<doWipeCache(g_rootdnsname, false, QType::NS); - g_recCache->replace(time(0), g_rootdnsname, QType(QType::NS), nsset, vector>(), vector>(), false, g_rootdnsname, boost::none, boost::none, validationState, from); // and stuff in the cache + g_recCache->replace(now, g_rootdnsname, QType(QType::NS), nsset, vector>(), vector>(), false, g_rootdnsname, boost::none, boost::none, validationState, from); // and stuff in the cache return true; } - // Do not only put the root hints into the cache, but also make sure // the NS records of the top level domains of the names of the root // servers are in the cache. We need these to correctly determine the @@ -164,8 +180,8 @@ void primeRootNSZones(bool dnssecmode, unsigned int depth) // beginResolve() can yield to another mthread that could trigger t_rootNSZones updates, // so make a local copy - set copy(t_rootNSZones); - for (const auto & qname: copy) { + set copy(t_rootNSZones); + for (const auto& qname : copy) { g_recCache->doWipeCache(qname, false, QType::NS); vector ret; sr.beginResolve(qname, QType(QType::NS), QClass::IN, ret, depth + 1); @@ -175,101 +191,101 @@ void primeRootNSZones(bool dnssecmode, unsigned int depth) static void makeNameToIPZone(std::shared_ptr newMap, const DNSName& hostname, const string& ip) { SyncRes::AuthDomain ad; - ad.d_rdForward=false; + ad.d_rdForward = false; DNSRecord dr; - dr.d_name=hostname; - dr.d_place=DNSResourceRecord::ANSWER; - dr.d_ttl=86400; - dr.d_type=QType::SOA; + dr.d_name = hostname; + dr.d_place = DNSResourceRecord::ANSWER; + dr.d_ttl = 86400; + dr.d_type = QType::SOA; dr.d_class = 1; dr.d_content = DNSRecordContent::mastermake(QType::SOA, 1, "localhost. root 1 604800 86400 2419200 604800"); - + ad.d_records.insert(dr); - dr.d_type=QType::NS; - dr.d_content=std::make_shared("localhost."); + dr.d_type = QType::NS; + dr.d_content = std::make_shared("localhost."); ad.d_records.insert(dr); - - dr.d_type=QType::A; + + dr.d_type = QType::A; dr.d_content = DNSRecordContent::mastermake(QType::A, 1, ip); ad.d_records.insert(dr); - - if(newMap->count(dr.d_name)) { - g_log<count(dr.d_name)) { + g_log << Logger::Warning << "Hosts file will not overwrite zone '" << dr.d_name << "' already loaded" << endl; } else { - g_log< newMap, const vector& parts) { - string address=parts[0]; + string address = parts[0]; vector ipparts; - stringtok(ipparts, address,"."); - + stringtok(ipparts, address, "."); + SyncRes::AuthDomain ad; - ad.d_rdForward=false; + ad.d_rdForward = false; DNSRecord dr; - for(int n=ipparts.size()-1; n>=0 ; --n) { + for (int n = ipparts.size() - 1; n >= 0; --n) { dr.d_name.appendRawLabel(ipparts[n]); } dr.d_name.appendRawLabel("in-addr"); dr.d_name.appendRawLabel("arpa"); dr.d_class = 1; - dr.d_place=DNSResourceRecord::ANSWER; - dr.d_ttl=86400; - dr.d_type=QType::SOA; - dr.d_content=DNSRecordContent::mastermake(QType::SOA, 1, "localhost. root 1 604800 86400 2419200 604800"); - + dr.d_place = DNSResourceRecord::ANSWER; + dr.d_ttl = 86400; + dr.d_type = QType::SOA; + dr.d_content = DNSRecordContent::mastermake(QType::SOA, 1, "localhost. root 1 604800 86400 2419200 604800"); + ad.d_records.insert(dr); - dr.d_type=QType::NS; - dr.d_content=std::make_shared(DNSName("localhost.")); + dr.d_type = QType::NS; + dr.d_content = std::make_shared(DNSName("localhost.")); ad.d_records.insert(dr); - dr.d_type=QType::PTR; + dr.d_type = QType::PTR; - if(ipparts.size()==4) // otherwise this is a partial zone - for(unsigned int n=1; n < parts.size(); ++n) { - dr.d_content=DNSRecordContent::mastermake(QType::PTR, 1, DNSName(parts[n]).toString()); // XXX FIXME DNSNAME PAIN CAN THIS BE RIGHT? + if (ipparts.size() == 4) // otherwise this is a partial zone + for (unsigned int n = 1; n < parts.size(); ++n) { + dr.d_content = DNSRecordContent::mastermake(QType::PTR, 1, DNSName(parts[n]).toString()); // XXX FIXME DNSNAME PAIN CAN THIS BE RIGHT? ad.d_records.insert(dr); } - if(newMap->count(dr.d_name)) { - g_log<count(dr.d_name)) { + g_log << Logger::Warning << "Will not overwrite zone '" << dr.d_name << "' already loaded" << endl; } else { - if(ipparts.size()==4) - g_log< servers; stringtok(servers, input, sepa); ad.d_servers.clear(); - for(vector::const_iterator iter = servers.begin(); iter != servers.end(); ++iter) { - if(verbose && iter != servers.begin()) - g_log<<", "; + for (vector::const_iterator iter = servers.begin(); iter != servers.end(); ++iter) { + if (verbose && iter != servers.begin()) + g_log << ", "; - ComboAddress addr=parseIPAndPort(*iter, 53); - if(verbose) - g_log< newmap) @@ -280,19 +296,19 @@ static void* pleaseUseNewSDomainsMap(std::shared_ptr newma string reloadAuthAndForwards() { - std::shared_ptr original=SyncRes::getDomainMap(); - + std::shared_ptr original = SyncRes::getDomainMap(); + try { - g_log< extraConfigs; ::arg().gatherIncludes(extraConfigs); - for(const std::string& fn : extraConfigs) { - if(!::arg().preParseFile(fn.c_str(), "forward-zones", ::arg()["forward-zones"])) - throw runtime_error("Unable to re-parse configuration file include '"+fn+"'"); + for (const std::string& fn : extraConfigs) { + if (!::arg().preParseFile(fn.c_str(), "forward-zones", ::arg()["forward-zones"])) + throw runtime_error("Unable to re-parse configuration file include '" + fn + "'"); ::arg().preParseFile(fn.c_str(), "forward-zones-file", ::arg()["forward-zones-file"]); ::arg().preParseFile(fn.c_str(), "forward-zones-recurse", ::arg()["forward-zones-recurse"]); - ::arg().preParseFile(fn.c_str(), "auth-zones",::arg()["auth-zones"]); - ::arg().preParseFile(fn.c_str(), "export-etc-hosts",::arg()["export-etc-hosts"]); - ::arg().preParseFile(fn.c_str(), "serve-rfc1918",::arg()["serve-rfc1918"]); + ::arg().preParseFile(fn.c_str(), "auth-zones", ::arg()["auth-zones"]); + ::arg().preParseFile(fn.c_str(), "export-etc-hosts", ::arg()["export-etc-hosts"]); + ::arg().preParseFile(fn.c_str(), "serve-rfc1918", ::arg()["serve-rfc1918"]); } ::arg().preParse(g_argc, g_argv, "forward-zones"); @@ -326,33 +342,33 @@ string reloadAuthAndForwards() // purge both original and new names std::set oldAndNewDomains; - for(const auto& i : *newDomainMap) { + for (const auto& i : *newDomainMap) { oldAndNewDomains.insert(i.first); } - if(original) { - for(const auto& i : *original) { + if (original) { + for (const auto& i : *original) { oldAndNewDomains.insert(i.first); } } - for(const auto& i : oldAndNewDomains) { + for (const auto& i : oldAndNewDomains) { g_recCache->doWipeCache(i, true, 0xffff); - broadcastAccFunction([&]{return pleaseWipePacketCache(i, true, 0xffff);}); + broadcastAccFunction([&] { return pleaseWipePacketCache(i, true, 0xffff); }); g_negCache->wipe(i, true); } - broadcastFunction([=]{return pleaseUseNewSDomainsMap(newDomainMap);}); + broadcastFunction([=] { return pleaseUseNewSDomainsMap(newDomainMap); }); return "ok\n"; } - catch(std::exception& e) { - g_log< parseAuthAndForwards() auto newMap = std::make_shared(); typedef vector parts_t; - parts_t parts; - const char *option_names[3]={"auth-zones", "forward-zones", "forward-zones-recurse"}; - for(int n=0; n < 3 ; ++n ) { + parts_t parts; + const char* option_names[3] = {"auth-zones", "forward-zones", "forward-zones-recurse"}; + for (int n = 0; n < 3; ++n) { parts.clear(); stringtok(parts, ::arg()[option_names[n]], " ,\t\n\r"); - for(parts_t::const_iterator iter = parts.begin(); iter != parts.end(); ++iter) { + for (parts_t::const_iterator iter = parts.begin(); iter != parts.end(); ++iter) { SyncRes::AuthDomain ad; if ((*iter).find('=') == string::npos) throw PDNSException("Error parsing '" + *iter + "', missing ="); - pair headers=splitField(*iter, '='); + pair headers = splitField(*iter, '='); boost::trim(headers.first); boost::trim(headers.second); // headers.first=toCanonic("", headers.first); - if(n==0) { + if (n == 0) { ad.d_rdForward = false; - g_log<(fopen(::arg()["forward-zones-file"].c_str(), "r"), fclose); - if(!fp) { - throw PDNSException("Error opening forward-zones-file '"+::arg()["forward-zones-file"]+"': "+stringerror()); + auto fp = std::unique_ptr(fopen(::arg()["forward-zones-file"].c_str(), "r"), fclose); + if (!fp) { + throw PDNSException("Error opening forward-zones-file '" + ::arg()["forward-zones-file"] + "': " + stringerror()); } string line; - int linenum=0; + int linenum = 0; uint64_t before = newMap->size(); - while(linenum++, stringfgets(fp.get(), line)) { + while (linenum++, stringfgets(fp.get(), line)) { boost::trim(line); if (line[0] == '#') // Comment line, skip to the next line continue; string domain, instructions; - tie(domain, instructions)=splitField(line, '='); + tie(domain, instructions) = splitField(line, '='); instructions = splitField(instructions, '#').first; // Remove EOL comments boost::trim(domain); boost::trim(instructions); - if(domain.empty() && instructions.empty()) { // empty line + if (domain.empty() && instructions.empty()) { // empty line continue; } - if(boost::starts_with(domain,"+")) { - domain=domain.c_str()+1; + if (boost::starts_with(domain, "+")) { + domain = domain.c_str() + 1; ad.d_rdForward = true; } else ad.d_rdForward = false; - if(domain.empty()) { - throw PDNSException("Error parsing line "+std::to_string(linenum)+" of " +::arg()["forward-zones-file"]); + if (domain.empty()) { + throw PDNSException("Error parsing line " + std::to_string(linenum) + " of " + ::arg()["forward-zones-file"]); } try { convertServersForAD(instructions, ad, ",; ", false); } - catch(...) { - throw PDNSException("Conversion error parsing line "+std::to_string(linenum)+" of " +::arg()["forward-zones-file"]); + catch (...) { + throw PDNSException("Conversion error parsing line " + std::to_string(linenum) + " of " + ::arg()["forward-zones-file"]); } ad.d_name = DNSName(domain); - (*newMap)[ad.d_name]=ad; + (*newMap)[ad.d_name] = ad; } - g_log<size() - before<<" forwarding instructions from file '"<<::arg()["forward-zones-file"]<<"'"<size() - before << " forwarding instructions from file '" << ::arg()["forward-zones-file"] << "'" << endl; } - if(::arg().mustDo("export-etc-hosts")) { + if (::arg().mustDo("export-etc-hosts")) { string line; - string fname=::arg()["etc-hosts-file"]; - + string fname = ::arg()["etc-hosts-file"]; + ifstream ifs(fname.c_str()); - if(!ifs) { - g_log< parseAuthAndForwards() } } } - if(::arg().mustDo("serve-rfc1918")) { - g_log<