]> git.ipfire.org Git - thirdparty/pdns.git/blobdiff - pdns/dnsdist.hh
Merge pull request #14200 from rgacogne/auth-enable-leak-detection-unit-tests
[thirdparty/pdns.git] / pdns / dnsdist.hh
diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh
deleted file mode 100644 (file)
index 871d901..0000000
+++ /dev/null
@@ -1,1158 +0,0 @@
-/*
- * This file is part of PowerDNS or dnsdist.
- * Copyright -- PowerDNS.COM B.V. and its contributors
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * In addition, for the avoidance of any doubt, permission is granted to
- * link this program with OpenSSL and to (re)distribute the binaries
- * produced as the result of such linking.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-#pragma once
-
-#include "config.h"
-#include "ext/luawrapper/include/LuaContext.hpp"
-
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-#include <string>
-#include <thread>
-#include <time.h>
-#include <unistd.h>
-#include <unordered_map>
-
-#include <boost/variant.hpp>
-
-#include "circular_buffer.hh"
-#include "dnscrypt.hh"
-#include "dnsdist-cache.hh"
-#include "dnsdist-dynbpf.hh"
-#include "dnsdist-idstate.hh"
-#include "dnsdist-lbpolicies.hh"
-#include "dnsdist-protocols.hh"
-#include "dnsname.hh"
-#include "dnsdist-doh-common.hh"
-#include "doq.hh"
-#include "doh3.hh"
-#include "ednsoptions.hh"
-#include "iputils.hh"
-#include "misc.hh"
-#include "mplexer.hh"
-#include "noinitvector.hh"
-#include "sholder.hh"
-#include "tcpiohandler.hh"
-#include "uuid-utils.hh"
-#include "proxy-protocol.hh"
-#include "stat_t.hh"
-
-uint64_t uptimeOfProcess(const std::string& str);
-
-extern uint16_t g_ECSSourcePrefixV4;
-extern uint16_t g_ECSSourcePrefixV6;
-extern bool g_ECSOverride;
-
-using QTag = std::unordered_map<string, string>;
-
-class IncomingTCPConnectionState;
-
-struct ClientState;
-
-struct DNSQuestion
-{
-  DNSQuestion(InternalQueryState& ids_, PacketBuffer& data_):
-    data(data_), ids(ids_), ecsPrefixLength(ids.origRemote.sin4.sin_family == AF_INET ? g_ECSSourcePrefixV4 : g_ECSSourcePrefixV6), ecsOverride(g_ECSOverride) {
-  }
-  DNSQuestion(const DNSQuestion&) = delete;
-  DNSQuestion& operator=(const DNSQuestion&) = delete;
-  DNSQuestion(DNSQuestion&&) = default;
-  virtual ~DNSQuestion() = default;
-
-  std::string getTrailingData() const;
-  bool setTrailingData(const std::string&);
-  const PacketBuffer& getData() const
-  {
-    return data;
-  }
-  PacketBuffer& getMutableData()
-  {
-    return data;
-  }
-
-  bool editHeader(const std::function<bool(dnsheader&)>& editFunction);
-
-  const dnsheader_aligned getHeader() const
-  {
-    if (data.size() < sizeof(dnsheader)) {
-      throw std::runtime_error("Trying to access the dnsheader of a too small (" + std::to_string(data.size()) + ") DNSQuestion buffer");
-    }
-    dnsheader_aligned dh(data.data());
-    return dh;
-  }
-
-  /* this function is not safe against unaligned access, you should
-     use editHeader() instead, but we need it for the Lua bindings */
-  dnsheader* getMutableHeader() const
-  {
-    if (data.size() < sizeof(dnsheader)) {
-      throw std::runtime_error("Trying to access the dnsheader of a too small (" + std::to_string(data.size()) + ") DNSQuestion buffer");
-    }
-    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
-    return reinterpret_cast<dnsheader*>(data.data());
-  }
-
-  bool hasRoomFor(size_t more) const
-  {
-    return data.size() <= getMaximumSize() && (getMaximumSize() - data.size()) >= more;
-  }
-
-  size_t getMaximumSize() const
-  {
-    if (overTCP()) {
-      return std::numeric_limits<uint16_t>::max();
-    }
-    return 4096;
-  }
-
-  dnsdist::Protocol getProtocol() const
-  {
-    return ids.protocol;
-  }
-
-  bool overTCP() const
-  {
-    return !(ids.protocol == dnsdist::Protocol::DoUDP || ids.protocol == dnsdist::Protocol::DNSCryptUDP);
-  }
-
-  void setTag(std::string&& key, std::string&& value) {
-    if (!ids.qTag) {
-      ids.qTag = std::make_unique<QTag>();
-    }
-    ids.qTag->insert_or_assign(std::move(key), std::move(value));
-  }
-
-  void setTag(const std::string& key, const std::string& value) {
-    if (!ids.qTag) {
-      ids.qTag = std::make_unique<QTag>();
-    }
-    ids.qTag->insert_or_assign(key, value);
-  }
-
-  const struct timespec& getQueryRealTime() const
-  {
-    return ids.queryRealTime.d_start;
-  }
-
-  bool isAsynchronous() const
-  {
-    return asynchronous;
-  }
-
-  std::shared_ptr<IncomingTCPConnectionState> getIncomingTCPState() const
-  {
-    return d_incomingTCPState;
-  }
-
-  ClientState* getFrontend() const
-  {
-    return ids.cs;
-  }
-
-protected:
-  PacketBuffer& data;
-
-public:
-  InternalQueryState& ids;
-  std::unique_ptr<Netmask> ecs{nullptr};
-  std::string sni; /* Server Name Indication, if any (DoT or DoH) */
-  mutable std::unique_ptr<EDNSOptionViewMap> ednsOptions; /* this needs to be mutable because it is parsed just in time, when DNSQuestion is read-only */
-  std::shared_ptr<IncomingTCPConnectionState> d_incomingTCPState{nullptr};
-  std::unique_ptr<std::vector<ProxyProtocolValue>> proxyProtocolValues{nullptr};
-  uint16_t ecsPrefixLength;
-  uint8_t ednsRCode{0};
-  bool ecsOverride;
-  bool useECS{true};
-  bool addXPF{true};
-  bool asynchronous{false};
-};
-
-struct DownstreamState;
-
-struct DNSResponse : DNSQuestion
-{
-  DNSResponse(InternalQueryState& ids_, PacketBuffer& data_, const std::shared_ptr<DownstreamState>& downstream):
-    DNSQuestion(ids_, data_), d_downstream(downstream) { }
-  DNSResponse(const DNSResponse&) = delete;
-  DNSResponse& operator=(const DNSResponse&) = delete;
-  DNSResponse(DNSResponse&&) = default;
-
-  const std::shared_ptr<DownstreamState>& d_downstream;
-};
-
-/* so what could you do:
-   drop,
-   fake up nxdomain,
-   provide actual answer,
-   allow & and stop processing,
-   continue processing,
-   modify header:    (servfail|refused|notimp), set TC=1,
-   send to pool */
-
-class DNSAction
-{
-public:
-  enum class Action : uint8_t { Drop, Nxdomain, Refused, Spoof, Allow, HeaderModify, Pool, Delay, Truncate, ServFail, None, NoOp, NoRecurse, SpoofRaw, SpoofPacket };
-  static std::string typeToString(const Action& action)
-  {
-    switch(action) {
-    case Action::Drop:
-      return "Drop";
-    case Action::Nxdomain:
-      return "Send NXDomain";
-    case Action::Refused:
-      return "Send Refused";
-    case Action::Spoof:
-      return "Spoof an answer";
-    case Action::SpoofPacket:
-      return "Spoof a raw answer from bytes";
-    case Action::SpoofRaw:
-      return "Spoof an answer from raw bytes";
-    case Action::Allow:
-      return "Allow";
-    case Action::HeaderModify:
-      return "Modify the header";
-    case Action::Pool:
-      return "Route to a pool";
-    case Action::Delay:
-      return "Delay";
-    case Action::Truncate:
-      return "Truncate over UDP";
-    case Action::ServFail:
-      return "Send ServFail";
-    case Action::None:
-    case Action::NoOp:
-      return "Do nothing";
-    case Action::NoRecurse:
-      return "Set rd=0";
-    }
-
-    return "Unknown";
-  }
-
-  virtual Action operator()(DNSQuestion*, string* ruleresult) const =0;
-  virtual ~DNSAction()
-  {
-  }
-  virtual string toString() const = 0;
-  virtual std::map<string, double> getStats() const
-  {
-    return {{}};
-  }
-  virtual void reload()
-  {
-  }
-};
-
-class DNSResponseAction
-{
-public:
-  enum class Action : uint8_t { Allow, Delay, Drop, HeaderModify, ServFail, Truncate, None };
-  virtual Action operator()(DNSResponse*, string* ruleresult) const =0;
-  virtual ~DNSResponseAction()
-  {
-  }
-  virtual string toString() const = 0;
-  virtual void reload()
-  {
-  }
-};
-
-struct DynBlock
-{
-  DynBlock(): action(DNSAction::Action::None), warning(false)
-  {
-    until.tv_sec = 0;
-    until.tv_nsec = 0;
-  }
-
-  DynBlock(const std::string& reason_, const struct timespec& until_, const DNSName& domain_, DNSAction::Action action_): reason(reason_), domain(domain_), until(until_), action(action_), warning(false)
-  {
-  }
-
-  DynBlock(const DynBlock& rhs): reason(rhs.reason), domain(rhs.domain), until(rhs.until), action(rhs.action), warning(rhs.warning), bpf(rhs.bpf)
-  {
-    blocks.store(rhs.blocks);
-  }
-
-  DynBlock(DynBlock&& rhs): reason(std::move(rhs.reason)), domain(std::move(rhs.domain)), until(rhs.until), action(rhs.action), warning(rhs.warning), bpf(rhs.bpf)
-  {
-    blocks.store(rhs.blocks);
-  }
-
-  DynBlock& operator=(const DynBlock& rhs)
-  {
-    reason = rhs.reason;
-    until = rhs.until;
-    domain = rhs.domain;
-    action = rhs.action;
-    blocks.store(rhs.blocks);
-    warning = rhs.warning;
-    bpf = rhs.bpf;
-    return *this;
-  }
-
-  DynBlock& operator=(DynBlock&& rhs)
-  {
-    reason = std::move(rhs.reason);
-    until = rhs.until;
-    domain = std::move(rhs.domain);
-    action = rhs.action;
-    blocks.store(rhs.blocks);
-    warning = rhs.warning;
-    bpf = rhs.bpf;
-    return *this;
-  }
-
-  string reason;
-  DNSName domain;
-  struct timespec until;
-  mutable std::atomic<unsigned int> blocks;
-  DNSAction::Action action{DNSAction::Action::None};
-  bool warning{false};
-  bool bpf{false};
-};
-
-extern GlobalStateHolder<NetmaskTree<DynBlock, AddressAndPortRange>> g_dynblockNMG;
-
-extern vector<pair<struct timeval, std::string> > g_confDelta;
-
-using pdns::stat_t;
-
-class BasicQPSLimiter
-{
-public:
-  BasicQPSLimiter()
-  {
-  }
-
-  BasicQPSLimiter(unsigned int burst): d_tokens(burst)
-  {
-    d_prev.start();
-  }
-
-  virtual ~BasicQPSLimiter()
-  {
-  }
-
-  bool check(unsigned int rate, unsigned int burst) const // this is not quite fair
-  {
-    if (checkOnly(rate, burst)) {
-      addHit();
-      return true;
-    }
-
-    return false;
-  }
-
-  bool checkOnly(unsigned int rate, unsigned int burst) const // this is not quite fair
-  {
-    auto delta = d_prev.udiffAndSet();
-
-    if (delta > 0.0) { // time, frequently, does go backwards..
-      d_tokens += 1.0 * rate * (delta/1000000.0);
-    }
-
-    if (d_tokens > burst) {
-      d_tokens = burst;
-    }
-
-    bool ret = false;
-    if (d_tokens >= 1.0) { // we need this because burst=1 is weird otherwise
-      ret = true;
-    }
-
-    return ret;
-  }
-
-  virtual void addHit() const
-  {
-    --d_tokens;
-  }
-
-  bool seenSince(const struct timespec& cutOff) const
-  {
-    return cutOff < d_prev.d_start;
-  }
-
-protected:
-  mutable StopWatch d_prev;
-  mutable double d_tokens{0.0};
-};
-
-class QPSLimiter : public BasicQPSLimiter
-{
-public:
-  QPSLimiter(): BasicQPSLimiter()
-  {
-  }
-
-  QPSLimiter(unsigned int rate, unsigned int burst): BasicQPSLimiter(burst), d_rate(rate), d_burst(burst), d_passthrough(false)
-  {
-    d_prev.start();
-  }
-
-  unsigned int getRate() const
-  {
-    return d_passthrough ? 0 : d_rate;
-  }
-
-  bool check() const // this is not quite fair
-  {
-    if (d_passthrough) {
-      return true;
-    }
-
-    return BasicQPSLimiter::check(d_rate, d_burst);
-  }
-
-  bool checkOnly() const
-  {
-    if (d_passthrough) {
-      return true;
-    }
-
-    return BasicQPSLimiter::checkOnly(d_rate, d_burst);
-  }
-
-  void addHit() const override
-  {
-    if (!d_passthrough) {
-      --d_tokens;
-    }
-  }
-
-private:
-  unsigned int d_rate{0};
-  unsigned int d_burst{0};
-  bool d_passthrough{true};
-};
-
-typedef std::unordered_map<string, unsigned int> QueryCountRecords;
-typedef std::function<std::tuple<bool, string>(const DNSQuestion* dq)> QueryCountFilter;
-struct QueryCount {
-  QueryCount()
-  {
-  }
-  ~QueryCount()
-  {
-  }
-  SharedLockGuarded<QueryCountRecords> records;
-  QueryCountFilter filter;
-  bool enabled{false};
-};
-
-extern QueryCount g_qcount;
-
-struct ClientState
-{
-  ClientState(const ComboAddress& local_, bool isTCP_, bool doReusePort, int fastOpenQueue, const std::string& itfName, const std::set<int>& cpus_, bool enableProxyProtocol): cpus(cpus_), interface(itfName), local(local_), fastOpenQueueSize(fastOpenQueue), tcp(isTCP_), reuseport(doReusePort), d_enableProxyProtocol(enableProxyProtocol)
-  {
-  }
-
-  stat_t queries{0};
-  stat_t nonCompliantQueries{0};
-  mutable stat_t responses{0};
-  mutable stat_t tcpDiedReadingQuery{0};
-  mutable stat_t tcpDiedSendingResponse{0};
-  mutable stat_t tcpGaveUp{0};
-  mutable stat_t tcpClientTimeouts{0};
-  mutable stat_t tcpDownstreamTimeouts{0};
-  /* current number of connections to this frontend */
-  mutable stat_t tcpCurrentConnections{0};
-  /* maximum number of concurrent connections to this frontend reached */
-  mutable stat_t tcpMaxConcurrentConnections{0};
-  stat_t tlsNewSessions{0}; // A new TLS session has been negotiated, no resumption
-  stat_t tlsResumptions{0}; // A TLS session has been resumed, either via session id or via a TLS ticket
-  stat_t tlsUnknownTicketKey{0}; // A TLS ticket has been presented but we don't have the associated key (might have expired)
-  stat_t tlsInactiveTicketKey{0}; // A TLS ticket has been successfully resumed but the key is no longer active, we should issue a new one
-  stat_t tls10queries{0};   // valid DNS queries received via TLSv1.0
-  stat_t tls11queries{0};   // valid DNS queries received via TLSv1.1
-  stat_t tls12queries{0};   // valid DNS queries received via TLSv1.2
-  stat_t tls13queries{0};   // valid DNS queries received via TLSv1.3
-  stat_t tlsUnknownqueries{0};   // valid DNS queries received via unknown TLS version
-  pdns::stat_t_trait<double> tcpAvgQueriesPerConnection{0.0};
-  /* in ms */
-  pdns::stat_t_trait<double> tcpAvgConnectionDuration{0.0};
-  std::set<int> cpus;
-  std::string interface;
-  ComboAddress local;
-  std::vector<std::pair<ComboAddress, int>> d_additionalAddresses;
-  std::shared_ptr<DNSCryptContext> dnscryptCtx{nullptr};
-  std::shared_ptr<TLSFrontend> tlsFrontend{nullptr};
-  std::shared_ptr<DOHFrontend> dohFrontend{nullptr};
-  std::shared_ptr<DOQFrontend> doqFrontend{nullptr};
-  std::shared_ptr<DOH3Frontend> doh3Frontend{nullptr};
-  std::shared_ptr<BPFFilter> d_filter{nullptr};
-  size_t d_maxInFlightQueriesPerConn{1};
-  size_t d_tcpConcurrentConnectionsLimit{0};
-  int udpFD{-1};
-  int tcpFD{-1};
-  int tcpListenQueueSize{SOMAXCONN};
-  int fastOpenQueueSize{0};
-  bool muted{false};
-  bool tcp;
-  bool reuseport;
-  bool d_enableProxyProtocol{true}; // the global proxy protocol ACL still applies
-  bool ready{false};
-
-  int getSocket() const
-  {
-    return udpFD != -1 ? udpFD : tcpFD;
-  }
-
-  bool isUDP() const
-  {
-    return udpFD != -1;
-  }
-
-  bool isTCP() const
-  {
-    return udpFD == -1;
-  }
-
-  bool isDoH() const
-  {
-    return dohFrontend != nullptr;
-  }
-
-  bool hasTLS() const
-  {
-    return tlsFrontend != nullptr || (dohFrontend != nullptr && dohFrontend->isHTTPS());
-  }
-
-  const TLSFrontend& getTLSFrontend() const
-  {
-    if (tlsFrontend != nullptr) {
-      return *tlsFrontend;
-    }
-    if (dohFrontend) {
-      return dohFrontend->d_tlsContext;
-    }
-    throw std::runtime_error("Trying to get a TLS frontend from a non-TLS ClientState");
-  }
-
-  dnsdist::Protocol getProtocol() const
-  {
-    if (dnscryptCtx) {
-      if (udpFD != -1) {
-        return dnsdist::Protocol::DNSCryptUDP;
-      }
-      return dnsdist::Protocol::DNSCryptTCP;
-    }
-    if (isDoH()) {
-      return dnsdist::Protocol::DoH;
-    }
-    else if (hasTLS()) {
-      return dnsdist::Protocol::DoT;
-    }
-    else if (udpFD != -1) {
-      return dnsdist::Protocol::DoUDP;
-    }
-    else {
-      return dnsdist::Protocol::DoTCP;
-    }
-  }
-
-  std::string getType() const
-  {
-    std::string result = udpFD != -1 ? "UDP" : "TCP";
-
-    if (doqFrontend) {
-      result += " (DNS over QUIC)";
-    }
-    else if (doh3Frontend) {
-      result += " (DNS over HTTP/3)";
-    }
-    else if (dohFrontend) {
-      if (dohFrontend->isHTTPS()) {
-        result += " (DNS over HTTPS)";
-      }
-      else {
-        result += " (DNS over HTTP)";
-      }
-    }
-    else if (tlsFrontend) {
-      result += " (DNS over TLS)";
-    }
-    else if (dnscryptCtx) {
-      result += " (DNSCrypt)";
-    }
-
-    return result;
-  }
-
-  void detachFilter(int socket)
-  {
-    if (d_filter) {
-      d_filter->removeSocket(socket);
-      d_filter = nullptr;
-    }
-  }
-
-  void attachFilter(shared_ptr<BPFFilter>& bpf, int socket)
-  {
-    detachFilter(socket);
-
-    bpf->addSocket(socket);
-    d_filter = bpf;
-  }
-
-  void detachFilter()
-  {
-    if (d_filter) {
-      detachFilter(getSocket());
-      for (const auto& [addr, socket] : d_additionalAddresses) {
-        (void) addr;
-        if (socket != -1) {
-          detachFilter(socket);
-        }
-      }
-
-      d_filter = nullptr;
-    }
-  }
-
-  void attachFilter(shared_ptr<BPFFilter>& bpf)
-  {
-    detachFilter();
-
-    bpf->addSocket(getSocket());
-    for (const auto& [addr, socket] : d_additionalAddresses) {
-      (void) addr;
-      if (socket != -1) {
-        bpf->addSocket(socket);
-      }
-    }
-    d_filter = bpf;
-  }
-
-  void updateTCPMetrics(size_t nbQueries, uint64_t durationMs)
-  {
-    tcpAvgQueriesPerConnection = (99.0 * tcpAvgQueriesPerConnection / 100.0) + (nbQueries / 100.0);
-    tcpAvgConnectionDuration = (99.0 * tcpAvgConnectionDuration / 100.0) + (durationMs / 100.0);
-  }
-};
-
-struct CrossProtocolQuery;
-
-struct DownstreamState: public std::enable_shared_from_this<DownstreamState>
-{
-  DownstreamState(const DownstreamState&) = delete;
-  DownstreamState(DownstreamState&&) = delete;
-  DownstreamState& operator=(const DownstreamState&) = delete;
-  DownstreamState& operator=(DownstreamState&&) = delete;
-
-  typedef std::function<std::tuple<DNSName, uint16_t, uint16_t>(const DNSName&, uint16_t, uint16_t, dnsheader*)> checkfunc_t;
-  enum class Availability : uint8_t { Up, Down, Auto, Lazy };
-  enum class LazyHealthCheckMode : uint8_t { TimeoutOnly, TimeoutOrServFail };
-
-  struct Config
-  {
-    Config()
-    {
-    }
-    Config(const ComboAddress& remote_): remote(remote_)
-    {
-    }
-
-    TLSContextParameters d_tlsParams;
-    set<string> pools;
-    std::set<int> d_cpus;
-    checkfunc_t checkFunction;
-    std::optional<boost::uuids::uuid> id;
-    DNSName checkName{"a.root-servers.net."};
-    ComboAddress remote;
-    ComboAddress sourceAddr;
-    std::string sourceItfName;
-    std::string d_tlsSubjectName;
-    std::string d_dohPath;
-    std::string name;
-    std::string nameWithAddr;
-    size_t d_numberOfSockets{1};
-    size_t d_maxInFlightQueriesPerConn{1};
-    size_t d_tcpConcurrentConnectionsLimit{0};
-    int order{1};
-    int d_weight{1};
-    int tcpConnectTimeout{5};
-    int tcpRecvTimeout{30};
-    int tcpSendTimeout{30};
-    int d_qpsLimit{0};
-    unsigned int checkInterval{1};
-    unsigned int sourceItf{0};
-    QType checkType{QType::A};
-    uint16_t checkClass{QClass::IN};
-    uint16_t d_retries{5};
-    uint16_t xpfRRCode{0};
-    uint16_t checkTimeout{1000}; /* in milliseconds */
-    uint16_t d_lazyHealthCheckSampleSize{100};
-    uint16_t d_lazyHealthCheckMinSampleCount{1};
-    uint16_t d_lazyHealthCheckFailedInterval{30};
-    uint16_t d_lazyHealthCheckMaxBackOff{3600};
-    uint8_t d_lazyHealthCheckThreshold{20};
-    LazyHealthCheckMode d_lazyHealthCheckMode{LazyHealthCheckMode::TimeoutOrServFail};
-    uint8_t maxCheckFailures{1};
-    uint8_t minRiseSuccesses{1};
-    Availability availability{Availability::Auto};
-    bool d_tlsSubjectIsAddr{false};
-    bool mustResolve{false};
-    bool useECS{false};
-    bool useProxyProtocol{false};
-    bool d_proxyProtocolAdvertiseTLS{false};
-    bool setCD{false};
-    bool disableZeroScope{false};
-    bool tcpFastOpen{false};
-    bool ipBindAddrNoPort{true};
-    bool reconnectOnUp{false};
-    bool d_tcpCheck{false};
-    bool d_tcpOnly{false};
-    bool d_addXForwardedHeaders{false}; // for DoH backends
-    bool d_lazyHealthCheckUseExponentialBackOff{false};
-    bool d_upgradeToLazyHealthChecks{false};
-  };
-
-  struct HealthCheckMetrics
-  {
-    stat_t d_failures{0};
-    stat_t d_timeOuts{0};
-    stat_t d_parseErrors{0};
-    stat_t d_networkErrors{0};
-    stat_t d_mismatchErrors{0};
-    stat_t d_invalidResponseErrors{0};
-  };
-
-  DownstreamState(DownstreamState::Config&& config, std::shared_ptr<TLSCtx> tlsCtx, bool connect);
-  DownstreamState(const ComboAddress& remote): DownstreamState(DownstreamState::Config(remote), nullptr, false)
-  {
-  }
-
-  ~DownstreamState();
-
-  Config d_config;
-  HealthCheckMetrics d_healthCheckMetrics;
-  stat_t sendErrors{0};
-  stat_t outstanding{0};
-  stat_t reuseds{0};
-  stat_t queries{0};
-  stat_t responses{0};
-  stat_t nonCompliantResponses{0};
-  struct {
-    stat_t sendErrors{0};
-    stat_t reuseds{0};
-    stat_t queries{0};
-  } prev;
-  stat_t tcpDiedSendingQuery{0};
-  stat_t tcpDiedReadingResponse{0};
-  stat_t tcpGaveUp{0};
-  stat_t tcpReadTimeouts{0};
-  stat_t tcpWriteTimeouts{0};
-  stat_t tcpConnectTimeouts{0};
-  /* current number of connections to this backend */
-  stat_t tcpCurrentConnections{0};
-  /* maximum number of concurrent connections to this backend reached */
-  stat_t tcpMaxConcurrentConnections{0};
-  /* number of times we had to enforce the maximum concurrent connections limit */
-  stat_t tcpTooManyConcurrentConnections{0};
-  stat_t tcpReusedConnections{0};
-  stat_t tcpNewConnections{0};
-  stat_t tlsResumptions{0};
-  pdns::stat_t_trait<double> tcpAvgQueriesPerConnection{0.0};
-  /* in ms */
-  pdns::stat_t_trait<double> tcpAvgConnectionDuration{0.0};
-  pdns::stat_t_trait<double> queryLoad{0.0};
-  pdns::stat_t_trait<double> dropRate{0.0};
-
-  SharedLockGuarded<std::vector<unsigned int>> hashes;
-  LockGuarded<std::unique_ptr<FDMultiplexer>> mplexer{nullptr};
-private:
-  LockGuarded<std::map<uint16_t, IDState>> d_idStatesMap;
-  vector<IDState> idStates;
-
-  struct LazyHealthCheckStats
-  {
-    boost::circular_buffer<bool> d_lastResults;
-    time_t d_nextCheck{0};
-    enum class LazyStatus: uint8_t { Healthy = 0, PotentialFailure, Failed };
-    LazyStatus d_status{LazyStatus::Healthy};
-  };
-  LockGuarded<LazyHealthCheckStats> d_lazyHealthCheckStats;
-
-public:
-  std::shared_ptr<TLSCtx> d_tlsCtx{nullptr};
-  std::vector<int> sockets;
-  StopWatch sw;
-  QPSLimiter qps;
-  std::atomic<uint64_t> idOffset{0};
-  size_t socketsOffset{0};
-  double latencyUsec{0.0};
-  double latencyUsecTCP{0.0};
-  unsigned int d_nextCheck{0};
-  uint16_t currentCheckFailures{0};
-  std::atomic<bool> hashesComputed{false};
-  std::atomic<bool> connected{false};
-  bool upStatus{false};
-
-private:
-  void handleUDPTimeout(IDState& ids);
-  void updateNextLazyHealthCheck(LazyHealthCheckStats& stats, bool checkScheduled, std::optional<time_t> currentTime = std::nullopt);
-  void connectUDPSockets();
-
-  std::thread tid;
-  std::mutex connectLock;
-  std::condition_variable d_connectedWait;
-  std::atomic_flag threadStarted;
-  uint8_t consecutiveSuccessfulChecks{0};
-  bool d_stopped{false};
-public:
-
-  void start();
-
-  bool isUp() const
-  {
-    if (d_config.availability == Availability::Down) {
-      return false;
-    }
-    else if (d_config.availability == Availability::Up) {
-      return true;
-    }
-    return upStatus;
-  }
-
-  void setUp() {
-    d_config.availability = Availability::Up;
-  }
-
-  void setUpStatus(bool newStatus)
-  {
-    upStatus = newStatus;
-    if (!upStatus) {
-      latencyUsec = 0.0;
-      latencyUsecTCP = 0.0;
-    }
-  }
-  void setDown()
-  {
-    d_config.availability = Availability::Down;
-    latencyUsec = 0.0;
-    latencyUsecTCP = 0.0;
-  }
-  void setAuto() {
-    d_config.availability = Availability::Auto;
-  }
-  void setLazyAuto() {
-    d_config.availability = Availability::Lazy;
-    d_lazyHealthCheckStats.lock()->d_lastResults.set_capacity(d_config.d_lazyHealthCheckSampleSize);
-  }
-  bool healthCheckRequired(std::optional<time_t> currentTime = std::nullopt);
-
-  const string& getName() const {
-    return d_config.name;
-  }
-  const string& getNameWithAddr() const {
-    return d_config.nameWithAddr;
-  }
-  void setName(const std::string& newName)
-  {
-    d_config.name = newName;
-    d_config.nameWithAddr = newName.empty() ? d_config.remote.toStringWithPort() : (d_config.name + " (" + d_config.remote.toStringWithPort()+ ")");
-  }
-
-  string getStatus() const
-  {
-    string status;
-    if (d_config.availability == DownstreamState::Availability::Up) {
-      status = "UP";
-    }
-    else if (d_config.availability == DownstreamState::Availability::Down) {
-      status = "DOWN";
-    }
-    else {
-      status = (upStatus ? "up" : "down");
-    }
-    return status;
-  }
-
-  bool reconnect(bool initialAttempt = false);
-  void waitUntilConnected();
-  void hash();
-  void setId(const boost::uuids::uuid& newId);
-  void setWeight(int newWeight);
-  void stop();
-  bool isStopped() const
-  {
-    return d_stopped;
-  }
-  const boost::uuids::uuid& getID() const
-  {
-    return *d_config.id;
-  }
-
-  void updateTCPMetrics(size_t nbQueries, uint64_t durationMs)
-  {
-    tcpAvgQueriesPerConnection = (99.0 * tcpAvgQueriesPerConnection / 100.0) + (nbQueries / 100.0);
-    tcpAvgConnectionDuration = (99.0 * tcpAvgConnectionDuration / 100.0) + (durationMs / 100.0);
-  }
-
-  void updateTCPLatency(double udiff)
-  {
-    latencyUsecTCP = (127.0 * latencyUsecTCP / 128.0) + udiff / 128.0;
-  }
-
-  void incQueriesCount()
-  {
-    ++queries;
-    qps.addHit();
-  }
-
-  void incCurrentConnectionsCount();
-
-  bool doHealthcheckOverTCP() const
-  {
-    return d_config.d_tcpOnly || d_config.d_tcpCheck || d_tlsCtx != nullptr;
-  }
-
-  bool isTCPOnly() const
-  {
-    return d_config.d_tcpOnly || d_tlsCtx != nullptr;
-  }
-
-  bool isDoH() const
-  {
-    return !d_config.d_dohPath.empty();
-  }
-
-  bool passCrossProtocolQuery(std::unique_ptr<CrossProtocolQuery>&& cpq);
-  int pickSocketForSending();
-  void pickSocketsReadyForReceiving(std::vector<int>& ready);
-  void handleUDPTimeouts();
-  void reportTimeoutOrError();
-  void reportResponse(uint8_t rcode);
-  void submitHealthCheckResult(bool initial, bool newState);
-  time_t getNextLazyHealthCheck();
-  uint16_t saveState(InternalQueryState&&);
-  void restoreState(uint16_t id, InternalQueryState&&);
-  std::optional<InternalQueryState> getState(uint16_t id);
-
-  dnsdist::Protocol getProtocol() const
-  {
-    if (isDoH()) {
-      return dnsdist::Protocol::DoH;
-    }
-    if (d_tlsCtx != nullptr) {
-      return dnsdist::Protocol::DoT;
-    }
-    if (isTCPOnly()) {
-      return dnsdist::Protocol::DoTCP;
-    }
-    return dnsdist::Protocol::DoUDP;
-  }
-
-  double getRelevantLatencyUsec() const
-  {
-    if (isTCPOnly()) {
-      return latencyUsecTCP;
-    }
-    return latencyUsec;
-  }
-
-  static int s_udpTimeout;
-  static bool s_randomizeSockets;
-  static bool s_randomizeIDs;
-};
-using servers_t = vector<std::shared_ptr<DownstreamState>>;
-
-void responderThread(std::shared_ptr<DownstreamState> state);
-extern LockGuarded<LuaContext> g_lua;
-extern std::string g_outputBuffer; // locking for this is ok, as locked by g_luamutex
-
-class DNSRule
-{
-public:
-  virtual ~DNSRule ()
-  {
-  }
-  virtual bool matches(const DNSQuestion* dq) const = 0;
-  virtual string toString() const = 0;
-  mutable stat_t d_matches{0};
-};
-
-struct ServerPool
-{
-  ServerPool(): d_servers(std::make_shared<const ServerPolicy::NumberedServerVector>())
-  {
-  }
-
-  ~ServerPool()
-  {
-  }
-
-  const std::shared_ptr<DNSDistPacketCache> getCache() const { return packetCache; };
-
-  bool getECS() const
-  {
-    return d_useECS;
-  }
-
-  void setECS(bool useECS)
-  {
-    d_useECS = useECS;
-  }
-
-  std::shared_ptr<DNSDistPacketCache> packetCache{nullptr};
-  std::shared_ptr<ServerPolicy> policy{nullptr};
-
-  size_t poolLoad();
-  size_t countServers(bool upOnly);
-  const std::shared_ptr<const ServerPolicy::NumberedServerVector> getServers();
-  void addServer(shared_ptr<DownstreamState>& server);
-  void removeServer(shared_ptr<DownstreamState>& server);
-
-private:
-  SharedLockGuarded<std::shared_ptr<const ServerPolicy::NumberedServerVector>> d_servers;
-  bool d_useECS{false};
-};
-
-enum ednsHeaderFlags {
-  EDNS_HEADER_FLAG_NONE = 0,
-  EDNS_HEADER_FLAG_DO = 32768
-};
-
-struct DNSDistRuleAction
-{
-  std::shared_ptr<DNSRule> d_rule;
-  std::shared_ptr<DNSAction> d_action;
-  std::string d_name;
-  boost::uuids::uuid d_id;
-  uint64_t d_creationOrder;
-};
-
-struct DNSDistResponseRuleAction
-{
-  std::shared_ptr<DNSRule> d_rule;
-  std::shared_ptr<DNSResponseAction> d_action;
-  std::string d_name;
-  boost::uuids::uuid d_id;
-  uint64_t d_creationOrder;
-};
-
-extern GlobalStateHolder<SuffixMatchTree<DynBlock>> g_dynblockSMT;
-extern DNSAction::Action g_dynBlockAction;
-
-extern GlobalStateHolder<ServerPolicy> g_policy;
-extern GlobalStateHolder<servers_t> g_dstates;
-extern GlobalStateHolder<pools_t> g_pools;
-extern GlobalStateHolder<vector<DNSDistRuleAction> > g_ruleactions;
-extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_respruleactions;
-extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cachehitrespruleactions;
-extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_selfansweredrespruleactions;
-extern GlobalStateHolder<vector<DNSDistResponseRuleAction> > g_cacheInsertedRespRuleActions;
-extern GlobalStateHolder<NetmaskGroup> g_ACL;
-
-extern ComboAddress g_serverControl; // not changed during runtime
-
-extern std::vector<shared_ptr<TLSFrontend>> g_tlslocals;
-extern std::vector<shared_ptr<DOHFrontend>> g_dohlocals;
-extern std::vector<shared_ptr<DOQFrontend>> g_doqlocals;
-extern std::vector<shared_ptr<DOH3Frontend>> g_doh3locals;
-extern std::vector<std::unique_ptr<ClientState>> g_frontends;
-extern bool g_truncateTC;
-extern bool g_fixupCase;
-extern int g_tcpRecvTimeout;
-extern int g_tcpSendTimeout;
-extern uint16_t g_maxOutstanding;
-extern std::atomic<bool> g_configurationDone;
-extern boost::optional<uint64_t> g_maxTCPClientThreads;
-extern uint64_t g_maxTCPQueuedConnections;
-extern size_t g_maxTCPQueriesPerConn;
-extern size_t g_maxTCPConnectionDuration;
-extern size_t g_tcpInternalPipeBufferSize;
-extern pdns::stat16_t g_cacheCleaningDelay;
-extern pdns::stat16_t g_cacheCleaningPercentage;
-extern uint32_t g_staleCacheEntriesTTL;
-extern bool g_apiReadWrite;
-extern std::string g_apiConfigDirectory;
-extern bool g_servFailOnNoPolicy;
-extern size_t g_udpVectorSize;
-extern bool g_allowEmptyResponse;
-extern uint32_t g_socketUDPSendBuffer;
-extern uint32_t g_socketUDPRecvBuffer;
-
-extern shared_ptr<BPFFilter> g_defaultBPFFilter;
-extern std::vector<std::shared_ptr<DynBPFFilter> > g_dynBPFFilters;
-
-struct LocalHolders
-{
-  LocalHolders(): acl(g_ACL.getLocal()), policy(g_policy.getLocal()), ruleactions(g_ruleactions.getLocal()), cacheHitRespRuleactions(g_cachehitrespruleactions.getLocal()), cacheInsertedRespRuleActions(g_cacheInsertedRespRuleActions.getLocal()), selfAnsweredRespRuleactions(g_selfansweredrespruleactions.getLocal()), servers(g_dstates.getLocal()), dynNMGBlock(g_dynblockNMG.getLocal()), dynSMTBlock(g_dynblockSMT.getLocal()), pools(g_pools.getLocal())
-  {
-  }
-
-  LocalStateHolder<NetmaskGroup> acl;
-  LocalStateHolder<ServerPolicy> policy;
-  LocalStateHolder<vector<DNSDistRuleAction> > ruleactions;
-  LocalStateHolder<vector<DNSDistResponseRuleAction> > cacheHitRespRuleactions;
-  LocalStateHolder<vector<DNSDistResponseRuleAction> > cacheInsertedRespRuleActions;
-  LocalStateHolder<vector<DNSDistResponseRuleAction> > selfAnsweredRespRuleactions;
-  LocalStateHolder<servers_t> servers;
-  LocalStateHolder<NetmaskTree<DynBlock, AddressAndPortRange> > dynNMGBlock;
-  LocalStateHolder<SuffixMatchTree<DynBlock> > dynSMTBlock;
-  LocalStateHolder<pools_t> pools;
-};
-
-void tcpAcceptorThread(const std::vector<ClientState*>& states);
-
-void setLuaNoSideEffect(); // if nothing has been declared, set that there are no side effects
-void setLuaSideEffect();   // set to report a side effect, cancelling all _no_ side effect calls
-bool getLuaNoSideEffect(); // set if there were only explicit declarations of _no_ side effect
-void resetLuaSideEffect(); // reset to indeterminate state
-
-bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr<DownstreamState>& remote, unsigned int& qnameWireLength);
-
-bool checkQueryHeaders(const struct dnsheader* dh, ClientState& cs);
-
-extern std::vector<std::shared_ptr<DNSCryptContext>> g_dnsCryptLocals;
-int handleDNSCryptQuery(PacketBuffer& packet, DNSCryptQuery& query, bool tcp, time_t now, PacketBuffer& response);
-bool checkDNSCryptQuery(const ClientState& cs, PacketBuffer& query, std::unique_ptr<DNSCryptQuery>& dnsCryptQuery, time_t now, bool tcp);
-
-#include "dnsdist-snmp.hh"
-
-extern bool g_snmpEnabled;
-extern bool g_snmpTrapsEnabled;
-extern DNSDistSNMPAgent* g_snmpAgent;
-extern bool g_addEDNSToSelfGeneratedResponses;
-
-extern std::set<std::string> g_capabilitiesToRetain;
-static const uint16_t s_udpIncomingBufferSize{1500}; // don't accept UDP queries larger than this value
-
-enum class ProcessQueryResult : uint8_t { Drop, SendAnswer, PassToBackend, Asynchronous };
-ProcessQueryResult processQuery(DNSQuestion& dq, LocalHolders& holders, std::shared_ptr<DownstreamState>& selectedBackend);
-ProcessQueryResult processQueryAfterRules(DNSQuestion& dq, LocalHolders& holders, std::shared_ptr<DownstreamState>& selectedBackend);
-bool processResponse(PacketBuffer& response, const std::vector<DNSDistResponseRuleAction>& respRuleActions, const std::vector<DNSDistResponseRuleAction>& insertedRespRuleActions, DNSResponse& dr, bool muted);
-bool processRulesResult(const DNSAction::Action& action, DNSQuestion& dq, std::string& ruleresult, bool& drop);
-bool processResponseAfterRules(PacketBuffer& response, const std::vector<DNSDistResponseRuleAction>& cacheInsertedRespRuleActions, DNSResponse& dr, bool muted);
-
-bool assignOutgoingUDPQueryToBackend(std::shared_ptr<DownstreamState>& ds, uint16_t queryID, DNSQuestion& dq, PacketBuffer& query);
-
-ssize_t udpClientSendRequestToBackend(const std::shared_ptr<DownstreamState>& ss, const int sd, const PacketBuffer& request, bool healthCheck = false);
-bool sendUDPResponse(int origFD, const PacketBuffer& response, const int delayMsec, const ComboAddress& origDest, const ComboAddress& origRemote);
-void handleResponseSent(const DNSName& qname, const QType& qtype, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol outgoingProtocol, dnsdist::Protocol incomingProtocol, bool fromBackend);
-void handleResponseSent(const InternalQueryState& ids, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol outgoingProtocol, bool fromBackend);