From: Otto Moerbeek Date: Wed, 9 Mar 2022 13:37:16 +0000 (+0100) Subject: wip X-Git-Tag: rec-4.7.0-beta1^2~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6715fc093d3357c7475f88b897385ae40021c907;p=thirdparty%2Fpdns.git wip --- diff --git a/pdns/rec_channel_rec.cc b/pdns/rec_channel_rec.cc index 3855a8a38a..845d78a463 100644 --- a/pdns/rec_channel_rec.cc +++ b/pdns/rec_channel_rec.cc @@ -387,6 +387,11 @@ static uint64_t* pleaseDumpNonResolvingNS(int fd) return new uint64_t(SyncRes::doDumpNonResolvingNS(fd)); } +static uint64_t* pleaseDumpDoTProbeMap(int fd) +{ + return new uint64_t(SyncRes::doDumpDoTProbeMap(fd)); +} + // Generic dump to file command static RecursorControlChannel::Answer doDumpToFile(int s, uint64_t* (*function)(int s), const string& name, bool threads = true) { @@ -1904,6 +1909,7 @@ RecursorControlChannel::Answer RecursorControlParser::getAnswer(int s, const str "clear-nta [DOMAIN]... Clear the Negative Trust Anchor for DOMAINs, if no DOMAIN is specified, remove all\n" "clear-ta [DOMAIN]... Clear the Trust Anchor for DOMAINs\n" "dump-cache dump cache contents to the named file\n" + "dump-dot-probe-map dump the contents of the DoT probe map to the named file\n" "dump-edns [status] dump EDNS status to the named file\n" "dump-failedservers dump the failed servers to the named file\n" "dump-non-resolving dump non-resolving nameservers addresses to the named file\n" @@ -1977,6 +1983,9 @@ RecursorControlChannel::Answer RecursorControlParser::getAnswer(int s, const str if (cmd == "dump-cache") { return doDumpCache(s); } + if (cmd == "dump-dot-probe-map") { + return doDumpToFile(s, pleaseDumpDoTProbeMap, cmd, false); + } if (cmd == "dump-ednsstatus" || cmd == "dump-edns") { return doDumpToFile(s, pleaseDumpEDNSMap, cmd); } diff --git a/pdns/rec_control.cc b/pdns/rec_control.cc index 4b8d32b74e..0bcf476e10 100644 --- a/pdns/rec_control.cc +++ b/pdns/rec_control.cc @@ -96,7 +96,8 @@ int main(int argc, char** argv) "dump-rpz", "dump-throttlemap", "dump-non-resolving", - "dump-saved-parent-ns-sets"}; + "dump-saved-parent-ns-sets", + "dump-dot-probe-map"}; try { initArguments(argc, argv); string sockname = "pdns_recursor"; diff --git a/pdns/recursordist/rec-main.cc b/pdns/recursordist/rec-main.cc index ba6c1a48c7..6fe927e428 100644 --- a/pdns/recursordist/rec-main.cc +++ b/pdns/recursordist/rec-main.cc @@ -1894,7 +1894,7 @@ static void houseKeeping(void*) // Likley a few handler tasks could be moved to the taskThread if (info.isTaskThread()) { // TaskQueue is run always - runTaskOnce(g_logCommonErrors); + runTasks(g_logCommonErrors, 10); static PeriodicTask ztcTask{"ZTC", 60}; static map ztcStates; diff --git a/pdns/recursordist/rec-taskqueue.cc b/pdns/recursordist/rec-taskqueue.cc index 22f03df404..f1e7c5c05b 100644 --- a/pdns/recursordist/rec-taskqueue.cc +++ b/pdns/recursordist/rec-taskqueue.cc @@ -163,6 +163,65 @@ static void resolve(const struct timeval& now, bool logErrors, const pdns::Resol } } +static void tryDoT(const struct timeval& now, bool logErrors, const pdns::ResolveTask& task) noexcept +{ + auto log = g_slog->withName("taskq")->withValues("method", Logging::Loggable("tryDoT"), "name", Logging::Loggable(task.d_qname), "qtype", Logging::Loggable(QType(task.d_qtype).toString()), "ip", Logging::Loggable(task.d_ip)); + const string msg = "Exception while running a background tryDoT task"; + SyncRes sr(now); + vector ret; + sr.setRefreshAlmostExpired(false); + bool ex = true; + try { + log->info(Logr::Warning, "trying DoT"); + bool ok = sr.tryDoT(task.d_qname, QType(task.d_qtype), DNSName("auth"), DNSName("ns"), task.d_ip, now.tv_sec); + ex = false; + log->info(Logr::Warning, "done", "ok", Logging::Loggable(ok)); + } + catch (const std::exception& e) { + log->error(Logr::Error, msg, e.what()); + } + catch (const PDNSException& e) { + log->error(Logr::Error, msg, e.reason); + } + catch (const ImmediateServFailException& e) { + if (logErrors) { + log->error(Logr::Error, msg, e.reason); + } + } + catch (const PolicyHitException& e) { + if (logErrors) { + log->error(Logr::Notice, msg, "PolicyHit"); + } + } + catch (...) { + log->error(Logr::Error, msg, "Unexpectec exception"); + } + if (ex) { + ++s_resolve_tasks.exceptions; + } + else { + ++s_resolve_tasks.run; + } +} + +void runTasks(size_t max, bool logErrors) +{ + for (size_t count = 0; count < max; count++) { + pdns::ResolveTask task; + { + auto lock = s_taskQueue.lock(); + if (lock->queue.empty()) { + return; + } + task = lock->queue.pop(); + } + bool expired = task.run(logErrors); + if (expired) { + s_taskQueue.lock()->queue.incExpired(); + } + } +} + void runTaskOnce(bool logErrors) { pdns::ResolveTask task; @@ -186,9 +245,10 @@ void pushAlmostExpiredTask(const DNSName& qname, uint16_t qtype, time_t deadline log->error(Logr::Error, "Cannot push task", "qtype unsupported"); return; } - pdns::ResolveTask task{qname, qtype, deadline, true, resolve}; - s_taskQueue.lock()->queue.push(std::move(task)); - ++s_almost_expired_tasks.pushed; + pdns::ResolveTask task{qname, qtype, deadline, true, resolve, {}}; + if (s_taskQueue.lock()->queue.push(std::move(task))) { + ++s_almost_expired_tasks.pushed; + } } void pushResolveTask(const DNSName& qname, uint16_t qtype, time_t now, time_t deadline) @@ -198,13 +258,30 @@ void pushResolveTask(const DNSName& qname, uint16_t qtype, time_t now, time_t de log->error(Logr::Error, "Cannot push task", "qtype unsupported"); return; } - pdns::ResolveTask task{qname, qtype, deadline, false, resolve}; + pdns::ResolveTask task{qname, qtype, deadline, false, resolve, {}}; auto lock = s_taskQueue.lock(); bool inserted = lock->rateLimitSet.insert(now, task); if (inserted) { - lock->queue.push(std::move(task)); - ++s_resolve_tasks.pushed; + if (lock->queue.push(std::move(task))) { + ++s_resolve_tasks.pushed; + } + } +} + +bool pushTryDoTTask(const DNSName& qname, uint16_t qtype, const ComboAddress& ip, time_t deadline) +{ + if (SyncRes::isUnsupported(qtype)) { + auto log = g_slog->withName("taskq")->withValues("name", Logging::Loggable(qname), "qtype", Logging::Loggable(QType(qtype).toString())); + log->error(Logr::Error, "Cannot push task", "qtype unsupported"); + return false; + } + + pdns::ResolveTask task{qname, qtype, deadline, false, tryDoT, ip}; + bool pushed = s_taskQueue.lock()->queue.push(std::move(task)); + if (pushed) { + ++s_almost_expired_tasks.pushed; } + return pushed; } uint64_t getTaskPushes() diff --git a/pdns/recursordist/rec-taskqueue.hh b/pdns/recursordist/rec-taskqueue.hh index 84f165b7e1..06633b2f08 100644 --- a/pdns/recursordist/rec-taskqueue.hh +++ b/pdns/recursordist/rec-taskqueue.hh @@ -25,13 +25,17 @@ #include class DNSName; +union ComboAddress; + namespace pdns { struct ResolveTask; } +void runTasks(size_t max, bool logErrors); void runTaskOnce(bool logErrors); void pushAlmostExpiredTask(const DNSName& qname, uint16_t qtype, time_t deadline); void pushResolveTask(const DNSName& qname, uint16_t qtype, time_t now, time_t deadline); +bool pushTryDoTTask(const DNSName& qname, uint16_t qtype, const ComboAddress& ip, time_t deadline); void taskQueueClear(); pdns::ResolveTask taskQueuePop(); diff --git a/pdns/recursordist/taskqueue.cc b/pdns/recursordist/taskqueue.cc index f933f99fac..8382cc4f4b 100644 --- a/pdns/recursordist/taskqueue.cc +++ b/pdns/recursordist/taskqueue.cc @@ -28,13 +28,14 @@ namespace pdns { -void TaskQueue::push(ResolveTask&& task) +bool TaskQueue::push(ResolveTask&& task) { // Insertion fails if it's already there, no problem since we're already scheduled - auto result = d_queue.insert(std::move(task)); - if (result.second) { + auto result = d_queue.insert(std::move(task)).second; + if (result) { d_pushes++; } + return result; } ResolveTask TaskQueue::pop() @@ -66,3 +67,11 @@ bool ResolveTask::run(bool logErrors) } } /* namespace pdns */ + +namespace boost +{ +size_t hash_value(const ComboAddress& a) +{ + return ComboAddress::addressOnlyHash()(a); +} +} diff --git a/pdns/recursordist/taskqueue.hh b/pdns/recursordist/taskqueue.hh index 1faaa30ef1..a57a81fe6b 100644 --- a/pdns/recursordist/taskqueue.hh +++ b/pdns/recursordist/taskqueue.hh @@ -24,6 +24,12 @@ #include #include +union ComboAddress; +namespace boost +{ + size_t hash_value(const ComboAddress&); +} + #include #include #include @@ -32,8 +38,10 @@ #include #include "dnsname.hh" +#include "iputils.hh" #include "qtype.hh" + namespace pdns { using namespace ::boost::multi_index; @@ -47,11 +55,13 @@ struct ResolveTask bool d_refreshMode; // Whether to run this task in regular mode (false) or in the mode that refreshes almost expired tasks // Use a function pointer as comparing std::functions is a nuisance void (*d_func)(const struct timeval& now, bool logErrors, const ResolveTask& task); + ComboAddress d_ip; bool operator<(const ResolveTask& a) const { - return std::tie(d_qname, d_qtype, d_refreshMode, d_func) < std::tie(a.d_qname, a.d_qtype, a.d_refreshMode, a.d_func); + return std::tie(d_qname, d_qtype, d_refreshMode, d_func, d_ip) < std::tie(a.d_qname, a.d_qtype, a.d_refreshMode, a.d_func, d_ip); } + bool run(bool logErrors); }; @@ -68,7 +78,7 @@ public: return d_queue.size(); } - void push(ResolveTask&& task); + bool push(ResolveTask&& task); ResolveTask pop(); uint64_t getPushes() @@ -107,7 +117,9 @@ private: composite_key, member, - member>>, + member, + member, + member>>, sequenced>>> queue_t; @@ -117,3 +129,4 @@ private: }; } + diff --git a/pdns/syncres.cc b/pdns/syncres.cc index b2dd490489..935c129b31 100644 --- a/pdns/syncres.cc +++ b/pdns/syncres.cc @@ -286,6 +286,30 @@ const std::unordered_set SyncRes::s_redirectionQTypes = {QType::CNAME, QT static LockGuarded> s_fails; static LockGuarded> s_nonresolving; +struct DoTStatus +{ + enum Status : uint8_t { Unknown, Busy, Bad, Good }; + DNSName d_auth; + time_t d_last; + Status d_status{Unknown}; + std::string toString() const + { + const std::string n[] = { "Unknown", "Busy", "Bad", "Good"}; + unsigned int v = static_cast(d_status); + return v >= sizeof(n) ? "?" : n[v]; + } +}; + +class DotMap: public map +{ +public: + uint64_t d_numBusy{0}; +}; +LockGuarded s_dotMap; + +const time_t dotFailWait = 24 * 3600; +const time_t dotSuccessWait = 3 * 24 * 3600; + unsigned int SyncRes::s_maxnegttl; unsigned int SyncRes::s_maxbogusttl; unsigned int SyncRes::s_maxcachettl; @@ -1148,6 +1172,30 @@ uint64_t SyncRes::doDumpSavedParentNSSets(int fd) char tmp[26]; fprintf(fp.get(), "%s\t%" PRIu64 "\t%s\n", i.d_domain.toString().c_str(), i.d_count, timestamp(i.d_ttd, tmp, sizeof(tmp))); } + return count; +} + +uint64_t SyncRes::doDumpDoTProbeMap(int fd) +{ + int newfd = dup(fd); + if (newfd == -1) { + return 0; + } + auto fp = std::unique_ptr(fdopen(newfd, "w"), fclose); + if (!fp) { + close(newfd); + return 0; + } + fprintf(fp.get(), "; DoT probing map follows\n"); + fprintf(fp.get(), "; ip\tstatus\ttimestamp\n"); + uint64_t count=0; + + // We get a copy, so the I/O does not need to happen while holding the lock + for (const auto& i : *s_dotMap.lock()) { + count++; + char tmp[26]; + fprintf(fp.get(), "%s\t%s\t%s\t%s", i.first.toString().c_str(), i.second.d_auth.toString().c_str(), i.second.toString().c_str(), ctime_r(&i.second.d_last, tmp)); + } return count; } @@ -4647,6 +4695,77 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co return done; } + +static void submitTryDotTask(ComboAddress address, const DNSName& auth, time_t now) +{ + if (address.getPort() == 853) { + return; + } + address.setPort(853); + auto lock = s_dotMap.lock(); + auto it = lock->emplace(address, DoTStatus{auth, now}).first; + if (it->second.d_status == DoTStatus::Busy) { + cerr << "Already probing DoT for " << address.toString() << endl << endl; + return; + } + if (it->second.d_status == DoTStatus::Bad && it->second.d_last + dotFailWait > now) { + cerr << "Bad DoT for " << address.toString()<< endl << endl; + return; + } + if (it->second.d_status == DoTStatus::Good && it->second.d_last + dotSuccessWait > now) { + cerr << "Good DoT for " << address.toString() << endl << endl; + return; + } + it->second.d_last = now; + bool pushed = pushTryDoTTask(auth, QType::SOA, address, std::numeric_limits::max()); + if (pushed) { + cerr << "Probing DoT for " << address.toString() << endl << endl; + it->second.d_status = DoTStatus::Busy; + ++lock->d_numBusy; + } else { + cerr << "NOT Probing DoT for " << address.toString() << endl << endl; + } +} + +static bool shouldDoDoT(ComboAddress address, time_t now) +{ + address.setPort(853); + auto lock = s_dotMap.lock(); + if (lock->d_numBusy > 1) { + cerr << "Busy DoT for " << address.toString() << endl << endl; + return false; + } + auto it = lock->find(address); + if (it == lock->end()) { + return false; + } + if (it->second.d_status == DoTStatus::Good && it->second.d_last + dotSuccessWait > now) { + cerr << "Doing DoT for " << address.toString() << endl << endl; + return true; + } + return false; +} + +bool SyncRes::tryDoT(const DNSName& qname, const QType qtype, const DNSName& auth, const DNSName& nsName, ComboAddress address, time_t now) +{ + LWResult lwr; + bool truncated; + bool spoofed; + boost::optional nm; + address.setPort(853); + bool ok = doResolveAtThisIP("", qname, qtype, lwr, nm, auth, false, false, nsName, address, true, true, truncated, spoofed); + ok = ok && lwr.d_rcode == RCode::NoError && lwr.d_records.size() > 0; + + auto lock = s_dotMap.lock(); + --lock->d_numBusy; + auto it = lock->find(address); + if (it != lock->end()) { + cerr << "DoT for " << address.toString() << ": " << ok << endl << endl; + it->second.d_status = ok ? DoTStatus::Good : DoTStatus::Bad; + } + return ok; +} + bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname, const QType qtype, LWResult& lwr, boost::optional& ednsmask, const DNSName& auth, bool const sendRDQuery, const bool wasForwarded, const DNSName& nsName, const ComboAddress& remoteIP, bool doTCP, bool doDoT, bool& truncated, bool& spoofed) { bool chained = false; @@ -5183,8 +5302,14 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con if (SyncRes::s_dot_to_port_853 && remoteIP->getPort() == 853) { doDoT = true; } + if (!doDoT && shouldDoDoT(*remoteIP, d_now.tv_sec)) { + doDoT = true; + } bool forceTCP = doDoT; + if (!doDoT) { + submitTryDotTask(*remoteIP, auth, d_now.tv_sec); + } if (!forceTCP) { gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery, wasForwarded, tns->first, *remoteIP, false, false, truncated, spoofed); diff --git a/pdns/syncres.hh b/pdns/syncres.hh index f1c13e55b3..98ca150256 100644 --- a/pdns/syncres.hh +++ b/pdns/syncres.hh @@ -253,6 +253,7 @@ public: static uint64_t doDumpFailedServers(int fd); static uint64_t doDumpNonResolvingNS(int fd); static uint64_t doDumpSavedParentNSSets(int fd); + static uint64_t doDumpDoTProbeMap(int fd); static int getRootNS(struct timeval now, asyncresolve_t asyncCallback, unsigned int depth); static void addDontQuery(const std::string& mask) @@ -395,6 +396,7 @@ public: explicit SyncRes(const struct timeval& now); int beginResolve(const DNSName &qname, QType qtype, QClass qclass, vector&ret, unsigned int depth = 0); + bool tryDoT(const DNSName& qname, QType qtype, const DNSName& auth, const DNSName& nsName, ComboAddress address, time_t); void setId(int id) {