]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
wip
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Wed, 9 Mar 2022 13:37:16 +0000 (14:37 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Tue, 12 Apr 2022 11:39:42 +0000 (13:39 +0200)
pdns/rec_channel_rec.cc
pdns/rec_control.cc
pdns/recursordist/rec-main.cc
pdns/recursordist/rec-taskqueue.cc
pdns/recursordist/rec-taskqueue.hh
pdns/recursordist/taskqueue.cc
pdns/recursordist/taskqueue.hh
pdns/syncres.cc
pdns/syncres.hh

index 3855a8a38a62681f0fa591b510bc961ad73dc990..845d78a46396c257f5920dd6de8de53eb16b3a2e 100644 (file)
@@ -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 <filename>            dump cache contents to the named file\n"
+            "dump-dot-probe-map <filename>    dump the contents of the DoT probe map to the named file\n"
             "dump-edns [status] <filename>    dump EDNS status to the named file\n"
             "dump-failedservers <filename>    dump the failed servers to the named file\n"
             "dump-non-resolving <filename>    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);
   }
index 4b8d32b74e5965b504919c5311e94514b974bfac..0bcf476e10a37b370d0e3b03b0aec96a0f23ae81 100644 (file)
@@ -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";
index ba6c1a48c7d3a0ad8b85697419314da309e3e6bc..6fe927e4281c669f0b824e302cc65b24f0749363 100644 (file)
@@ -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<DNSName, RecZoneToCache::State> ztcStates;
index 22f03df4041d10fd353f7ef358dfad3cf4f9cad8..f1e7c5c05bf4db61ada914863d07c553832e6a11 100644 (file)
@@ -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<DNSRecord> 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()
index 84f165b7e1a3ad13d18c5f9f775b38ba9f30d597..06633b2f08bea973c48924a1d301b4741daac293 100644 (file)
 #include <time.h>
 
 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();
 
index f933f99fac38af03853e24446ade2a74c2c5a8a1..8382cc4f4bc02c1c117144896780a312c4b83286 100644 (file)
 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);
+}
+}
index 1faaa30ef185dbb2bdfc8ceb4b9315ac7abccb03..a57a81fe6b91f94408a7cbf94aa98086a317723b 100644 (file)
 #include <sys/time.h>
 #include <thread>
 
+union ComboAddress;
+namespace boost
+{
+  size_t hash_value(const ComboAddress&);
+}
+
 #include <boost/multi_index_container.hpp>
 #include <boost/multi_index/hashed_index.hpp>
 #include <boost/multi_index/key_extractors.hpp>
 #include <boost/multi_index/tag.hpp>
 
 #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<ResolveTask,
                                   member<ResolveTask, DNSName, &ResolveTask::d_qname>,
                                   member<ResolveTask, uint16_t, &ResolveTask::d_qtype>,
-                                  member<ResolveTask, bool, &ResolveTask::d_refreshMode>>>,
+                                  member<ResolveTask, bool, &ResolveTask::d_refreshMode>,
+                                  member<ResolveTask, void (*)(const struct timeval& now, bool logErrors, const ResolveTask& task), &ResolveTask::d_func>,
+                                  member<ResolveTask, ComboAddress, &ResolveTask::d_ip>>>,
       sequenced<tag<SequencedTag>>>>
     queue_t;
 
@@ -117,3 +129,4 @@ private:
 };
 
 }
+
index b2dd49048917081062b1d4372750018f224e17d4..935c129b3177dcb6a678a243b967781e27af38b4 100644 (file)
@@ -286,6 +286,30 @@ const std::unordered_set<QType> SyncRes::s_redirectionQTypes = {QType::CNAME, QT
 static LockGuarded<fails_t<ComboAddress>> s_fails;
 static LockGuarded<fails_t<DNSName>> 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<unsigned int>(d_status);
+    return v >= sizeof(n) ? "?" : n[v];
+  }
+};
+
+class DotMap: public map<ComboAddress, DoTStatus>
+{
+public:
+  uint64_t d_numBusy{0};
+};
+LockGuarded<DotMap> 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<FILE, int(*)(FILE*)>(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<time_t>::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<Netmask> 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<Netmask>& 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);
index f1c13e55b3ce9cf537692c64756635ee9c2a4d90..98ca15025623e7f3aab72252400522ba850fe03e 100644 (file)
@@ -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<DNSRecord>&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)
   {