static size_t const pwhash_prefix_size = pwhash_prefix.size();
#endif
-uint64_t const CredentialsHolder::s_defaultWorkFactor{1024U}; /* N */
-uint64_t const CredentialsHolder::s_defaultParallelFactor{1U}; /* p */
-uint64_t const CredentialsHolder::s_defaultBlockSize{8U}; /* r */
-
SensitiveData::SensitiveData(std::string&& data) :
d_data(std::move(data))
{
static bool isHashingAvailable();
static SensitiveData readFromTerminal();
- static uint64_t const s_defaultWorkFactor;
- static uint64_t const s_defaultParallelFactor;
- static uint64_t const s_defaultBlockSize;
+ static uint64_t constexpr s_defaultWorkFactor{1024U}; /* N */;
+ static uint64_t constexpr s_defaultParallelFactor{1U}; /* p */;
+ static uint64_t constexpr s_defaultBlockSize{8U}; /* r */;
private:
SensitiveData d_credentials;
dnsdist-cache.cc dnsdist-cache.hh \
dnsdist-carbon.cc dnsdist-carbon.hh \
dnsdist-concurrent-connections.hh \
+ dnsdist-configuration.cc dnsdist-configuration.hh \
dnsdist-console.cc dnsdist-console.hh \
dnsdist-crypto.cc dnsdist-crypto.hh \
dnsdist-discovery.cc dnsdist-discovery.hh \
dnsdist-protobuf.cc dnsdist-protobuf.hh \
dnsdist-protocols.cc dnsdist-protocols.hh \
dnsdist-proxy-protocol.cc dnsdist-proxy-protocol.hh \
+ dnsdist-query-count.hh dnsdist-query-count.cc \
dnsdist-random.cc dnsdist-random.hh \
dnsdist-resolver.cc dnsdist-resolver.hh \
dnsdist-rings.cc dnsdist-rings.hh \
dnsdist-backoff.hh \
dnsdist-cache.cc dnsdist-cache.hh \
dnsdist-concurrent-connections.hh \
+ dnsdist-configuration.cc dnsdist-configuration.hh \
dnsdist-crypto.cc dnsdist-crypto.hh \
dnsdist-dnsparser.cc dnsdist-dnsparser.hh \
dnsdist-doh-common.cc dnsdist-doh-common.hh \
channel.hh channel.cc \
dns.cc dns.hh \
dnsdist-cache.cc dnsdist-cache.hh \
+ dnsdist-configuration.cc dnsdist-configuration.hh \
dnsdist-dnsparser.cc dnsdist-dnsparser.hh \
dnsdist-ecs.cc dnsdist-ecs.hh \
dnsdist-idstate.hh \
void getCertificateResponse(time_t now, PacketBuffer& response) const;
int encryptResponse(PacketBuffer& response, size_t maxResponseSize, bool tcp);
- static const size_t s_minUDPLength = 256;
+ static constexpr size_t s_minUDPLength = 256;
private:
static void fillServerNonce(DNSCryptNonceType& nonce);
#include "dnsdist-nghttp2.hh"
#include "dnsdist-random.hh"
#include "dnsdist-rings.hh"
+#include "dnsdist-snmp.hh"
#include "dnsdist-tcp.hh"
#include "dnsdist-xsk.hh"
#include "dolog.hh"
connected = true;
}
catch (const std::runtime_error& error) {
- if (initialAttempt || g_verbose) {
+ if (initialAttempt || dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose) {
infolog("Error connecting to new server with address %s: %s", d_config.remote.toStringWithPort(), error.what());
}
connected = false;
void DownstreamState::hash()
{
vinfolog("Computing hashes for id=%s and weight=%d", *d_config.id, d_config.d_weight);
+ const auto hashPerturbation = dnsdist::configuration::getImmutableConfiguration().d_hashPerturbation;
auto w = d_config.d_weight;
auto idStr = boost::str(boost::format("%s") % *d_config.id);
auto lockedHashes = hashes.write_lock();
lockedHashes->reserve(w);
while (w > 0) {
std::string uuid = boost::str(boost::format("%s-%d") % idStr % w);
- unsigned int wshash = burtleCI(reinterpret_cast<const unsigned char*>(uuid.c_str()), uuid.size(), g_hashperturb);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): sorry, it's the burtle API
+ unsigned int wshash = burtleCI(reinterpret_cast<const unsigned char*>(uuid.c_str()), uuid.size(), hashPerturbation);
lockedHashes->push_back(wshash);
--w;
}
#ifdef HAVE_NGHTTP2
setupDoHClientProtocolNegotiation(d_tlsCtx);
- if (g_configurationDone && g_outgoingDoHWorkerThreads && *g_outgoingDoHWorkerThreads == 0) {
+ if (dnsdist::configuration::isConfigurationDone() && g_outgoingDoHWorkerThreads && *g_outgoingDoHWorkerThreads == 0) {
throw std::runtime_error("Error: setOutgoingDoHWorkerThreads() is set to 0 so no outgoing DoH worker thread is available to serve queries");
}
void DownstreamState::connectUDPSockets()
{
- if (s_randomizeIDs) {
+ const auto& config = dnsdist::configuration::getImmutableConfiguration();
+ if (config.d_randomizeIDsToBackend) {
idStates.clear();
}
else {
- idStates.resize(g_maxOutstanding);
+ idStates.resize(config.d_maxUDPOutstanding);
}
sockets.resize(d_config.d_numberOfSockets);
return sockets[0];
}
- size_t idx;
- if (s_randomizeSockets) {
+ size_t idx{0};
+ if (dnsdist::configuration::getImmutableConfiguration().d_randomizeUDPSocketsToBackend) {
idx = dnsdist::getRandomValue(numberOfSockets);
}
else {
(*mplexer.lock())->getAvailableFDs(ready, 1000);
}
-bool DownstreamState::s_randomizeSockets{false};
-bool DownstreamState::s_randomizeIDs{false};
-int DownstreamState::s_udpTimeout{2};
-
-static bool isIDSExpired(const IDState& ids)
+static bool isIDSExpired(const IDState& ids, uint8_t udpTimeout)
{
auto age = ids.age.load();
- return age > DownstreamState::s_udpTimeout;
+ return age > udpTimeout;
}
void DownstreamState::handleUDPTimeout(IDState& ids)
return;
}
- if (s_randomizeIDs) {
+ const auto& config = dnsdist::configuration::getImmutableConfiguration();
+ const auto udpTimeout = config.d_udpTimeout;
+ if (config.d_randomizeIDsToBackend) {
auto map = d_idStatesMap.lock();
for (auto it = map->begin(); it != map->end(); ) {
auto& ids = it->second;
- if (isIDSExpired(ids)) {
+ if (isIDSExpired(ids, udpTimeout)) {
handleUDPTimeout(ids);
it = map->erase(it);
continue;
if (!ids.isInUse()) {
continue;
}
- if (!isIDSExpired(ids)) {
+ if (!isIDSExpired(ids, udpTimeout)) {
++ids.age;
continue;
}
continue;
}
/* check again, now that we have locked this state */
- if (ids.isInUse() && isIDSExpired(ids)) {
+ if (ids.isInUse() && isIDSExpired(ids, udpTimeout)) {
handleUDPTimeout(ids);
}
}
uint16_t DownstreamState::saveState(InternalQueryState&& state)
{
- if (s_randomizeIDs) {
+ const auto& config = dnsdist::configuration::getImmutableConfiguration();
+ if (config.d_randomizeIDsToBackend) {
/* if the state is already in use we will retry,
up to 5 five times. The last selected one is used
even if it was already in use */
void DownstreamState::restoreState(uint16_t id, InternalQueryState&& state)
{
- if (s_randomizeIDs) {
+ const auto& config = dnsdist::configuration::getImmutableConfiguration();
+ if (config.d_randomizeIDsToBackend) {
auto map = d_idStatesMap.lock();
auto [it, inserted] = map->emplace(id, IDState());
std::optional<InternalQueryState> DownstreamState::getState(uint16_t id)
{
std::optional<InternalQueryState> result = std::nullopt;
-
- if (s_randomizeIDs) {
+ const auto& config = dnsdist::configuration::getImmutableConfiguration();
+ if (config.d_randomizeIDsToBackend) {
auto map = d_idStatesMap.lock();
auto it = map->find(id);
}
setUpStatus(newState);
- if (g_snmpAgent && g_snmpTrapsEnabled) {
+ if (g_snmpAgent != nullptr && dnsdist::configuration::getCurrentRuntimeConfiguration().d_snmpTrapsEnabled) {
g_snmpAgent->sendBackendStatusChangeTrap(*this);
}
}
#include "dnsdist-carbon.hh"
#include "dnsdist.hh"
#include "dnsdist-backoff.hh"
+#include "dnsdist-configuration.hh"
#include "dnsdist-metrics.hh"
#ifndef DISABLE_CARBON
{
std::string qname;
- auto records = g_qcount.records.write_lock();
+ auto records = dnsdist::QueryCount::g_queryCountRecords.write_lock();
for (const auto& record : *records) {
qname = record.first;
boost::replace_all(qname, ".", "_");
#include <map>
#include "iputils.hh"
#include "lock.hh"
+#include "dnsdist-configuration.hh"
namespace dnsdist
{
public:
static bool accountNewTCPConnection(const ComboAddress& from)
{
- if (s_maxTCPConnectionsPerClient == 0) {
+ const auto maxConnsPerClient = dnsdist::configuration::getImmutableConfiguration().d_maxTCPConnectionsPerClient;
+ if (maxConnsPerClient == 0) {
return true;
}
auto db = s_tcpClientsConcurrentConnectionsCount.lock();
auto& count = (*db)[from];
- if (count >= s_maxTCPConnectionsPerClient) {
+ if (count >= maxConnsPerClient) {
return false;
}
++count;
static void accountClosedTCPConnection(const ComboAddress& from)
{
- if (s_maxTCPConnectionsPerClient == 0) {
+ const auto maxConnsPerClient = dnsdist::configuration::getImmutableConfiguration().d_maxTCPConnectionsPerClient;
+ if (maxConnsPerClient == 0) {
return;
}
auto db = s_tcpClientsConcurrentConnectionsCount.lock();
}
}
- static void setMaxTCPConnectionsPerClient(size_t max)
- {
- s_maxTCPConnectionsPerClient = max;
- }
-
private:
static LockGuarded<std::map<ComboAddress, size_t, ComboAddress::addressOnlyLessThan>> s_tcpClientsConcurrentConnectionsCount;
- static size_t s_maxTCPConnectionsPerClient;
};
}
--- /dev/null
+/*
+ * 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.
+ */
+
+#include "dnsdist-configuration.hh"
+#include "sholder.hh"
+
+namespace dnsdist::configuration
+{
+static GlobalStateHolder<RuntimeConfiguration> s_currentRuntimeConfiguration;
+static Configuration s_configuration;
+static std::atomic<bool> s_configurationDone{false};
+
+const RuntimeConfiguration& getCurrentRuntimeConfiguration()
+{
+ static thread_local auto t_threadLocalConfiguration = s_currentRuntimeConfiguration.getLocal();
+ return *t_threadLocalConfiguration;
+}
+
+void updateRuntimeConfiguration(const std::function<void(RuntimeConfiguration&)>& mutator)
+{
+ s_currentRuntimeConfiguration.modify(mutator);
+}
+
+void updateImmutableConfiguration(const std::function<void(Configuration&)>& mutator)
+{
+ if (isConfigurationDone()) {
+ throw std::runtime_error("Trying to update an immutable setting at runtime!");
+ }
+
+ mutator(s_configuration);
+}
+
+const Configuration& getImmutableConfiguration()
+{
+ return s_configuration;
+}
+
+bool isConfigurationDone()
+{
+ return s_configurationDone.load();
+}
+
+void setConfigurationDone()
+{
+ s_configurationDone.store(true);
+}
+}
--- /dev/null
+/*
+ * 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 <functional>
+#include <map>
+#include <string>
+
+#include "dnsdist-query-count.hh"
+#include "iputils.hh"
+
+/* 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 */
+
+struct DNSQuestion;
+struct DNSResponse;
+
+class DNSAction
+{
+public:
+ enum class Action : uint8_t
+ {
+ Drop,
+ Nxdomain,
+ Refused,
+ Spoof,
+ Allow,
+ HeaderModify,
+ Pool,
+ Delay,
+ Truncate,
+ ServFail,
+ None,
+ NoOp,
+ NoRecurse,
+ SpoofRaw,
+ SpoofPacket,
+ SetTag,
+ };
+ 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::SetTag:
+ return "Set Tag";
+ case Action::None:
+ case Action::NoOp:
+ return "Do nothing";
+ case Action::NoRecurse:
+ return "Set rd=0";
+ }
+
+ return "Unknown";
+ }
+
+ virtual Action operator()(DNSQuestion*, std::string* ruleresult) const = 0;
+ virtual ~DNSAction() = default;
+ virtual std::string toString() const = 0;
+ virtual std::map<std::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*, std::string* ruleresult) const = 0;
+ virtual ~DNSResponseAction() = default;
+ virtual std::string toString() const = 0;
+ virtual void reload()
+ {
+ }
+};
+
+namespace dnsdist::configuration
+{
+/* when we add EDNS to a query, we don't want to advertise
+ a large buffer size */
+static constexpr size_t s_EdnsUDPPayloadSize{512};
+static constexpr uint16_t s_defaultPayloadSizeSelfGenAnswers = 1232;
+static constexpr uint16_t s_udpIncomingBufferSize{1500}; // don't accept UDP queries larger than this value
+static_assert(s_defaultPayloadSizeSelfGenAnswers < s_udpIncomingBufferSize, "The UDP responder's payload size should be smaller or equal to our incoming buffer size");
+
+struct Configuration
+{
+ std::string d_consoleKey;
+#ifdef __linux__
+ // On Linux this gives us 128k pending queries (default is 8192 queries),
+ // which should be enough to deal with huge spikes
+ uint64_t d_maxTCPQueuedConnections{10000};
+ size_t d_tcpInternalPipeBufferSize{1048576U};
+#else
+ uint64_t d_maxTCPQueuedConnections{1000};
+ size_t d_tcpInternalPipeBufferSize{0};
+#endif
+ double d_weightedBalancingFactor{0};
+ double d_consistentHashBalancingFactor{0};
+ uint64_t d_maxTCPClientThreads{0};
+ size_t d_maxTCPConnectionsPerClient{0};
+ size_t d_udpVectorSize{1};
+ uint32_t d_socketUDPSendBuffer{0};
+ uint32_t d_socketUDPRecvBuffer{0};
+ uint32_t d_hashPerturbation{0};
+ uint16_t d_maxUDPOutstanding{std::numeric_limits<uint16_t>::max()};
+ uint8_t d_udpTimeout{2};
+ bool d_randomizeUDPSocketsToBackend{false};
+ bool d_randomizeIDsToBackend{false};
+};
+
+struct RuntimeConfiguration
+{
+ NetmaskGroup d_proxyProtocolACL;
+ NetmaskGroup d_consoleACL;
+ dnsdist::QueryCount::Configuration d_queryCountConfig;
+ std::string d_secPollSuffix{"secpoll.powerdns.com."};
+ std::string d_apiConfigDirectory;
+ size_t d_maxTCPQueriesPerConn{0};
+ size_t d_maxTCPConnectionDuration{0};
+ size_t d_proxyProtocolMaximumSize{512};
+ uint32_t d_staleCacheEntriesTTL{0};
+ uint32_t d_secPollInterval{3600};
+ uint32_t d_consoleOutputMsgMaxSize{10000000};
+ uint16_t d_payloadSizeSelfGenAnswers{s_defaultPayloadSizeSelfGenAnswers};
+ uint16_t d_tcpRecvTimeout{2};
+ uint16_t d_tcpSendTimeout{2};
+ /* rfc7871: "11.1. Privacy" */
+ uint16_t d_ECSSourcePrefixV4{24};
+ uint16_t d_ECSSourcePrefixV6{56};
+ uint16_t d_cacheCleaningDelay{60};
+ uint16_t d_cacheCleaningPercentage{100};
+ uint16_t d_tlsSessionCacheCleanupDelay{60};
+ uint16_t d_tlsSessionCacheSessionValidity{600};
+ uint16_t d_tlsSessionCacheMaxSessionsPerBackend{20};
+ DNSAction::Action d_dynBlockAction{DNSAction::Action::Drop};
+ bool d_truncateTC{false};
+ bool d_fixupCase{false};
+ bool d_queryCountEnabled{false};
+ bool d_ecsOverride{false};
+ bool d_verbose{false};
+ bool d_verboseHealthChecks{false};
+ bool d_apiReadWrite{false};
+ bool d_roundrobinFailOnNoServer{false};
+ bool d_servFailOnNoPolicy{false};
+ bool d_allowEmptyResponse{false};
+ bool d_dropEmptyQueries{false};
+ bool d_snmpEnabled{false};
+ bool d_snmpTrapsEnabled{false};
+ bool d_consoleEnabled{false};
+ bool d_logConsoleConnections{true};
+ bool d_addEDNSToSelfGeneratedResponses{true};
+ bool d_applyACLToProxiedClients{false};
+};
+
+const RuntimeConfiguration& getCurrentRuntimeConfiguration();
+const Configuration& getImmutableConfiguration();
+void updateImmutableConfiguration(const std::function<void(Configuration&)>& mutator);
+void updateRuntimeConfiguration(const std::function<void(RuntimeConfiguration&)>& mutator);
+bool isConfigurationDone();
+void setConfigurationDone();
+}
#include "dnsdist-crypto.hh"
#include "threadname.hh"
-GlobalStateHolder<NetmaskGroup> g_consoleACL;
-vector<pair<struct timeval, string>> g_confDelta;
-std::string g_consoleKey;
-bool g_logConsoleConnections{true};
-bool g_consoleEnabled{false};
-uint32_t g_consoleOutputMsgMaxSize{10000000};
+static LockGuarded<std::vector<pair<timeval, string>>> s_confDelta;
static ConcurrentConnectionManager s_connManager(100);
s_connManager.setMaxConcurrentConnections(max);
}
-// MUST BE CALLED UNDER A LOCK - right now the LuaLock
static void feedConfigDelta(const std::string& line)
{
if (line.empty()) {
}
timeval now{};
gettimeofday(&now, nullptr);
- g_confDelta.emplace_back(now, line);
+ s_confDelta.lock()->emplace_back(now, line);
+}
+
+namespace dnsdist::console
+{
+const std::vector<std::pair<timeval, std::string>>& getConfigurationDelta()
+{
+ return *(s_confDelta.lock());
+}
}
#ifdef HAVE_LIBEDIT
}
*len = ntohl(raw);
- if (*len > g_consoleOutputMsgMaxSize) {
+ if (*len > dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleOutputMsgMaxSize) {
return ConsoleCommandResult::TooLarge;
}
static ConsoleCommandResult sendMessageToServer(int fileDesc, const std::string& line, dnsdist::crypto::authenticated::Nonce& readingNonce, dnsdist::crypto::authenticated::Nonce& writingNonce, const bool outputEmptyLine)
{
- string msg = dnsdist::crypto::authenticated::encryptSym(line, g_consoleKey, writingNonce);
+ const auto& consoleKey = dnsdist::configuration::getImmutableConfiguration().d_consoleKey;
+ string msg = dnsdist::crypto::authenticated::encryptSym(line, consoleKey, writingNonce);
const auto msgLen = msg.length();
if (msgLen > std::numeric_limits<uint32_t>::max()) {
cerr << "Encrypted message is too long to be sent to the server, " << std::to_string(msgLen) << " > " << std::numeric_limits<uint32_t>::max() << endl;
return commandResult;
}
if (commandResult == ConsoleCommandResult::TooLarge) {
- cerr << "Received a console message whose length (" << len << ") is exceeding the allowed one (" << g_consoleOutputMsgMaxSize << "), closing that connection" << endl;
+ cerr << "Received a console message whose length (" << len << ") is exceeding the allowed one (" << dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleOutputMsgMaxSize << "), closing that connection" << endl;
return commandResult;
}
msg.clear();
msg.resize(len);
readn2(fileDesc, msg.data(), len);
- msg = dnsdist::crypto::authenticated::decryptSym(msg, g_consoleKey, readingNonce);
+ msg = dnsdist::crypto::authenticated::decryptSym(msg, consoleKey, readingNonce);
cout << msg;
cout.flush();
void doClient(ComboAddress server, const std::string& command)
{
- if (!dnsdist::crypto::authenticated::isValidKey(g_consoleKey)) {
+ const auto& consoleKey = dnsdist::configuration::getImmutableConfiguration().d_consoleKey;
+ if (!dnsdist::crypto::authenticated::isValidKey(consoleKey)) {
cerr << "The currently configured console key is not valid, please configure a valid key using the setKey() directive" << endl;
return;
}
- if (g_verbose) {
+ if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose) {
cout << "Connecting to " << server.toStringWithPort() << endl;
}
setTCPNoDelay(conn.getFD());
+ const auto& consoleKey = dnsdist::configuration::getImmutableConfiguration().d_consoleKey;
dnsdist::crypto::authenticated::Nonce theirs;
dnsdist::crypto::authenticated::Nonce ours;
dnsdist::crypto::authenticated::Nonce readingNonce;
line.resize(len);
readn2(conn.getFD(), line.data(), len);
- line = dnsdist::crypto::authenticated::decryptSym(line, g_consoleKey, readingNonce);
+ line = dnsdist::crypto::authenticated::decryptSym(line, consoleKey, readingNonce);
string response;
try {
catch (const LuaContext::SyntaxErrorException& e) {
response = "Error: " + string(e.what()) + ": ";
}
- response = dnsdist::crypto::authenticated::encryptSym(response, g_consoleKey, writingNonce);
+ response = dnsdist::crypto::authenticated::encryptSym(response, consoleKey, writingNonce);
putMsgLen32(conn.getFD(), response.length());
writen2(conn.getFD(), response.c_str(), response.length());
}
- if (g_logConsoleConnections) {
+ if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_logConsoleConnections) {
infolog("Closed control connection from %s", conn.getClient().toStringWithPort());
}
}
client.sin4.sin_family = local.sin4.sin_family;
int sock{-1};
- auto localACL = g_consoleACL.getLocal();
infolog("Accepting control connections on %s", local.toStringWithPort());
while ((sock = SAccept(acceptFD->getHandle(), client)) >= 0) {
-
+ const auto& consoleKey = dnsdist::configuration::getImmutableConfiguration().d_consoleKey;
FDWrapper socket(sock);
- if (!dnsdist::crypto::authenticated::isValidKey(g_consoleKey)) {
+ if (!dnsdist::crypto::authenticated::isValidKey(consoleKey)) {
vinfolog("Control connection from %s dropped because we don't have a valid key configured, please configure one using setKey()", client.toStringWithPort());
continue;
}
- if (!localACL->match(client)) {
+ const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ if (!runtimeConfig.d_consoleACL.match(client)) {
vinfolog("Control connection from %s dropped because of ACL", client.toStringWithPort());
continue;
}
try {
ConsoleConnection conn(client, std::move(socket));
- if (g_logConsoleConnections) {
+ if (runtimeConfig.d_logConsoleConnections) {
warnlog("Got control connection from %s", client.toStringWithPort());
}
#ifdef HAVE_LIBEDIT
clear_history();
#endif /* HAVE_LIBEDIT */
- g_confDelta.clear();
+ s_confDelta.lock()->clear();
}
*/
#pragma once
+#include <vector>
+#include <string>
+
#include "config.h"
#include "sstuff.hh"
#endif /* DISABLE_COMPLETION */
-extern GlobalStateHolder<NetmaskGroup> g_consoleACL;
-extern std::string g_consoleKey; // in theory needs locking
-extern bool g_logConsoleConnections;
-extern bool g_consoleEnabled;
-extern uint32_t g_consoleOutputMsgMaxSize;
-
void doClient(ComboAddress server, const std::string& command);
void doConsole();
void controlThread(std::shared_ptr<Socket> acceptFD, ComboAddress local);
void clearConsoleHistory();
void setConsoleMaximumConcurrentConnections(size_t max);
+
+namespace dnsdist::console
+{
+const std::vector<std::pair<timeval, std::string>>& getConfigurationDelta();
+}
bool ServiceDiscovery::getDiscoveredConfig(const UpgradeableBackend& upgradeableBackend, ServiceDiscovery::DiscoveredResolverConfig& config)
{
+ const auto verbose = dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose;
const auto& backend = upgradeableBackend.d_ds;
const auto& addr = backend->d_config.remote;
try {
uint16_t responseSize = 0;
auto got = readn2WithTimeout(sock.getHandle(), &responseSize, sizeof(responseSize), remainingTime);
if (got != sizeof(responseSize)) {
- if (g_verbose) {
+ if (verbose) {
warnlog("Error while waiting for the ADD upgrade response size from backend %s: %d", addr.toStringWithPort(), got);
}
return false;
got = readn2WithTimeout(sock.getHandle(), packet.data(), packet.size(), remainingTime);
if (got != packet.size()) {
- if (g_verbose) {
+ if (verbose) {
warnlog("Error while waiting for the ADD upgrade response from backend %s: %d", addr.toStringWithPort(), got);
}
return false;
}
if (packet.size() <= sizeof(struct dnsheader)) {
- if (g_verbose) {
+ if (verbose) {
warnlog("Too short answer of size %d received from the backend %s", packet.size(), addr.toStringWithPort());
}
return false;
struct dnsheader d;
memcpy(&d, packet.data(), sizeof(d));
if (d.id != id) {
- if (g_verbose) {
+ if (verbose) {
warnlog("Invalid ID (%d / %d) received from the backend %s", d.id, id, addr.toStringWithPort());
}
return false;
}
if (d.rcode != RCode::NoError) {
- if (g_verbose) {
+ if (verbose) {
warnlog("Response code '%s' received from the backend %s for '%s'", RCode::to_s(d.rcode), addr.toStringWithPort(), s_discoveryDomain);
}
}
if (ntohs(d.qdcount) != 1) {
- if (g_verbose) {
+ if (verbose) {
warnlog("Invalid answer (qdcount %d) received from the backend %s", ntohs(d.qdcount), addr.toStringWithPort());
}
return false;
DNSName receivedName(reinterpret_cast<const char*>(packet.data()), packet.size(), sizeof(dnsheader), false, &receivedType, &receivedClass);
if (receivedName != s_discoveryDomain || receivedType != s_discoveryType || receivedClass != QClass::IN) {
- if (g_verbose) {
+ if (verbose) {
warnlog("Invalid answer, either the qname (%s / %s), qtype (%s / %s) or qclass (%s / %s) does not match, received from the backend %s", receivedName, s_discoveryDomain, QType(receivedType).toString(), s_discoveryType.toString(), QClass(receivedClass).toString(), QClass::IN.toString(), addr.toStringWithPort());
}
return false;
GlobalStateHolder<NetmaskTree<DynBlock, AddressAndPortRange>> g_dynblockNMG;
GlobalStateHolder<SuffixMatchTree<DynBlock>> g_dynblockSMT;
-DNSAction::Action g_dynBlockAction = DNSAction::Action::Drop;
#ifndef DISABLE_DYNBLOCKS
void DynBlockRulesGroup::apply(const timespec& now)
/* return the actual action that will be taken by that block:
- either the one set on that block, if any
- - or the one set with setDynBlocksAction in g_dynBlockAction
+ - or the one set with setDynBlocksAction
*/
static DNSAction::Action getActualAction(const DynBlock& block)
{
if (block.action != DNSAction::Action::None) {
return block.action;
}
- return g_dynBlockAction;
+ return dnsdist::configuration::getCurrentRuntimeConfiguration().d_dynBlockAction;
}
namespace dnsdist::DynamicBlocks
#include "ednsoptions.hh"
#include "ednssubnet.hh"
-/* when we add EDNS to a query, we don't want to advertise
- a large buffer size */
-size_t g_EdnsUDPPayloadSize = 512;
-static const uint16_t defaultPayloadSizeSelfGenAnswers = 1232;
-static_assert(defaultPayloadSizeSelfGenAnswers < s_udpIncomingBufferSize, "The UDP responder's payload size should be smaller or equal to our incoming buffer size");
-uint16_t g_PayloadSizeSelfGenAnswers{defaultPayloadSizeSelfGenAnswers};
-
-/* draft-ietf-dnsop-edns-client-subnet-04 "11.1. Privacy" */
-uint16_t g_ECSSourcePrefixV4 = 24;
-uint16_t g_ECSSourcePrefixV6 = 56;
-
-bool g_ECSOverride{false};
-bool g_addEDNSToSelfGeneratedResponses{true};
-
int rewriteResponseWithoutEDNS(const PacketBuffer& initialPacket, PacketBuffer& newContent)
{
if (initialPacket.size() < sizeof(dnsheader)) {
}
if (ednsAdded) {
- packetWriter.addOpt(g_EdnsUDPPayloadSize, 0, 0, {{optionToReplace, std::string(&newOptionContent.at(EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE), newOptionContent.size() - (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE))}}, 0);
+ packetWriter.addOpt(dnsdist::configuration::s_EdnsUDPPayloadSize, 0, 0, {{optionToReplace, std::string(&newOptionContent.at(EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE), newOptionContent.size() - (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE))}}, 0);
optionAdded = true;
}
static bool addEDNSWithECS(PacketBuffer& packet, size_t maximumSize, const string& newECSOption, bool& ednsAdded, bool& ecsAdded)
{
- if (!generateOptRR(newECSOption, packet, maximumSize, g_EdnsUDPPayloadSize, 0, false)) {
+ if (!generateOptRR(newECSOption, packet, maximumSize, dnsdist::configuration::s_EdnsUDPPayloadSize, 0, false)) {
return false;
}
bool hadEDNS = false;
bool dnssecOK = false;
- if (g_addEDNSToSelfGeneratedResponses) {
+ if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses) {
uint16_t payloadSize = 0;
uint16_t zValue = 0;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
if (hadEDNS) {
/* now we need to add a new OPT record */
- return addEDNS(packet, dnsQuestion.getMaximumSize(), dnssecOK, g_PayloadSizeSelfGenAnswers, dnsQuestion.ednsRCode);
+ return addEDNS(packet, dnsQuestion.getMaximumSize(), dnssecOK, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers, dnsQuestion.ednsRCode);
}
return true;
return true;
});
- if (g_addEDNSToSelfGeneratedResponses) {
+ if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses) {
/* now we need to add a new OPT record */
- return addEDNS(packet, dnsQuestion.getMaximumSize(), dnssecOK, g_PayloadSizeSelfGenAnswers, dnsQuestion.ednsRCode);
+ return addEDNS(packet, dnsQuestion.getMaximumSize(), dnssecOK, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers, dnsQuestion.ednsRCode);
}
/* otherwise we are just fine */
}
auto& data = dnsQuestion.getMutableData();
- if (generateOptRR(optRData, data, dnsQuestion.getMaximumSize(), g_EdnsUDPPayloadSize, 0, false)) {
+ if (generateOptRR(optRData, data, dnsQuestion.getMaximumSize(), dnsdist::configuration::s_EdnsUDPPayloadSize, 0, false)) {
dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
header.arcount = htons(1);
return true;
buffer.resize(sizeof(dnsheader) + qnameLength + sizeof(uint16_t) + sizeof(uint16_t));
if (hadEDNS) {
DNSQuestion dnsQuestion(state, buffer);
- if (!addEDNS(buffer, dnsQuestion.getMaximumSize(), (edns0.extFlags & htons(EDNS_HEADER_FLAG_DO)) != 0, g_PayloadSizeSelfGenAnswers, 0)) {
+ if (!addEDNS(buffer, dnsQuestion.getMaximumSize(), (edns0.extFlags & htons(EDNS_HEADER_FLAG_DO)) != 0, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers, 0)) {
return false;
}
}
// root label (1), type (2), class (2), ttl (4) + rdlen (2)
static const size_t optRecordMinimumSize = 11;
-extern size_t g_EdnsUDPPayloadSize;
-extern uint16_t g_PayloadSizeSelfGenAnswers;
-
int rewriteResponseWithoutEDNS(const PacketBuffer& initialPacket, PacketBuffer& newContent);
bool slowRewriteEDNSOptionInQueryWithRecords(const PacketBuffer& initialPacket, PacketBuffer& newContent, bool& ednsAdded, uint16_t optionToReplace, bool& optionAdded, bool overrideExisting, const string& newOptionContent);
int locateEDNSOptRR(const PacketBuffer& packet, uint16_t* optStart, size_t* optLen, bool* last);
#include "dnsdist-nghttp2.hh"
#include "dnsdist-session-cache.hh"
-bool g_verboseHealthChecks{false};
-
struct HealthCheckData
{
enum class TCPState : uint8_t
static bool handleResponse(std::shared_ptr<HealthCheckData>& data)
{
+ const auto verboseHealthChecks = dnsdist::configuration::getCurrentRuntimeConfiguration().d_verboseHealthChecks;
const auto& downstream = data->d_ds;
try {
if (data->d_buffer.size() < sizeof(dnsheader)) {
++data->d_ds->d_healthCheckMetrics.d_parseErrors;
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Invalid health check response of size %d from backend %s, expecting at least %d", data->d_buffer.size(), downstream->getNameWithAddr(), sizeof(dnsheader));
}
return false;
dnsheader_aligned responseHeader(data->d_buffer.data());
if (responseHeader.get()->id != data->d_queryID) {
++data->d_ds->d_healthCheckMetrics.d_mismatchErrors;
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Invalid health check response id %d from backend %s, expecting %d", responseHeader.get()->id, downstream->getNameWithAddr(), data->d_queryID);
}
return false;
if (!responseHeader.get()->qr) {
++data->d_ds->d_healthCheckMetrics.d_invalidResponseErrors;
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Invalid health check response from backend %s, expecting QR to be set", downstream->getNameWithAddr());
}
return false;
if (responseHeader.get()->rcode == RCode::ServFail) {
++data->d_ds->d_healthCheckMetrics.d_invalidResponseErrors;
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Backend %s responded to health check with ServFail", downstream->getNameWithAddr());
}
return false;
if (downstream->d_config.mustResolve && (responseHeader.get()->rcode == RCode::NXDomain || responseHeader.get()->rcode == RCode::Refused)) {
++data->d_ds->d_healthCheckMetrics.d_invalidResponseErrors;
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Backend %s responded to health check with %s while mustResolve is set", downstream->getNameWithAddr(), responseHeader.get()->rcode == RCode::NXDomain ? "NXDomain" : "Refused");
}
return false;
if (receivedName != data->d_checkName || receivedType != data->d_checkType || receivedClass != data->d_checkClass) {
++data->d_ds->d_healthCheckMetrics.d_mismatchErrors;
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Backend %s responded to health check with an invalid qname (%s vs %s), qtype (%s vs %s) or qclass (%d vs %d)", downstream->getNameWithAddr(), receivedName.toLogString(), data->d_checkName.toLogString(), QType(receivedType).toString(), QType(data->d_checkType).toString(), receivedClass, data->d_checkClass);
}
return false;
}
catch (const std::exception& e) {
++data->d_ds->d_healthCheckMetrics.d_parseErrors;
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Error checking the health of backend %s: %s", downstream->getNameWithAddr(), e.what());
}
return false;
}
catch (...) {
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Unknown exception while checking the health of backend %s", downstream->getNameWithAddr());
}
return false;
static void healthCheckUDPCallback(int descriptor, FDMultiplexer::funcparam_t& param)
{
auto data = boost::any_cast<std::shared_ptr<HealthCheckData>>(param);
+ const auto verboseHealthChecks = dnsdist::configuration::getCurrentRuntimeConfiguration().d_verboseHealthChecks;
ssize_t got = 0;
ComboAddress from;
return;
}
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Error receiving health check response from %s: %s", data->d_ds->d_config.remote.toStringWithPort(), stringerror(savederrno));
}
++data->d_ds->d_healthCheckMetrics.d_networkErrors;
/* we are using a connected socket but hey.. */
if (from != data->d_ds->d_config.remote) {
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Invalid health check response received from %s, expecting one from %s", from.toStringWithPort(), data->d_ds->d_config.remote.toStringWithPort());
}
++data->d_ds->d_healthCheckMetrics.d_networkErrors;
catch (const std::exception& e) {
++data->d_ds->d_healthCheckMetrics.d_networkErrors;
data->d_ds->submitHealthCheckResult(data->d_initial, false);
- if (g_verboseHealthChecks) {
+ const auto verboseHealthChecks = dnsdist::configuration::getCurrentRuntimeConfiguration().d_verboseHealthChecks;
+ if (verboseHealthChecks) {
infolog("Error checking the health of backend %s: %s", data->d_ds->getNameWithAddr(), e.what());
}
}
catch (...) {
data->d_ds->submitHealthCheckResult(data->d_initial, false);
- if (g_verboseHealthChecks) {
+ const auto verboseHealthChecks = dnsdist::configuration::getCurrentRuntimeConfiguration().d_verboseHealthChecks;
+ if (verboseHealthChecks) {
infolog("Unknown exception while checking the health of backend %s", data->d_ds->getNameWithAddr());
}
}
bool queueHealthCheck(std::unique_ptr<FDMultiplexer>& mplexer, const std::shared_ptr<DownstreamState>& downstream, bool initialCheck)
{
+ const auto verboseHealthChecks = dnsdist::configuration::getCurrentRuntimeConfiguration().d_verboseHealthChecks;
try {
uint16_t queryID = dnsdist::getRandomDNSID();
DNSName checkName = downstream->d_config.checkName;
#ifdef SO_BINDTODEVICE
if (!downstream->d_config.sourceItfName.empty()) {
int res = setsockopt(sock.getHandle(), SOL_SOCKET, SO_BINDTODEVICE, downstream->d_config.sourceItfName.c_str(), downstream->d_config.sourceItfName.length());
- if (res != 0 && g_verboseHealthChecks) {
+ if (res != 0 && verboseHealthChecks) {
infolog("Error setting SO_BINDTODEVICE on the health check socket for backend '%s': %s", downstream->getNameWithAddr(), stringerror());
}
}
ssize_t sent = udpClientSendRequestToBackend(downstream, data->d_udpSocket.getHandle(), packet, true);
if (sent < 0) {
int ret = errno;
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Error while sending a health check query (ID %d) to backend %s: %d", queryID, downstream->getNameWithAddr(), ret);
}
return false;
return true;
}
catch (const std::exception& e) {
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Error checking the health of backend %s: %s", downstream->getNameWithAddr(), e.what());
}
return false;
}
catch (...) {
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Unknown exception while checking the health of backend %s", downstream->getNameWithAddr());
}
return false;
void handleQueuedHealthChecks(FDMultiplexer& mplexer, bool initial)
{
+ const auto verboseHealthChecks = dnsdist::configuration::getCurrentRuntimeConfiguration().d_verboseHealthChecks;
while (mplexer.getWatchedFDCount(false) > 0 || mplexer.getWatchedFDCount(true) > 0) {
struct timeval now
{
};
int ret = mplexer.run(&now, 100);
if (ret == -1) {
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Error while waiting for the health check response from backends: %d", ret);
}
break;
else {
mplexer.removeReadFD(timeout.first);
}
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Timeout while waiting for the health check response (ID %d) from backend %s", data->d_queryID, data->d_ds->getNameWithAddr());
}
/* this is not supposed to happen as the file descriptor has to be
there for us to reach that code, and the submission code should not throw,
but let's provide a nice error message if it ever does. */
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Error while dealing with a timeout for the health check response (ID %d) from backend %s: %s", data->d_queryID, data->d_ds->getNameWithAddr(), e.what());
}
}
catch (...) {
/* this is even less likely to happen */
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Error while dealing with a timeout for the health check response (ID %d) from backend %s", data->d_queryID, data->d_ds->getNameWithAddr());
}
}
try {
/* UDP does not block while writing, H2 is handled separately */
data->d_ioState.reset();
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Timeout while waiting for the health check response (ID %d) from backend %s", data->d_queryID, data->d_ds->getNameWithAddr());
}
catch (const std::exception& e) {
/* this is not supposed to happen as the submission code should not throw,
but let's provide a nice error message if it ever does. */
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Error while dealing with a timeout for the health check response (ID %d) from backend %s: %s", data->d_queryID, data->d_ds->getNameWithAddr(), e.what());
}
}
catch (...) {
/* this is even less likely to happen */
- if (g_verboseHealthChecks) {
+ if (verboseHealthChecks) {
infolog("Error while dealing with a timeout for the health check response (ID %d) from backend %s", data->d_queryID, data->d_ds->getNameWithAddr());
}
}
#include "mplexer.hh"
#include "sstuff.hh"
-extern bool g_verboseHealthChecks;
-
bool queueHealthCheck(std::unique_ptr<FDMultiplexer>& mplexer, const std::shared_ptr<DownstreamState>& downstream, bool initial = false);
void handleQueuedHealthChecks(FDMultiplexer& mplexer, bool initial = false);
#include "dns_random.hh"
GlobalStateHolder<ServerPolicy> g_policy;
-bool g_roundrobinFailOnNoServer{false};
static constexpr size_t s_staticArrayCutOff = 16;
template <typename T> using DynamicIndexArray = std::vector<std::pair<T, size_t>>;
return leastOutstanding(servers, dq);
}
-double g_weightedBalancingFactor = 0;
-
template <class T> static std::shared_ptr<DownstreamState> getValRandom(const ServerPolicy::NumberedServerVector& servers, T& poss, const unsigned int val, const double targetLoad)
{
constexpr int max = std::numeric_limits<int>::max();
int sum = 0;
size_t usableServers = 0;
+ const auto weightedBalancingFactor = dnsdist::configuration::getImmutableConfiguration().d_consistentHashBalancingFactor;
for (const auto& d : servers) { // w=1, w=10 -> 1, 11
- if (d.second->isUp() && (g_weightedBalancingFactor == 0 || (d.second->outstanding <= (targetLoad * d.second->d_config.d_weight)))) {
+ if (d.second->isUp() && (weightedBalancingFactor == 0 || (static_cast<double>(d.second->outstanding.load()) <= (targetLoad * d.second->d_config.d_weight)))) {
// Don't overflow sum when adding high weights
if (d.second->d_config.d_weight > max - sum) {
sum = max;
{
using ValRandomType = int;
double targetLoad = std::numeric_limits<double>::max();
-
- if (g_weightedBalancingFactor > 0) {
+ const auto weightedBalancingFactor = dnsdist::configuration::getImmutableConfiguration().d_consistentHashBalancingFactor;
+ if (weightedBalancingFactor > 0) {
/* we start with one, representing the query we are currently handling */
double currentLoad = 1;
size_t totalWeight = 0;
}
if (totalWeight > 0) {
- targetLoad = (currentLoad / totalWeight) * g_weightedBalancingFactor;
+ targetLoad = (currentLoad / static_cast<double>(totalWeight)) * weightedBalancingFactor;
}
}
return valrandom(dns_random_uint32(), servers);
}
-uint32_t g_hashperturb;
-double g_consistentHashBalancingFactor = 0;
-
shared_ptr<DownstreamState> whashedFromHash(const ServerPolicy::NumberedServerVector& servers, size_t hash)
{
return valrandom(hash, servers);
shared_ptr<DownstreamState> whashed(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq)
{
- return whashedFromHash(servers, dq->ids.qname.hash(g_hashperturb));
+ const auto hashPerturbation = dnsdist::configuration::getImmutableConfiguration().d_hashPerturbation;
+ return whashedFromHash(servers, dq->ids.qname.hash(hashPerturbation));
}
shared_ptr<DownstreamState> chashedFromHash(const ServerPolicy::NumberedServerVector& servers, size_t qhash)
shared_ptr<DownstreamState> ret = nullptr, first = nullptr;
double targetLoad = std::numeric_limits<double>::max();
- if (g_consistentHashBalancingFactor > 0) {
+ const auto consistentHashBalancingFactor = dnsdist::configuration::getImmutableConfiguration().d_consistentHashBalancingFactor;
+ if (consistentHashBalancingFactor > 0) {
/* we start with one, representing the query we are currently handling */
double currentLoad = 1;
size_t totalWeight = 0;
}
if (totalWeight > 0) {
- targetLoad = (currentLoad / totalWeight) * g_consistentHashBalancingFactor;
+ targetLoad = (currentLoad / totalWeight) * consistentHashBalancingFactor;
}
}
for (const auto& d: servers) {
- if (d.second->isUp() && (g_consistentHashBalancingFactor == 0 || d.second->outstanding <= (targetLoad * d.second->d_config.d_weight))) {
+ if (d.second->isUp() && (consistentHashBalancingFactor == 0 || static_cast<double>(d.second->outstanding.load()) <= (targetLoad * d.second->d_config.d_weight))) {
// make sure hashes have been computed
if (!d.second->hashesComputed) {
d.second->hash();
shared_ptr<DownstreamState> chashed(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq)
{
- return chashedFromHash(servers, dq->ids.qname.hash(g_hashperturb));
+ const auto hashPerturbation = dnsdist::configuration::getImmutableConfiguration().d_hashPerturbation;
+ return chashedFromHash(servers, dq->ids.qname.hash(hashPerturbation));
}
shared_ptr<DownstreamState> roundrobin(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq)
}
if (candidates.empty()) {
- if (g_roundrobinFailOnNoServer) {
+ if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_roundrobinFailOnNoServer) {
return shared_ptr<DownstreamState>();
}
for (auto& d : servers) {
std::shared_ptr<DownstreamState> chashed(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq);
std::shared_ptr<DownstreamState> chashedFromHash(const ServerPolicy::NumberedServerVector& servers, size_t hash);
std::shared_ptr<DownstreamState> roundrobin(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq);
-
-extern double g_consistentHashBalancingFactor;
-extern double g_weightedBalancingFactor;
-extern uint32_t g_hashperturb;
-extern bool g_roundrobinFailOnNoServer;
#include "dnsdist-proxy-protocol.hh"
#include "dnsdist-kvs.hh"
#include "dnsdist-rule-chains.hh"
+#include "dnsdist-snmp.hh"
#include "dnsdist-svc.hh"
#include "dnstap.hh"
void TeeAction::worker()
{
setThreadName("dnsdist/TeeWork");
- std::array<char, s_udpIncomingBufferSize> packet{};
+ std::array<char, dnsdist::configuration::s_udpIncomingBufferSize> packet{};
ssize_t res = 0;
const dnsheader_aligned dnsheader(packet.data());
for (;;) {
if (!dnsdist::svc::generateSVCResponse(*dnsquestion, d_payloads, d_additionals4, d_additionals6, d_responseConfig)) {
return Action::None;
}
+
return Action::HeaderModify;
}
bool dnssecOK = false;
bool hadEDNS = false;
- if (g_addEDNSToSelfGeneratedResponses && queryHasEDNS(*dnsquestion)) {
+ const auto& runtimeConfiguration = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ if (runtimeConfiguration.d_addEDNSToSelfGeneratedResponses && queryHasEDNS(*dnsquestion)) {
hadEDNS = true;
dnssecOK = ((getEDNSZ(*dnsquestion) & EDNS_HEADER_FLAG_DO) != 0);
}
});
if (hadEDNS && !raw) {
- addEDNS(dnsquestion->getMutableData(), dnsquestion->getMaximumSize(), dnssecOK, g_PayloadSizeSelfGenAnswers, 0);
+ addEDNS(dnsquestion->getMutableData(), dnsquestion->getMaximumSize(), dnssecOK, runtimeConfiguration.d_payloadSizeSelfGenAnswers, 0);
}
return Action::HeaderModify;
}
auto& data = dnsquestion->getMutableData();
- if (generateOptRR(optRData, data, dnsquestion->getMaximumSize(), g_EdnsUDPPayloadSize, 0, false)) {
+ if (generateOptRR(optRData, data, dnsquestion->getMaximumSize(), dnsdist::configuration::s_EdnsUDPPayloadSize, 0, false)) {
dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsquestion->getMutableData(), [](dnsheader& header) {
header.arcount = htons(1);
return true;
{
auto filepointer = std::atomic_load_explicit(&d_fp, std::memory_order_acquire);
if (!filepointer) {
- if (!d_verboseOnly || g_verbose) {
+ if (!d_verboseOnly || dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose) {
if (d_includeTimestamp) {
infolog("[%u.%u] Packet from %s for %s %s with id %d", static_cast<unsigned long long>(dnsquestion->getQueryRealTime().tv_sec), static_cast<unsigned long>(dnsquestion->getQueryRealTime().tv_nsec), dnsquestion->ids.origRemote.toStringWithPort(), dnsquestion->ids.qname.toString(), QType(dnsquestion->ids.qtype).toString(), dnsquestion->getHeader()->id);
}
{
auto filepointer = std::atomic_load_explicit(&d_fp, std::memory_order_acquire);
if (!filepointer) {
- if (!d_verboseOnly || g_verbose) {
+ if (!d_verboseOnly || dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose) {
if (d_includeTimestamp) {
infolog("[%u.%u] Answer to %s for %s %s (%s) with id %u", static_cast<unsigned long long>(response->getQueryRealTime().tv_sec), static_cast<unsigned long>(response->getQueryRealTime().tv_nsec), response->ids.origRemote.toStringWithPort(), response->ids.qname.toString(), QType(response->ids.qtype).toString(), RCode::to_s(response->getHeader()->rcode), response->getHeader()->id);
}
}
DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
{
- if (g_snmpAgent != nullptr && g_snmpTrapsEnabled) {
+ if (g_snmpAgent != nullptr && dnsdist::configuration::getCurrentRuntimeConfiguration().d_snmpTrapsEnabled) {
g_snmpAgent->sendDNSTrap(*dnsquestion, d_reason);
}
}
DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override
{
- if (g_snmpAgent != nullptr && g_snmpTrapsEnabled) {
+ if (g_snmpAgent != nullptr && dnsdist::configuration::getCurrentRuntimeConfiguration().d_snmpTrapsEnabled) {
g_snmpAgent->sendDNSTrap(*response, d_reason);
}
#include "dnsdist-ecs.hh"
#include "dnsdist-internal-queries.hh"
#include "dnsdist-lua.hh"
+#include "dnsdist-snmp.hh"
#include "dnsparser.hh"
// NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold
luaCtx.registerFunction<void (DNSQuestion::*)(std::string)>("sendTrap", [](const DNSQuestion& dnsQuestion, boost::optional<std::string> reason) {
#ifdef HAVE_NET_SNMP
- if (g_snmpAgent != nullptr && g_snmpTrapsEnabled) {
+ if (g_snmpAgent != nullptr && dnsdist::configuration::getCurrentRuntimeConfiguration().d_snmpTrapsEnabled) {
g_snmpAgent->sendDNSTrap(dnsQuestion, reason ? *reason : "");
}
#endif /* HAVE_NET_SNMP */
luaCtx.registerFunction<void (DNSResponse::*)(std::string)>("sendTrap", [](const DNSResponse& dnsResponse, boost::optional<std::string> reason) {
#ifdef HAVE_NET_SNMP
- if (g_snmpAgent != nullptr && g_snmpTrapsEnabled) {
+ if (g_snmpAgent != nullptr && dnsdist::configuration::getCurrentRuntimeConfiguration().d_snmpTrapsEnabled) {
g_snmpAgent->sendDNSTrap(dnsResponse, reason ? *reason : "");
}
#endif /* HAVE_NET_SNMP */
luaCtx.registerFunction<void (std::shared_ptr<BPFFilter>::*)()>("attachToAllBinds", [](std::shared_ptr<BPFFilter>& bpf) {
std::string res;
- if (!g_configurationDone) {
+ if (!dnsdist::configuration::isConfigurationDone()) {
throw std::runtime_error("attachToAllBinds() cannot be used at configuration time!");
return;
}
#ifdef HAVE_XSK
using xskopt_t = LuaAssociativeTable<boost::variant<uint32_t, std::string>>;
luaCtx.writeFunction("newXsk", [client](xskopt_t opts) {
- if (g_configurationDone) {
+ if (dnsdist::configuration::isConfigurationDone()) {
throw std::runtime_error("newXsk() only can be used at configuration time!");
}
if (client) {
#include "dnsdist-ecs.hh"
#include "dnsdist-rings.hh"
#include "dnsdist-svc.hh"
+#include "dnsdist-snmp.hh"
#include "dolog.hh"
uint16_t dnsdist_ffi_dnsquestion_get_qtype(const dnsdist_ffi_dnsquestion_t* dq)
void dnsdist_ffi_dnsquestion_send_trap(dnsdist_ffi_dnsquestion_t* dq, const char* reason, size_t reasonLen)
{
- if (g_snmpAgent && g_snmpTrapsEnabled) {
+ if (g_snmpAgent != nullptr && dnsdist::configuration::getCurrentRuntimeConfiguration().d_snmpTrapsEnabled) {
g_snmpAgent->sendDNSTrap(*dq->dq, std::string(reason, reasonLen));
}
}
if (g_defaultBPFFilter && details.bpf) {
counter += g_defaultBPFFilter->getHits(client.getNetwork());
}
- list->d_entries.push_back({strdup(client.toString().c_str()), strdup(details.reason.c_str()), counter, static_cast<uint64_t>(details.until.tv_sec - now.tv_sec), static_cast<uint8_t>(details.action != DNSAction::Action::None ? details.action : g_dynBlockAction), g_defaultBPFFilter && details.bpf, details.warning});
+ list->d_entries.push_back({strdup(client.toString().c_str()), strdup(details.reason.c_str()), counter, static_cast<uint64_t>(details.until.tv_sec - now.tv_sec), static_cast<uint8_t>(details.action != DNSAction::Action::None ? details.action : dnsdist::configuration::getCurrentRuntimeConfiguration().d_dynBlockAction), g_defaultBPFFilter && details.bpf, details.warning});
}
auto count = list->d_entries.size();
};
gettime(&now);
+ const auto defaultAction = dnsdist::configuration::getCurrentRuntimeConfiguration().d_dynBlockAction;
auto fullCopy = g_dynblockSMT.getCopy();
- fullCopy.visit([&now, &list](const SuffixMatchTree<DynBlock>& node) {
+ fullCopy.visit([&now, &list, defaultAction](const SuffixMatchTree<DynBlock>& node) {
if (!(now < node.d_value.until)) {
return;
}
key = entry.domain.toString();
}
if (entry.action == DNSAction::Action::None) {
- entry.action = g_dynBlockAction;
+ entry.action = defaultAction;
}
list->d_entries.push_back({strdup(key.c_str()), strdup(entry.reason.c_str()), entry.blocks, static_cast<uint64_t>(entry.until.tv_sec - now.tv_sec), static_cast<uint8_t>(entry.action), entry.bpf, entry.warning});
});
#include <fcntl.h>
#include "dnsdist.hh"
-#include "dnsdist-lua.hh"
+#include "dnsdist-console.hh"
#include "dnsdist-dynblocks.hh"
+#include "dnsdist-lua.hh"
#include "dnsdist-nghttp2.hh"
#include "dnsdist-rings.hh"
#include "dnsdist-tcp.hh"
luaCtx.writeFunction("delta", []() {
setLuaNoSideEffect();
// we hold the lua lock already!
- for (const auto& entry : g_confDelta) {
+ for (const auto& entry : dnsdist::console::getConfigurationDelta()) {
tm entryTime{};
localtime_r(&entry.first.tv_sec, &entryTime);
std::array<char, 80> date{};
luaCtx.writeFunction("showTCPStats", [] {
setLuaNoSideEffect();
+ const auto& immutableConfig = dnsdist::configuration::getImmutableConfiguration();
ostringstream ret;
boost::format fmt("%-12d %-12d %-12d %-12d");
ret << (fmt % "Workers" % "Max Workers" % "Queued" % "Max Queued") << endl;
- ret << (fmt % g_tcpclientthreads->getThreadsCount() % (g_maxTCPClientThreads ? *g_maxTCPClientThreads : 0) % g_tcpclientthreads->getQueuedCount() % g_maxTCPQueuedConnections) << endl;
+ ret << (fmt % g_tcpclientthreads->getThreadsCount() % immutableConfig.d_maxTCPClientThreads % g_tcpclientthreads->getQueuedCount() % immutableConfig.d_maxTCPQueuedConnections) << endl;
ret << endl;
ret << "Frontends:" << endl;
#include "dnsdist.hh"
#include "dnsdist-carbon.hh"
#include "dnsdist-concurrent-connections.hh"
+#include "dnsdist-configuration.hh"
#include "dnsdist-console.hh"
#include "dnsdist-crypto.hh"
#include "dnsdist-dynblocks.hh"
#include "dnsdist-rings.hh"
#include "dnsdist-secpoll.hh"
#include "dnsdist-session-cache.hh"
+#include "dnsdist-snmp.hh"
#include "dnsdist-tcp-downstream.hh"
#include "dnsdist-web.hh"
static boost::optional<std::vector<std::function<void(void)>>> g_launchWork = boost::none;
-boost::tribool g_noLuaSideEffect;
-static bool g_included{false};
+static boost::tribool s_noLuaSideEffect;
/* this is a best effort way to prevent logging calls with no side-effects in the output of delta()
Functions can declare setLuaNoSideEffect() and if nothing else does declare a side effect, or nothing
has done so before on this invocation, this call won't be part of delta() output */
void setLuaNoSideEffect()
{
- if (g_noLuaSideEffect == false) {
+ if (s_noLuaSideEffect == false) {
// there has been a side effect already
return;
}
- g_noLuaSideEffect = true;
+ s_noLuaSideEffect = true;
}
void setLuaSideEffect()
{
- g_noLuaSideEffect = false;
+ s_noLuaSideEffect = false;
}
bool getLuaNoSideEffect()
{
- if (g_noLuaSideEffect) {
+ if (s_noLuaSideEffect) {
// NOLINTNEXTLINE(readability-simplify-boolean-expr): it's a tribool, not a boolean
return true;
}
void resetLuaSideEffect()
{
- g_noLuaSideEffect = boost::logic::indeterminate;
+ s_noLuaSideEffect = boost::logic::indeterminate;
}
using localbind_t = LuaAssociativeTable<boost::variant<bool, int, std::string, LuaArray<int>, LuaArray<std::string>, LuaAssociativeTable<std::string>, std::shared_ptr<XskSocket>>>;
static bool checkConfigurationTime(const std::string& name)
{
- if (!g_configurationDone) {
+ if (!dnsdist::configuration::isConfigurationDone()) {
return true;
}
g_outputBuffer = name + " cannot be used at runtime!\n";
static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
{
luaCtx.writeFunction("inClientStartup", [client]() {
- return client && !g_configurationDone;
+ return client && !dnsdist::configuration::isConfigurationDone();
});
luaCtx.writeFunction("inConfigCheck", [configCheck]() {
#ifdef HAVE_XSK
LuaArray<std::shared_ptr<XskSocket>> luaXskSockets;
if (getOptionalValue<LuaArray<std::shared_ptr<XskSocket>>>(vars, "xskSockets", luaXskSockets) > 0 && !luaXskSockets.empty()) {
- if (g_configurationDone) {
+ if (dnsdist::configuration::isConfigurationDone()) {
throw std::runtime_error("Adding a server with xsk at runtime is not supported");
}
std::vector<std::shared_ptr<XskSocket>> xskSockets;
server->stop();
});
- luaCtx.writeFunction("truncateTC", [](bool value) { setLuaSideEffect(); g_truncateTC = value; });
- luaCtx.writeFunction("fixupCase", [](bool value) { setLuaSideEffect(); g_fixupCase = value; });
+ struct BooleanConfigurationItems
+ {
+ const std::string name;
+ const std::function<void(dnsdist::configuration::RuntimeConfiguration& config, bool newValue)> mutator;
+ };
+ static const std::vector<BooleanConfigurationItems> booleanConfigItems{
+ {"truncateTC", [](dnsdist::configuration::RuntimeConfiguration& config, bool newValue) { config.d_truncateTC = newValue; }},
+ {"fixupCase", [](dnsdist::configuration::RuntimeConfiguration& config, bool newValue) { config.d_fixupCase = newValue; }},
+ {"setECSOverride", [](dnsdist::configuration::RuntimeConfiguration& config, bool newValue) { config.d_ecsOverride = newValue; }},
+ {"setQueryCount", [](dnsdist::configuration::RuntimeConfiguration& config, bool newValue) { config.d_queryCountConfig.d_enabled = newValue; }},
+ {"setVerbose", [](dnsdist::configuration::RuntimeConfiguration& config, bool newValue) { config.d_verbose = newValue; }},
+ {"setVerboseHealthChecks", [](dnsdist::configuration::RuntimeConfiguration& config, bool newValue) { config.d_verboseHealthChecks = newValue; }},
+ {"setServFailWhenNoServer", [](dnsdist::configuration::RuntimeConfiguration& config, bool newValue) { config.d_servFailOnNoPolicy = newValue; }},
+ {"setRoundRobinFailOnNoServer", [](dnsdist::configuration::RuntimeConfiguration& config, bool newValue) { config.d_roundrobinFailOnNoServer = newValue; }},
+ {"setDropEmptyQueries", [](dnsdist::configuration::RuntimeConfiguration& config, bool newValue) { config.d_dropEmptyQueries = newValue; }},
+ {"setAllowEmptyResponse", [](dnsdist::configuration::RuntimeConfiguration& config, bool newValue) { config.d_allowEmptyResponse = newValue; }},
+ {"setConsoleConnectionsLogging", [](dnsdist::configuration::RuntimeConfiguration& config, bool newValue) { config.d_logConsoleConnections = newValue; }},
+ {"setProxyProtocolApplyACLToProxiedClients", [](dnsdist::configuration::RuntimeConfiguration& config, bool newValue) { config.d_applyACLToProxiedClients = newValue; }},
+ {"setAddEDNSToSelfGeneratedResponses", [](dnsdist::configuration::RuntimeConfiguration& config, bool newValue) { config.d_addEDNSToSelfGeneratedResponses = newValue; }},
+ };
+ struct UnsignedIntegerConfigurationItems
+ {
+ const std::string name;
+ const std::function<void(dnsdist::configuration::RuntimeConfiguration& config, uint64_t value)> mutator;
+ const size_t maximumValue{std::numeric_limits<uint64_t>::max()};
+ };
+ static const std::vector<UnsignedIntegerConfigurationItems> unsignedIntegerConfigItems{
+ {"setCacheCleaningDelay", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) { config.d_cacheCleaningDelay = newValue; }, std::numeric_limits<uint32_t>::max()},
+ {"setCacheCleaningPercentage", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) { config.d_cacheCleaningPercentage = newValue; }, 100U},
+ {"setOutgoingTLSSessionsCacheMaxTicketsPerBackend", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) { config.d_tlsSessionCacheMaxSessionsPerBackend = newValue; }, std::numeric_limits<uint16_t>::max() },
+ {"setOutgoingTLSSessionsCacheCleanupDelay", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) { config.d_tlsSessionCacheCleanupDelay = newValue; }, std::numeric_limits<uint16_t>::max() },
+ {"setOutgoingTLSSessionsCacheMaxTicketValidity", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) { config.d_tlsSessionCacheSessionValidity = newValue; }, std::numeric_limits<uint16_t>::max() },
+ {"setECSSourcePrefixV4", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) { config.d_ECSSourcePrefixV4 = newValue; }, std::numeric_limits<uint16_t>::max()},
+ {"setECSSourcePrefixV6", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) { config.d_ECSSourcePrefixV6 = newValue; }, std::numeric_limits<uint16_t>::max()},
+ {"setTCPRecvTimeout", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) { config.d_tcpRecvTimeout = newValue; }, std::numeric_limits<uint16_t>::max()},
+ {"setTCPSendTimeout", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) { config.d_tcpSendTimeout = newValue; }, std::numeric_limits<uint16_t>::max()},
+ {"setMaxTCPQueriesPerConnection", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) { config.d_maxTCPQueriesPerConn = newValue; }, std::numeric_limits<uint64_t>::max()},
+ {"setMaxTCPConnectionDuration", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) { config.d_maxTCPConnectionDuration = newValue; }, std::numeric_limits<uint32_t>::max()},
+ {"setStaleCacheEntriesTTL", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) { config.d_staleCacheEntriesTTL = newValue; }, std::numeric_limits<uint32_t>::max()},
+ {"setConsoleOutputMaxMsgSize", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) { config.d_consoleOutputMsgMaxSize = newValue; }, std::numeric_limits<uint32_t>::max()},
+#ifndef DISABLE_SECPOLL
+ {"setSecurityPollInterval", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) { config.d_secPollInterval = newValue; }, std::numeric_limits<uint32_t>::max()},
+#endif /* DISABLE_SECPOLL */
+ {"setProxyProtocolMaximumPayloadSize", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) { config.d_proxyProtocolMaximumSize = std::max(static_cast<uint64_t>(16), newValue); }, std::numeric_limits<uint32_t>::max()},
+ {"setPayloadSizeOnSelfGeneratedAnswers", [](dnsdist::configuration::RuntimeConfiguration& config, uint64_t newValue) {
+ if (newValue < 512) {
+ warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!");
+ g_outputBuffer = "setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!";
+ newValue = 512;
+ }
+ if (newValue > dnsdist::configuration::s_udpIncomingBufferSize) {
+ warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to %d instead!", dnsdist::configuration::s_udpIncomingBufferSize);
+ g_outputBuffer = "setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to " + std::to_string(dnsdist::configuration::s_udpIncomingBufferSize) + " instead";
+ newValue = dnsdist::configuration::s_udpIncomingBufferSize;
+ }
+ config.d_payloadSizeSelfGenAnswers = newValue;
+ },
+ std::numeric_limits<uint64_t>::max()},
+ };
+
+ struct StringConfigurationItems
+ {
+ const std::string name;
+ const std::function<void(dnsdist::configuration::RuntimeConfiguration& config, const std::string& value)> mutator;
+ };
+ static const std::vector<StringConfigurationItems> stringConfigItems{
+#ifndef DISABLE_SECPOLL
+ {"setSecurityPollSuffix", [](dnsdist::configuration::RuntimeConfiguration& config, const std::string& newValue) { config.d_secPollSuffix = newValue; }},
+#endif /* DISABLE_SECPOLL */
+ };
+
+ for (const auto& item : booleanConfigItems) {
+ luaCtx.writeFunction(item.name, [&item](bool value) {
+ setLuaSideEffect();
+ dnsdist::configuration::updateRuntimeConfiguration([value, &item](dnsdist::configuration::RuntimeConfiguration& config) {
+ item.mutator(config, value);
+ });
+ });
+ }
+
+ for (const auto& item : unsignedIntegerConfigItems) {
+ luaCtx.writeFunction(item.name, [&item](uint64_t value) {
+ setLuaSideEffect();
+ checkParameterBound(item.name, value, item.maximumValue);
+ dnsdist::configuration::updateRuntimeConfiguration([value, &item](dnsdist::configuration::RuntimeConfiguration& config) {
+ item.mutator(config, value);
+ });
+ });
+ }
+
+ for (const auto& item : stringConfigItems) {
+ luaCtx.writeFunction(item.name, [&item](const std::string& value) {
+ setLuaSideEffect();
+ dnsdist::configuration::updateRuntimeConfiguration([value, &item](dnsdist::configuration::RuntimeConfiguration& config) {
+ item.mutator(config, value);
+ });
+ });
+ }
+
+ struct BooleanImmutableConfigurationItems
+ {
+ const std::string name;
+ const std::function<void(dnsdist::configuration::Configuration& config, bool newValue)> mutator;
+ };
+ static const std::vector<BooleanImmutableConfigurationItems> booleanImmutableConfigItems{
+ {"setRandomizedOutgoingSockets", [](dnsdist::configuration::Configuration& config, bool newValue) { config.d_randomizeUDPSocketsToBackend = newValue; }},
+ {"setRandomizedIdsOverUDP", [](dnsdist::configuration::Configuration& config, bool newValue) { config.d_randomizeIDsToBackend = newValue; }},
+ };
+ struct UnsignedIntegerImmutableConfigurationItems
+ {
+ const std::string name;
+ const std::function<void(dnsdist::configuration::Configuration& config, uint64_t value)> mutator;
+ const size_t maximumValue{std::numeric_limits<uint64_t>::max()};
+ };
+ static const std::vector<UnsignedIntegerImmutableConfigurationItems> unsignedIntegerImmutableConfigItems{
+ {"setMaxTCPQueuedConnections", [](dnsdist::configuration::Configuration& config, uint64_t newValue) { config.d_maxTCPQueuedConnections = newValue; }, std::numeric_limits<uint16_t>::max()},
+ {"setMaxTCPClientThreads", [](dnsdist::configuration::Configuration& config, uint64_t newValue) { config.d_maxTCPClientThreads = newValue; }, std::numeric_limits<uint16_t>::max()},
+ {"setMaxTCPConnectionsPerClient", [](dnsdist::configuration::Configuration& config, uint64_t newValue) { config.d_maxTCPConnectionsPerClient = newValue; }, std::numeric_limits<uint64_t>::max()},
+ {"setTCPInternalPipeBufferSize", [](dnsdist::configuration::Configuration& config, uint64_t newValue) { config.d_tcpInternalPipeBufferSize = newValue; }, std::numeric_limits<uint64_t>::max()},
+ {"setMaxUDPOutstanding", [](dnsdist::configuration::Configuration& config, uint64_t newValue) { config.d_maxUDPOutstanding = newValue; }, std::numeric_limits<uint16_t>::max()},
+ {"setWHashedPertubation", [](dnsdist::configuration::Configuration& config, uint64_t newValue) { config.d_hashPerturbation = newValue; }, std::numeric_limits<uint32_t>::max()},
+#ifndef DISABLE_RECVMMSG
+ {"setUDPMultipleMessagesVectorSize", [](dnsdist::configuration::Configuration& config, uint64_t newValue) { config.d_udpVectorSize = newValue; }, std::numeric_limits<uint32_t>::max()},
+#endif /* DISABLE_RECVMMSG */
+ {"setUDPTimeout", [](dnsdist::configuration::Configuration& config, uint64_t newValue) { config.d_udpTimeout = newValue; }, std::numeric_limits<uint8_t>::max()},
+ };
+ struct DoubleImmutableConfigurationItems
+ {
+ const std::string name;
+ const std::function<void(dnsdist::configuration::Configuration& config, double value)> mutator;
+ const double maximumValue{1.0};
+ };
+ static const std::vector<DoubleImmutableConfigurationItems> doubleImmutableConfigItems{
+ {"setConsistentHashingBalancingFactor", [](dnsdist::configuration::Configuration& config, double newValue) { config.d_consistentHashBalancingFactor = newValue; }, 1.0},
+ {"setWeightedBalancingFactor", [](dnsdist::configuration::Configuration& config, double newValue) { config.d_weightedBalancingFactor = newValue; }, 1.0},
+ };
+
+ for (const auto& item : booleanConfigItems) {
+ luaCtx.writeFunction(item.name, [&item](bool value) {
+ try {
+ dnsdist::configuration::updateRuntimeConfiguration([value, &item](dnsdist::configuration::RuntimeConfiguration& config) {
+ item.mutator(config, value);
+ });
+ }
+ catch (const std::exception& exp) {
+ g_outputBuffer = item.name + " cannot be used at runtime!\n";
+ errlog("%s cannot be used at runtime!", item.name);
+ }
+ });
+ }
+
+ for (const auto& item : unsignedIntegerImmutableConfigItems) {
+ luaCtx.writeFunction(item.name, [&item](uint64_t value) {
+ checkParameterBound(item.name, value, item.maximumValue);
+ try {
+ dnsdist::configuration::updateImmutableConfiguration([value, &item](dnsdist::configuration::Configuration& config) {
+ item.mutator(config, value);
+ });
+ }
+ catch (const std::exception& exp) {
+ g_outputBuffer = item.name + " cannot be used at runtime!\n";
+ errlog("%s cannot be used at runtime!", item.name);
+ }
+ });
+ }
+ for (const auto& item : doubleImmutableConfigItems) {
+ luaCtx.writeFunction(item.name, [&item](double value) {
+ if (value > item.maximumValue) {
+ g_outputBuffer = "Invalid value passed to " + item.name + "()!\n";
+ errlog("Invalid value passed to %s()!", item.name);
+ return;
+ }
+
+ try {
+ dnsdist::configuration::updateImmutableConfiguration([value, &item](dnsdist::configuration::Configuration& config) {
+ item.mutator(config, value);
+ });
+ }
+ catch (const std::exception& exp) {
+ g_outputBuffer = item.name + " cannot be used at runtime!\n";
+ errlog("%s cannot be used at runtime!", item.name);
+ }
+ setLuaSideEffect();
+ });
+ }
+
+ luaCtx.writeFunction("getVerbose", []() { return dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose; });
luaCtx.writeFunction("addACL", [](const std::string& domain) {
setLuaSideEffect();
return;
}
- g_consoleEnabled = true;
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_consoleEnabled = true;
+ });
#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO)
- if (g_configurationDone && g_consoleKey.empty()) {
+ if (dnsdist::configuration::isConfigurationDone() && dnsdist::configuration::getImmutableConfiguration().d_consoleKey.empty()) {
warnlog("Warning, the console has been enabled via 'controlSocket()' but no key has been set with 'setKey()' so all connections will fail until a key has been set");
}
#endif
warnlog("Allowing remote access to the console while neither libsodium not libcrypto support has been enabled is not secure, and will result in cleartext communications");
#endif
- g_consoleACL.modify([netmask](NetmaskGroup& nmg) { nmg.addMask(netmask); });
+ dnsdist::configuration::updateRuntimeConfiguration([&netmask](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_consoleACL.addMask(netmask);
+ });
});
luaCtx.writeFunction("setConsoleACL", [](LuaTypeOrArrayOf<std::string> inp) {
nmg.addMask(entry.second);
}
}
- g_consoleACL.setState(nmg);
+ dnsdist::configuration::updateRuntimeConfiguration([&nmg](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_consoleACL = std::move(nmg);
+ });
});
luaCtx.writeFunction("showConsoleACL", []() {
warnlog("Allowing remote access to the console while neither libsodium nor libcrypto support has not been enabled is not secure, and will result in cleartext communications");
#endif
- auto aclEntries = g_consoleACL.getLocal()->toStringVector();
+ auto aclEntries = dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleACL.toStringVector();
for (const auto& entry : aclEntries) {
g_outputBuffer += entry + "\n";
luaCtx.writeFunction("clearQueryCounters", []() {
unsigned int size{0};
{
- auto records = g_qcount.records.write_lock();
+ auto records = dnsdist::QueryCount::g_queryCountRecords.write_lock();
size = records->size();
records->clear();
}
luaCtx.writeFunction("getQueryCounters", [](boost::optional<uint64_t> optMax) {
setLuaNoSideEffect();
- auto records = g_qcount.records.read_lock();
+ auto records = dnsdist::QueryCount::g_queryCountRecords.read_lock();
g_outputBuffer = "query counting is currently: ";
- g_outputBuffer += g_qcount.enabled ? "enabled" : "disabled";
+ g_outputBuffer += dnsdist::configuration::getCurrentRuntimeConfiguration().d_queryCountConfig.d_enabled ? "enabled" : "disabled";
g_outputBuffer += (boost::format(" (%d records in buffer)\n") % records->size()).str();
boost::format fmt("%-3d %s: %d request(s)\n");
}
});
- luaCtx.writeFunction("setQueryCount", [](bool enabled) { g_qcount.enabled = enabled; });
-
- luaCtx.writeFunction("setQueryCountFilter", [](QueryCountFilter func) {
- g_qcount.filter = std::move(func);
+ luaCtx.writeFunction("setQueryCountFilter", [](dnsdist::QueryCount::Configuration::Filter func) {
+ dnsdist::configuration::updateRuntimeConfiguration([&func](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_queryCountConfig.d_filter = std::move(func);
+ });
});
luaCtx.writeFunction("makeKey", []() {
});
luaCtx.writeFunction("setKey", [](const std::string& key) {
- if (!g_configurationDone && !g_consoleKey.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
+ if (!dnsdist::configuration::isConfigurationDone() && !dnsdist::configuration::getImmutableConfiguration().d_consoleKey.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
return; // but later setKeys() trump the -k value again
}
#if !defined(HAVE_LIBSODIUM) && !defined(HAVE_LIBCRYPTO)
#endif
setLuaSideEffect();
- string newkey;
- if (B64Decode(key, newkey) < 0) {
+ string newKey;
+ if (B64Decode(key, newKey) < 0) {
g_outputBuffer = string("Unable to decode ") + key + " as Base64";
errlog("%s", g_outputBuffer);
+ return;
}
- else {
- g_consoleKey = std::move(newkey);
- }
+
+ dnsdist::configuration::updateImmutableConfiguration([&newKey](dnsdist::configuration::Configuration& config) {
+ config.d_consoleKey = std::move(newKey);
+ });
});
luaCtx.writeFunction("clearConsoleHistory", []() {
setLuaNoSideEffect();
#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO)
try {
+ const auto& consoleKey = dnsdist::configuration::getImmutableConfiguration().d_consoleKey;
string testmsg;
if (optTestMsg) {
dnsdist::crypto::authenticated::Nonce nonce2;
nonce1.init();
nonce2 = nonce1;
- string encrypted = dnsdist::crypto::authenticated::encryptSym(testmsg, g_consoleKey, nonce1);
- string decrypted = dnsdist::crypto::authenticated::decryptSym(encrypted, g_consoleKey, nonce2);
+ string encrypted = dnsdist::crypto::authenticated::encryptSym(testmsg, consoleKey, nonce1);
+ string decrypted = dnsdist::crypto::authenticated::decryptSym(encrypted, consoleKey, nonce2);
nonce1.increment();
nonce2.increment();
- encrypted = dnsdist::crypto::authenticated::encryptSym(testmsg, g_consoleKey, nonce1);
- decrypted = dnsdist::crypto::authenticated::decryptSym(encrypted, g_consoleKey, nonce2);
+ encrypted = dnsdist::crypto::authenticated::encryptSym(testmsg, consoleKey, nonce1);
+ decrypted = dnsdist::crypto::authenticated::decryptSym(encrypted, consoleKey, nonce2);
if (testmsg == decrypted) {
g_outputBuffer = "Everything is ok!\n";
#endif
});
- luaCtx.writeFunction("setTCPRecvTimeout", [](int timeout) { g_tcpRecvTimeout = timeout; });
-
- luaCtx.writeFunction("setTCPSendTimeout", [](int timeout) { g_tcpSendTimeout = timeout; });
-
- luaCtx.writeFunction("setUDPTimeout", [](int timeout) { DownstreamState::s_udpTimeout = timeout; });
-
- luaCtx.writeFunction("setMaxUDPOutstanding", [](uint64_t max) {
- if (!checkConfigurationTime("setMaxUDPOutstanding")) {
- return;
- }
-
- checkParameterBound("setMaxUDPOutstanding", max);
- g_maxOutstanding = max;
- });
-
- luaCtx.writeFunction("setMaxTCPClientThreads", [](uint64_t max) {
- if (!checkConfigurationTime("setMaxTCPClientThreads")) {
- return;
- }
- g_maxTCPClientThreads = max;
- });
-
- luaCtx.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max) {
- if (!checkConfigurationTime("setMaxTCPQueuedConnections")) {
- return;
- }
- g_maxTCPQueuedConnections = max;
- });
-
- luaCtx.writeFunction("setMaxTCPQueriesPerConnection", [](uint64_t max) {
- if (!checkConfigurationTime("setMaxTCPQueriesPerConnection")) {
- return;
- }
- g_maxTCPQueriesPerConn = max;
- });
-
- luaCtx.writeFunction("setMaxTCPConnectionsPerClient", [](uint64_t max) {
- if (!checkConfigurationTime("setMaxTCPConnectionsPerClient")) {
- return;
- }
- dnsdist::IncomingConcurrentTCPConnectionsManager::setMaxTCPConnectionsPerClient(max);
- });
-
- luaCtx.writeFunction("setMaxTCPConnectionDuration", [](uint64_t max) {
- if (!checkConfigurationTime("setMaxTCPConnectionDuration")) {
- return;
- }
- g_maxTCPConnectionDuration = max;
- });
-
luaCtx.writeFunction("setMaxCachedTCPConnectionsPerDownstream", [](uint64_t max) {
setTCPDownstreamMaxIdleConnectionsPerBackend(max);
});
});
#endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
- luaCtx.writeFunction("setOutgoingTLSSessionsCacheMaxTicketsPerBackend", [](uint64_t max) {
- if (!checkConfigurationTime("setOutgoingTLSSessionsCacheMaxTicketsPerBackend")) {
- return;
- }
- TLSSessionCache::setMaxTicketsPerBackend(max);
- });
-
- luaCtx.writeFunction("setOutgoingTLSSessionsCacheCleanupDelay", [](time_t delay) {
- if (!checkConfigurationTime("setOutgoingTLSSessionsCacheCleanupDelay")) {
- return;
- }
- TLSSessionCache::setCleanupDelay(delay);
- });
-
- luaCtx.writeFunction("setOutgoingTLSSessionsCacheMaxTicketValidity", [](time_t validity) {
- if (!checkConfigurationTime("setOutgoingTLSSessionsCacheMaxTicketValidity")) {
- return;
- }
- TLSSessionCache::setSessionValidity(validity);
- });
-
luaCtx.writeFunction("getOutgoingTLSSessionCacheSize", []() {
setLuaNoSideEffect();
return g_sessionCache.getSize();
});
- luaCtx.writeFunction("setCacheCleaningDelay", [](uint64_t delay) {
- checkParameterBound("setCacheCleaningDelay", delay, std::numeric_limits<uint32_t>::max());
- g_cacheCleaningDelay = delay;
- });
-
- luaCtx.writeFunction("setCacheCleaningPercentage", [](uint64_t percentage) {
- if (percentage < 100) {
- g_cacheCleaningPercentage = percentage;
- }
- else {
- g_cacheCleaningPercentage = 100;
- }
- });
-
- luaCtx.writeFunction("setECSSourcePrefixV4", [](uint64_t prefix) {
- checkParameterBound("setECSSourcePrefixV4", prefix, std::numeric_limits<uint16_t>::max());
- g_ECSSourcePrefixV4 = prefix;
- });
-
- luaCtx.writeFunction("setECSSourcePrefixV6", [](uint64_t prefix) {
- checkParameterBound("setECSSourcePrefixV6", prefix, std::numeric_limits<uint16_t>::max());
- g_ECSSourcePrefixV6 = prefix;
- });
-
- luaCtx.writeFunction("setECSOverride", [](bool override) { g_ECSOverride = override; });
-
#ifndef DISABLE_DYNBLOCKS
luaCtx.writeFunction("showDynBlocks", []() {
setLuaNoSideEffect();
+ const auto runtimeConf = dnsdist::configuration::getCurrentRuntimeConfiguration();
auto slow = g_dynblockNMG.getCopy();
timespec now{};
gettime(&now);
if (g_defaultBPFFilter && entry.second.bpf) {
counter += g_defaultBPFFilter->getHits(entry.first.getNetwork());
}
- g_outputBuffer += (fmt % entry.first.toString() % (entry.second.until.tv_sec - now.tv_sec) % counter % (entry.second.warning ? "true" : "false") % DNSAction::typeToString(entry.second.action != DNSAction::Action::None ? entry.second.action : g_dynBlockAction) % (g_defaultBPFFilter && entry.second.bpf ? "*" : "") % entry.second.reason).str();
+ g_outputBuffer += (fmt % entry.first.toString() % (entry.second.until.tv_sec - now.tv_sec) % counter % (entry.second.warning ? "true" : "false") % DNSAction::typeToString(entry.second.action != DNSAction::Action::None ? entry.second.action : runtimeConf.d_dynBlockAction) % (g_defaultBPFFilter && entry.second.bpf ? "*" : "") % entry.second.reason).str();
}
}
auto slow2 = g_dynblockSMT.getCopy();
- slow2.visit([&now, &fmt](const SuffixMatchTree<DynBlock>& node) {
+ slow2.visit([&now, &fmt, &runtimeConf](const SuffixMatchTree<DynBlock>& node) {
if (now < node.d_value.until) {
string dom("empty");
if (!node.d_value.domain.empty()) {
dom = node.d_value.domain.toString();
}
- g_outputBuffer += (fmt % dom % (node.d_value.until.tv_sec - now.tv_sec) % node.d_value.blocks % (node.d_value.warning ? "true" : "false") % DNSAction::typeToString(node.d_value.action != DNSAction::Action::None ? node.d_value.action : g_dynBlockAction) % "" % node.d_value.reason).str();
+ g_outputBuffer += (fmt % dom % (node.d_value.until.tv_sec - now.tv_sec) % node.d_value.blocks % (node.d_value.warning ? "true" : "false") % DNSAction::typeToString(node.d_value.action != DNSAction::Action::None ? node.d_value.action : runtimeConf.d_dynBlockAction) % "" % node.d_value.reason).str();
}
});
});
gettime(&now);
LuaAssociativeTable<DynBlock> entries;
+ const auto defaultAction = dnsdist::configuration::getCurrentRuntimeConfiguration().d_dynBlockAction;
auto fullCopy = g_dynblockNMG.getCopy();
for (const auto& blockPair : fullCopy) {
const auto& requestor = blockPair.first;
entry.blocks += g_defaultBPFFilter->getHits(requestor.getNetwork());
}
if (entry.action == DNSAction::Action::None) {
- entry.action = g_dynBlockAction;
+ entry.action = defaultAction;
}
entries.emplace(requestor.toString(), std::move(entry));
}
gettime(&now);
LuaAssociativeTable<DynBlock> entries;
+ const auto defaultAction = dnsdist::configuration::getCurrentRuntimeConfiguration().d_dynBlockAction;
auto fullCopy = g_dynblockSMT.getCopy();
- fullCopy.visit([&now, &entries](const SuffixMatchTree<DynBlock>& node) {
+ fullCopy.visit([&now, &entries, defaultAction](const SuffixMatchTree<DynBlock>& node) {
if (!(now < node.d_value.until)) {
return;
}
key = entry.domain.toString();
}
if (entry.action == DNSAction::Action::None) {
- entry.action = g_dynBlockAction;
+ entry.action = defaultAction;
}
entries.emplace(std::move(key), std::move(entry));
});
});
luaCtx.writeFunction("setDynBlocksAction", [](DNSAction::Action action) {
- if (!checkConfigurationTime("setDynBlocksAction")) {
- return;
- }
if (action == DNSAction::Action::Drop || action == DNSAction::Action::NoOp || action == DNSAction::Action::Nxdomain || action == DNSAction::Action::Refused || action == DNSAction::Action::Truncate || action == DNSAction::Action::NoRecurse) {
- g_dynBlockAction = action;
+ dnsdist::configuration::updateRuntimeConfiguration([action](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_dynBlockAction = action;
+ });
}
else {
errlog("Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!");
return pool;
});
- luaCtx.writeFunction("setVerbose", [](bool verbose) { g_verbose = verbose; });
- luaCtx.writeFunction("getVerbose", []() { return g_verbose; });
- luaCtx.writeFunction("setVerboseHealthChecks", [](bool verbose) { g_verboseHealthChecks = verbose; });
luaCtx.writeFunction("setVerboseLogDestination", [](const std::string& dest) {
if (!checkConfigurationTime("setVerboseLogDestination")) {
return;
dnsdist::logging::LoggingConfiguration::setStructuredLogging(enable, levelPrefix);
});
- luaCtx.writeFunction("setStaleCacheEntriesTTL", [](uint64_t ttl) {
- checkParameterBound("setStaleCacheEntriesTTL", ttl, std::numeric_limits<uint32_t>::max());
- g_staleCacheEntriesTTL = ttl;
- });
-
luaCtx.writeFunction("showBinds", []() {
setLuaNoSideEffect();
try {
if (!checkConfigurationTime("includeDirectory")) {
return;
}
- if (g_included) {
+ static bool s_included{false};
+
+ if (s_included) {
errlog("includeDirectory() cannot be used recursively!");
g_outputBuffer = "includeDirectory() cannot be used recursively!\n";
return;
std::sort(files.begin(), files.end());
- g_included = true;
+ s_included = true;
for (const auto& file : files) {
std::ifstream ifs(file);
luaCtx.executeCode(ifs);
}
catch (...) {
- g_included = false;
+ s_included = false;
throw;
}
luaCtx.executeCode(ifs);
}
- g_included = false;
+ s_included = false;
});
luaCtx.writeFunction("setAPIWritable", [](bool writable, boost::optional<std::string> apiConfigDir) {
- setLuaSideEffect();
- g_apiReadWrite = writable;
- if (apiConfigDir) {
- if (!(*apiConfigDir).empty()) {
- g_apiConfigDirectory = *apiConfigDir;
- }
- else {
- errlog("The API configuration directory value cannot be empty!");
- g_outputBuffer = "The API configuration directory value cannot be empty!";
- }
- }
- });
-
- luaCtx.writeFunction("setServFailWhenNoServer", [](bool servfail) {
- setLuaSideEffect();
- g_servFailOnNoPolicy = servfail;
- });
-
- luaCtx.writeFunction("setRoundRobinFailOnNoServer", [](bool fail) {
- setLuaSideEffect();
- g_roundrobinFailOnNoServer = fail;
- });
-
- luaCtx.writeFunction("setConsistentHashingBalancingFactor", [](double factor) {
- setLuaSideEffect();
- if (factor >= 1.0) {
- g_consistentHashBalancingFactor = factor;
- }
- else {
- errlog("Invalid value passed to setConsistentHashingBalancingFactor()!");
- g_outputBuffer = "Invalid value passed to setConsistentHashingBalancingFactor()!\n";
+ if (apiConfigDir && (*apiConfigDir).empty()) {
+ errlog("The API configuration directory value cannot be empty!");
+ g_outputBuffer = "The API configuration directory value cannot be empty!";
return;
}
- });
-
- luaCtx.writeFunction("setWeightedBalancingFactor", [](double factor) {
+ dnsdist::configuration::updateRuntimeConfiguration([writable, &apiConfigDir](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_apiReadWrite = writable;
+ if (!(*apiConfigDir).empty()) {
+ config.d_apiConfigDirectory = *apiConfigDir;
+ }
+ });
setLuaSideEffect();
- if (factor >= 1.0) {
- g_weightedBalancingFactor = factor;
- }
- else {
- errlog("Invalid value passed to setWeightedBalancingFactor()!");
- g_outputBuffer = "Invalid value passed to setWeightedBalancingFactor()!\n";
- return;
- }
});
luaCtx.writeFunction("setRingBuffersSize", [client](uint64_t capacity, boost::optional<uint64_t> numberOfShards) {
}
});
- luaCtx.writeFunction("setWHashedPertubation", [](uint64_t perturb) {
- setLuaSideEffect();
- checkParameterBound("setWHashedPertubation", perturb, std::numeric_limits<uint32_t>::max());
- g_hashperturb = perturb;
- });
-
- luaCtx.writeFunction("setTCPInternalPipeBufferSize", [](uint64_t size) { g_tcpInternalPipeBufferSize = size; });
luaCtx.writeFunction("setTCPFastOpenKey", [](const std::string& keyString) {
setLuaSideEffect();
std::array<uint32_t, 4> key{};
if (!checkConfigurationTime("snmpAgent")) {
return;
}
- if (g_snmpEnabled) {
- errlog("snmpAgent() cannot be used twice!");
- g_outputBuffer = "snmpAgent() cannot be used twice!\n";
- return;
+
+ {
+ const auto runtimeConfiguration = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ if (runtimeConfiguration.d_snmpEnabled) {
+ errlog("snmpAgent() cannot be used twice!");
+ g_outputBuffer = "snmpAgent() cannot be used twice!\n";
+ return;
+ }
}
- g_snmpEnabled = true;
- g_snmpTrapsEnabled = enableTraps;
+ dnsdist::configuration::updateRuntimeConfiguration([enableTraps](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_snmpEnabled = true;
+ config.d_snmpTrapsEnabled = enableTraps;
+ });
+
g_snmpAgent = std::make_unique<DNSDistSNMPAgent>("dnsdist", daemonSocket ? *daemonSocket : std::string());
});
luaCtx.writeFunction("sendCustomTrap", [](const std::string& str) {
- if (g_snmpAgent != nullptr && g_snmpTrapsEnabled) {
+ const auto runtimeConfiguration = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ if (g_snmpAgent != nullptr && runtimeConfiguration.d_snmpTrapsEnabled) {
g_snmpAgent->sendCustomTrap(str);
}
});
});
#endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
- luaCtx.writeFunction("setConsoleConnectionsLogging", [](bool enabled) {
- g_logConsoleConnections = enabled;
- });
-
- luaCtx.writeFunction("setConsoleOutputMaxMsgSize", [](uint64_t size) {
- checkParameterBound("setConsoleOutputMaxMsgSize", size, std::numeric_limits<uint32_t>::max());
- g_consoleOutputMsgMaxSize = size;
- });
-
luaCtx.writeFunction("setProxyProtocolACL", [](LuaTypeOrArrayOf<std::string> inp) {
if (!checkConfigurationTime("setProxyProtocolACL")) {
return;
nmg.addMask(entry.second);
}
}
- g_proxyProtocolACL = std::move(nmg);
- });
-
- luaCtx.writeFunction("setProxyProtocolApplyACLToProxiedClients", [](bool apply) {
- if (!checkConfigurationTime("setProxyProtocolApplyACLToProxiedClients")) {
- return;
- }
- setLuaSideEffect();
- g_applyACLToProxiedClients = apply;
- });
-
- luaCtx.writeFunction("setProxyProtocolMaximumPayloadSize", [](uint64_t size) {
- if (!checkConfigurationTime("setProxyProtocolMaximumPayloadSize")) {
- return;
- }
- setLuaSideEffect();
- g_proxyProtocolMaximumSize = std::max(static_cast<uint64_t>(16), size);
- });
-
-#ifndef DISABLE_RECVMMSG
- luaCtx.writeFunction("setUDPMultipleMessagesVectorSize", [](uint64_t vSize) {
- if (!checkConfigurationTime("setUDPMultipleMessagesVectorSize")) {
- return;
- }
-#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
- setLuaSideEffect();
- g_udpVectorSize = vSize;
-#else
- errlog("recvmmsg() support is not available!");
- g_outputBuffer = "recvmmsg support is not available!\n";
-#endif
- });
-#endif /* DISABLE_RECVMMSG */
-
- luaCtx.writeFunction("setAddEDNSToSelfGeneratedResponses", [](bool add) {
- g_addEDNSToSelfGeneratedResponses = add;
- });
-
- luaCtx.writeFunction("setPayloadSizeOnSelfGeneratedAnswers", [](uint64_t payloadSize) {
- if (payloadSize < 512) {
- warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!");
- g_outputBuffer = "setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!";
- payloadSize = 512;
- }
- if (payloadSize > s_udpIncomingBufferSize) {
- warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to %d instead!", s_udpIncomingBufferSize);
- g_outputBuffer = "setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to " + std::to_string(s_udpIncomingBufferSize) + " instead";
- payloadSize = s_udpIncomingBufferSize;
- }
- g_PayloadSizeSelfGenAnswers = payloadSize;
+ dnsdist::configuration::updateRuntimeConfiguration([&nmg](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_proxyProtocolACL = std::move(nmg);
+ });
});
#ifndef DISABLE_SECPOLL
setLuaNoSideEffect();
g_outputBuffer = std::to_string(dnsdist::metrics::g_stats.securityStatus) + "\n";
});
-
- luaCtx.writeFunction("setSecurityPollSuffix", [](const std::string& suffix) {
- if (!checkConfigurationTime("setSecurityPollSuffix")) {
- return;
- }
- g_secPollSuffix = suffix;
- });
-
- luaCtx.writeFunction("setSecurityPollInterval", [](time_t newInterval) {
- if (newInterval <= 0) {
- warnlog("setSecurityPollInterval() should be > 0, skipping");
- g_outputBuffer = "setSecurityPollInterval() should be > 0, skipping";
- }
-
- g_secPollInterval = newInterval;
- });
#endif /* DISABLE_SECPOLL */
luaCtx.writeFunction("setSyslogFacility", [](boost::variant<int, std::string> facility) {
}
});
- luaCtx.writeFunction("setAllowEmptyResponse", [](bool allow) { g_allowEmptyResponse = allow; });
- luaCtx.writeFunction("setDropEmptyQueries", [](bool drop) { extern bool g_dropEmptyQueries; g_dropEmptyQueries = drop; });
-
#if defined(HAVE_LIBSSL) && defined(HAVE_OCSP_BASIC_SIGN) && !defined(DISABLE_OCSP_STAPLING)
luaCtx.writeFunction("generateOCSPResponse", [client](const std::string& certFile, const std::string& caCert, const std::string& caKey, const std::string& outFile, int ndays, int nmin) {
if (client) {
if (client) {
return;
}
- if (!checkConfigurationTime("setUDPSocketBufferSizes")) {
- return;
- }
checkParameterBound("setUDPSocketBufferSizes", recv, std::numeric_limits<uint32_t>::max());
checkParameterBound("setUDPSocketBufferSizes", snd, std::numeric_limits<uint32_t>::max());
- setLuaSideEffect();
-
- g_socketUDPSendBuffer = snd;
- g_socketUDPRecvBuffer = recv;
- });
- luaCtx.writeFunction("setRandomizedOutgoingSockets", [](bool randomized) {
- DownstreamState::s_randomizeSockets = randomized;
- });
-
- luaCtx.writeFunction("setRandomizedIdsOverUDP", [](bool randomized) {
- DownstreamState::s_randomizeIDs = randomized;
+ try {
+ dnsdist::configuration::updateImmutableConfiguration([snd, recv](dnsdist::configuration::Configuration& config) {
+ config.d_socketUDPSendBuffer = snd;
+ config.d_socketUDPRecvBuffer = recv;
+ });
+ setLuaSideEffect();
+ }
+ catch (const std::exception& exp) {
+ g_outputBuffer = "setUDPSocketBufferSizes cannot be used at runtime!\n";
+ errlog("setUDPSocketBufferSizes cannot be used at runtime!");
+ }
});
#if defined(HAVE_LIBSSL) && !defined(HAVE_TLS_PROVIDERS)
gettimeofday(&now, nullptr);
try {
- if (maxConnectionDurationReached(g_maxTCPConnectionDuration, now)) {
+ const auto& currentConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ if (maxConnectionDurationReached(currentConfig.d_maxTCPConnectionDuration, now)) {
vinfolog("Terminating DoH connection from %s because it reached the maximum TCP connection duration", d_ci.remote.toStringWithPort());
stopIO();
d_connectionClosing = true;
boost::optional<struct timeval> IncomingHTTP2Connection::getIdleClientReadTTD(struct timeval now) const
{
+ const auto& currentConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
auto idleTimeout = d_ci.cs->dohFrontend->d_idleTimeout;
- if (g_maxTCPConnectionDuration == 0 && idleTimeout == 0) {
+ if (currentConfig.d_maxTCPConnectionDuration == 0 && idleTimeout == 0) {
return boost::none;
}
- if (g_maxTCPConnectionDuration > 0) {
+ if (currentConfig.d_maxTCPConnectionDuration > 0) {
auto elapsed = now.tv_sec - d_connectionStartTime.tv_sec;
- if (elapsed < 0 || (static_cast<size_t>(elapsed) >= g_maxTCPConnectionDuration)) {
+ if (elapsed < 0 || (static_cast<size_t>(elapsed) >= currentConfig.d_maxTCPConnectionDuration)) {
return now;
}
- auto remaining = g_maxTCPConnectionDuration - elapsed;
+ auto remaining = currentConfig.d_maxTCPConnectionDuration - elapsed;
if (idleTimeout == 0 || remaining <= static_cast<size_t>(idleTimeout)) {
now.tv_sec += static_cast<time_t>(remaining);
return now;
{
#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
try {
- auto [sender, receiver] = pdns::channel::createObjectQueue<CrossProtocolQuery>(pdns::channel::SenderBlockingMode::SenderNonBlocking, pdns::channel::ReceiverBlockingMode::ReceiverNonBlocking, g_tcpInternalPipeBufferSize);
+ const auto internalPipeBufferSize = dnsdist::configuration::getImmutableConfiguration().d_tcpInternalPipeBufferSize;
+ auto [sender, receiver] = pdns::channel::createObjectQueue<CrossProtocolQuery>(pdns::channel::SenderBlockingMode::SenderNonBlocking, pdns::channel::ReceiverBlockingMode::ReceiverNonBlocking, internalPipeBufferSize);
vinfolog("Adding DoH Client thread");
std::lock_guard<std::mutex> lock(d_mutex);
*/
#include "dnsdist-proxy-protocol.hh"
+
+#include "dnsdist.hh"
#include "dnsdist-metrics.hh"
#include "dolog.hh"
-NetmaskGroup g_proxyProtocolACL;
-size_t g_proxyProtocolMaximumSize = 512;
-bool g_applyACLToProxiedClients = false;
-
std::string getProxyProtocolPayload(const DNSQuestion& dq)
{
return makeProxyHeader(dq.overTCP(), dq.ids.origRemote, dq.ids.origDest, dq.proxyProtocolValues ? *dq.proxyProtocolValues : std::vector<ProxyProtocolValue>());
bool expectProxyProtocolFrom(const ComboAddress& remote)
{
- return g_proxyProtocolACL.match(remote);
+ return dnsdist::configuration::getCurrentRuntimeConfiguration().d_proxyProtocolACL.match(remote);
}
bool handleProxyProtocol(const ComboAddress& remote, bool isTCP, const NetmaskGroup& acl, PacketBuffer& query, ComboAddress& realRemote, ComboAddress& realDestination, std::vector<ProxyProtocolValue>& values)
vinfolog("Ignoring invalid proxy protocol (%d, %d) query over %s from %s", query.size(), used, (isTCP ? "TCP" : "UDP"), remote.toStringWithPort());
return false;
}
- else if (static_cast<size_t>(used) > g_proxyProtocolMaximumSize) {
+ if (static_cast<size_t>(used) > dnsdist::configuration::getCurrentRuntimeConfiguration().d_proxyProtocolMaximumSize) {
vinfolog("Proxy protocol header in %s packet from %s is larger than proxy-protocol-maximum-size (%d), dropping", (isTCP ? "TCP" : "UDP"), remote.toStringWithPort(), used);
++dnsdist::metrics::g_stats.proxyProtocolInvalid;
return false;
return false;
}
- if (proxyProto && g_applyACLToProxiedClients) {
+ if (proxyProto && dnsdist::configuration::getCurrentRuntimeConfiguration().d_applyACLToProxiedClients) {
if (!acl.match(realRemote)) {
vinfolog("Query from %s dropped because of ACL", realRemote.toStringWithPort());
++dnsdist::metrics::g_stats.aclDrops;
*/
#pragma once
-#include "dnsdist.hh"
+#include <string>
-extern NetmaskGroup g_proxyProtocolACL;
-extern size_t g_proxyProtocolMaximumSize;
-extern bool g_applyACLToProxiedClients;
+#include "iputils.hh"
+#include "noinitvector.hh"
+#include "proxy-protocol.hh"
+
+struct DNSQuestion;
std::string getProxyProtocolPayload(const DNSQuestion& dq);
--- /dev/null
+/*
+ * 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.
+ */
+
+#include "dnsdist-query-count.hh"
+
+namespace dnsdist::QueryCount
+{
+SharedLockGuarded<Records> g_queryCountRecords;
+}
--- /dev/null
+/*
+ * 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 <functional>
+#include <unordered_map>
+#include <string>
+#include <tuple>
+
+#include "lock.hh"
+
+struct DNSQuestion;
+
+namespace dnsdist::QueryCount
+{
+struct Configuration
+{
+ using Filter = std::function<std::tuple<bool, std::string>(const DNSQuestion* dq)>;
+ Configuration() = default;
+ Filter d_filter;
+ bool d_enabled{false};
+};
+
+using Records = std::unordered_map<std::string, unsigned int>;
+extern SharedLockGuarded<Records> g_queryCountRecords;
+}
static std::string getSecPollStatus(const std::string& queriedName, int timeout=2)
{
+ const auto verbose = dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose;
+
const DNSName sentName(queriedName);
std::vector<uint8_t> packet;
DNSPacketWriter pw(packet, sentName, QType::TXT);
string reply;
int ret = waitForData(sock.getHandle(), timeout, 0);
if (ret < 0) {
- if (g_verbose) {
+ if (verbose) {
warnlog("Error while waiting for the secpoll response from stub resolver %s: %d", dest.toString(), ret);
}
continue;
}
else if (ret == 0) {
- if (g_verbose) {
+ if (verbose) {
warnlog("Timeout while waiting for the secpoll response from stub resolver %s", dest.toString());
}
continue;
sock.read(reply);
}
catch(const std::exception& e) {
- if (g_verbose) {
+ if (verbose) {
warnlog("Error while reading for the secpoll response from stub resolver %s: %s", dest.toString(), e.what());
}
continue;
}
if (reply.size() <= sizeof(struct dnsheader)) {
- if (g_verbose) {
+ if (verbose) {
warnlog("Too short answer of size %d received from the secpoll stub resolver %s", reply.size(), dest.toString());
}
continue;
struct dnsheader d;
memcpy(&d, reply.c_str(), sizeof(d));
if (d.id != pw.getHeader()->id) {
- if (g_verbose) {
+ if (verbose) {
warnlog("Invalid ID (%d / %d) received from the secpoll stub resolver %s", d.id, pw.getHeader()->id, dest.toString());
}
continue;
}
if (d.rcode != RCode::NoError) {
- if (g_verbose) {
+ if (verbose) {
warnlog("Response code '%s' received from the secpoll stub resolver %s for '%s'", RCode::to_s(d.rcode), dest.toString(), queriedName);
}
}
if (ntohs(d.qdcount) != 1 || ntohs(d.ancount) != 1) {
- if (g_verbose) {
+ if (verbose) {
warnlog("Invalid answer (qdcount %d / ancount %d) received from the secpoll stub resolver %s", ntohs(d.qdcount), ntohs(d.ancount), dest.toString());
}
continue;
DNSName receivedName(reply.c_str(), reply.size(), sizeof(dnsheader), false, &receivedType, &receivedClass);
if (receivedName != sentName || receivedType != QType::TXT || receivedClass != QClass::IN) {
- if (g_verbose) {
+ if (verbose) {
warnlog("Invalid answer, either the qname (%s / %s), qtype (%s / %s) or qclass (%s / %s) does not match, received from the secpoll stub resolver %s", receivedName, sentName, QType(receivedType).toString(), QType(QType::TXT).toString(), QClass(receivedClass).toString(), QClass::IN.toString(), dest.toString());
}
continue;
throw std::runtime_error("Unable to get a valid Security Status update");
}
-static bool g_secPollDone{false};
-std::string g_secPollSuffix{"secpoll.powerdns.com."};
-time_t g_secPollInterval{3600};
-
+namespace dnsdist::secpoll
+{
void doSecPoll(const std::string& suffix)
{
+ static bool s_secPollDone{false};
+
if (suffix.empty()) {
return;
}
int securityStatus = std::stoi(split.first);
std::string securityMessage = split.second;
- if (securityStatus == 1 && !g_secPollDone) {
+ if (securityStatus == 1 && !s_secPollDone) {
infolog("Polled security status of version %s at startup, no known issues reported: %s", std::string(VERSION), securityMessage);
}
if (securityStatus == 2) {
}
dnsdist::metrics::g_stats.securityStatus = securityStatus;
- g_secPollDone = true;
+ s_secPollDone = true;
return;
}
catch (const std::exception& e) {
if (releaseVersion) {
warnlog("Error while retrieving the security update for version %s: %s", version, e.what());
}
- else if (!g_secPollDone) {
+ else if (!s_secPollDone) {
infolog("Error while retrieving the security update for version %s: %s", version, e.what());
}
}
if (releaseVersion) {
warnlog("Failed to retrieve security status update for '%s' on %s", pkgv, queriedName);
}
- else if (!g_secPollDone) {
+ else if (!s_secPollDone) {
infolog("Not validating response for security status update, this is a non-release version.");
/* for non-released versions, there is no use sending the same message several times,
let's just accept that there will be no security polling for this exact version */
- g_secPollDone = true;
+ s_secPollDone = true;
}
}
+}
#endif /* DISABLE_SECPOLL */
#ifndef DISABLE_SECPOLL
#include <string>
-#include <ctime>
-
-extern std::string g_secPollSuffix;
-extern time_t g_secPollInterval;
+namespace dnsdist::secpoll
+{
void doSecPoll(const std::string& suffix);
+}
#endif /* DISABLE_SECPOLL */
*/
#include "dnsdist-session-cache.hh"
-TLSSessionCache g_sessionCache;
+#include "dnsdist-configuration.hh"
-time_t TLSSessionCache::s_cleanupDelay{60};
-time_t TLSSessionCache::s_sessionValidity{600};
-uint16_t TLSSessionCache::s_maxSessionsPerBackend{20};
+TLSSessionCache g_sessionCache;
void TLSSessionCache::cleanup(time_t now, LockGuardedHolder<TLSSessionCache::CacheData>& data)
{
- time_t cutOff = now + s_sessionValidity;
+ const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ time_t cutOff = now + runtimeConfig.d_tlsSessionCacheSessionValidity;
for (auto it = data->d_sessions.begin(); it != data->d_sessions.end();) {
if (it->second.d_lastUsed > cutOff || it->second.d_sessions.size() == 0) {
}
}
- data->d_nextCleanup = now + s_cleanupDelay;
+ data->d_nextCleanup = now + runtimeConfig.d_tlsSessionCacheCleanupDelay;
}
void TLSSessionCache::putSessions(const boost::uuids::uuid& backendID, time_t now, std::vector<std::unique_ptr<TLSSession>>&& sessions)
cleanup(now, data);
}
+ const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
for (auto& session : sessions) {
auto& entry = data->d_sessions[backendID];
- if (entry.d_sessions.size() >= s_maxSessionsPerBackend) {
+ if (entry.d_sessions.size() >= runtimeConfig.d_tlsSessionCacheMaxSessionsPerBackend) {
entry.d_sessions.pop_back();
}
entry.d_sessions.push_front(std::move(session));
void putSessions(const boost::uuids::uuid& backendID, time_t now, std::vector<std::unique_ptr<TLSSession>>&& sessions);
std::unique_ptr<TLSSession> getSession(const boost::uuids::uuid& backendID, time_t now);
- static void setCleanupDelay(time_t delay)
- {
- s_cleanupDelay = delay;
- }
-
- static void setSessionValidity(time_t validity)
- {
- s_sessionValidity = validity;
- }
-
- static void setMaxTicketsPerBackend(uint16_t max)
- {
- s_maxSessionsPerBackend = max;
- }
-
size_t getSize();
private:
- static time_t s_cleanupDelay;
- static time_t s_sessionValidity;
- static uint16_t s_maxSessionsPerBackend;
-
struct BackendEntry
{
std::deque<std::unique_ptr<TLSSession>> d_sessions;
#include "dnsdist-metrics.hh"
#include "dolog.hh"
-bool g_snmpEnabled{false};
-bool g_snmpTrapsEnabled{false};
std::unique_ptr<DNSDistSNMPAgent> g_snmpAgent{nullptr};
#ifdef HAVE_NET_SNMP
bool sendCustomTrap(const std::string& reason);
bool sendDNSTrap(const DNSQuestion&, const std::string& reason = "");
};
+
+extern std::unique_ptr<DNSDistSNMPAgent> g_snmpAgent;
}
}
- if (g_addEDNSToSelfGeneratedResponses && queryHasEDNS(dnsQuestion)) {
+
+ const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ if (runtimeConfig.d_addEDNSToSelfGeneratedResponses && queryHasEDNS(dnsQuestion)) {
bool dnssecOK = ((getEDNSZ(dnsQuestion) & EDNS_HEADER_FLAG_DO) != 0);
- packetWriter.addOpt(g_PayloadSizeSelfGenAnswers, 0, dnssecOK ? EDNS_HEADER_FLAG_DO : 0);
+ packetWriter.addOpt(runtimeConfig.d_payloadSizeSelfGenAnswers, 0, dnssecOK ? EDNS_HEADER_FLAG_DO : 0);
packetWriter.commit();
}
enum class QueryProcessingResult : uint8_t { Forwarded, TooSmall, InvalidHeaders, Dropped, SelfAnswered, NoBackend, Asynchronous };
enum class ProxyProtocolResult : uint8_t { Reading, Done, Error };
- IncomingTCPConnectionState(ConnectionInfo&& ci, TCPClientThreadData& threadData, const struct timeval& now): d_buffer(sizeof(uint16_t)), d_ci(std::move(ci)), d_handler(d_ci.fd, timeval{g_tcpRecvTimeout,0}, d_ci.cs->tlsFrontend ? d_ci.cs->tlsFrontend->getContext() : (d_ci.cs->dohFrontend ? d_ci.cs->dohFrontend->d_tlsContext.getContext() : nullptr), now.tv_sec), d_connectionStartTime(now), d_ioState(make_unique<IOStateHandler>(*threadData.mplexer, d_ci.fd)), d_threadData(threadData), d_creatorThreadID(std::this_thread::get_id())
+ IncomingTCPConnectionState(ConnectionInfo&& ci, TCPClientThreadData& threadData, const struct timeval& now): d_buffer(sizeof(uint16_t)), d_ci(std::move(ci)), d_handler(d_ci.fd, timeval{dnsdist::configuration::getCurrentRuntimeConfiguration().d_tcpRecvTimeout,0}, d_ci.cs->tlsFrontend ? d_ci.cs->tlsFrontend->getContext() : (d_ci.cs->dohFrontend ? d_ci.cs->dohFrontend->d_tlsContext.getContext() : nullptr), now.tv_sec), d_connectionStartTime(now), d_ioState(make_unique<IOStateHandler>(*threadData.mplexer, d_ci.fd)), d_threadData(threadData), d_creatorThreadID(std::this_thread::get_id())
{
d_origDest.reset();
d_origDest.sin4.sin_family = d_ci.remote.sin4.sin_family;
boost::optional<struct timeval> getClientReadTTD(struct timeval now) const
{
- if (g_maxTCPConnectionDuration == 0 && g_tcpRecvTimeout == 0) {
+ const auto& runtimeConfiguration = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ if (runtimeConfiguration.d_maxTCPConnectionDuration == 0 && runtimeConfiguration.d_tcpRecvTimeout == 0) {
return boost::none;
}
- if (g_maxTCPConnectionDuration > 0) {
+ if (runtimeConfiguration.d_maxTCPConnectionDuration > 0) {
auto elapsed = now.tv_sec - d_connectionStartTime.tv_sec;
- if (elapsed < 0 || (static_cast<size_t>(elapsed) >= g_maxTCPConnectionDuration)) {
+ if (elapsed < 0 || (static_cast<size_t>(elapsed) >= runtimeConfiguration.d_maxTCPConnectionDuration)) {
return now;
}
- auto remaining = g_maxTCPConnectionDuration - elapsed;
- if (g_tcpRecvTimeout == 0 || remaining <= static_cast<size_t>(g_tcpRecvTimeout)) {
+ auto remaining = runtimeConfiguration.d_maxTCPConnectionDuration - elapsed;
+ if (runtimeConfiguration.d_tcpRecvTimeout == 0 || remaining <= static_cast<size_t>(runtimeConfiguration.d_tcpRecvTimeout)) {
now.tv_sec += remaining;
return now;
}
}
- now.tv_sec += g_tcpRecvTimeout;
+ now.tv_sec += runtimeConfiguration.d_tcpRecvTimeout;
return now;
}
boost::optional<struct timeval> getClientWriteTTD(const struct timeval& now) const
{
- if (g_maxTCPConnectionDuration == 0 && g_tcpSendTimeout == 0) {
+ const auto& runtimeConfiguration = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ if (runtimeConfiguration.d_maxTCPConnectionDuration == 0 && runtimeConfiguration.d_tcpSendTimeout == 0) {
return boost::none;
}
- struct timeval res = now;
+ timeval res(now);
- if (g_maxTCPConnectionDuration > 0) {
+ if (runtimeConfiguration.d_maxTCPConnectionDuration > 0) {
auto elapsed = res.tv_sec - d_connectionStartTime.tv_sec;
- if (elapsed < 0 || static_cast<size_t>(elapsed) >= g_maxTCPConnectionDuration) {
+ if (elapsed < 0 || static_cast<size_t>(elapsed) >= runtimeConfiguration.d_maxTCPConnectionDuration) {
return res;
}
- auto remaining = g_maxTCPConnectionDuration - elapsed;
- if (g_tcpSendTimeout == 0 || remaining <= static_cast<size_t>(g_tcpSendTimeout)) {
+ auto remaining = runtimeConfiguration.d_maxTCPConnectionDuration - elapsed;
+ if (runtimeConfiguration.d_tcpSendTimeout == 0 || remaining <= static_cast<size_t>(runtimeConfiguration.d_tcpSendTimeout)) {
res.tv_sec += remaining;
return res;
}
}
- res.tv_sec += g_tcpSendTimeout;
+ res.tv_sec += runtimeConfiguration.d_tcpSendTimeout;
return res;
}
Let's start naively.
*/
-size_t g_maxTCPQueriesPerConn{0};
-size_t g_maxTCPConnectionDuration{0};
-
-#ifdef __linux__
-// On Linux this gives us 128k pending queries (default is 8192 queries),
-// which should be enough to deal with huge spikes
-size_t g_tcpInternalPipeBufferSize{1048576U};
-uint64_t g_maxTCPQueuedConnections{10000};
-#else
-size_t g_tcpInternalPipeBufferSize{0};
-uint64_t g_maxTCPQueuedConnections{1000};
-#endif
-
-int g_tcpRecvTimeout{2};
-int g_tcpSendTimeout{2};
std::atomic<uint64_t> g_tcpStatesDumpRequested{0};
LockGuarded<std::map<ComboAddress, size_t, ComboAddress::addressOnlyLessThan>> dnsdist::IncomingConcurrentTCPConnectionsManager::s_tcpClientsConcurrentConnectionsCount;
-size_t dnsdist::IncomingConcurrentTCPConnectionsManager::s_maxTCPConnectionsPerClient = 0;
IncomingTCPConnectionState::~IncomingTCPConnectionState()
{
void TCPClientCollection::addTCPClientThread(std::vector<ClientState*>& tcpAcceptStates)
{
try {
- auto [queryChannelSender, queryChannelReceiver] = pdns::channel::createObjectQueue<ConnectionInfo>(pdns::channel::SenderBlockingMode::SenderNonBlocking, pdns::channel::ReceiverBlockingMode::ReceiverNonBlocking, g_tcpInternalPipeBufferSize);
+ const auto internalPipeBufferSize = dnsdist::configuration::getImmutableConfiguration().d_tcpInternalPipeBufferSize;
+
+ auto [queryChannelSender, queryChannelReceiver] = pdns::channel::createObjectQueue<ConnectionInfo>(pdns::channel::SenderBlockingMode::SenderNonBlocking, pdns::channel::ReceiverBlockingMode::ReceiverNonBlocking, internalPipeBufferSize);
- auto [crossProtocolQueryChannelSender, crossProtocolQueryChannelReceiver] = pdns::channel::createObjectQueue<CrossProtocolQuery>(pdns::channel::SenderBlockingMode::SenderNonBlocking, pdns::channel::ReceiverBlockingMode::ReceiverNonBlocking, g_tcpInternalPipeBufferSize);
+ auto [crossProtocolQueryChannelSender, crossProtocolQueryChannelReceiver] = pdns::channel::createObjectQueue<CrossProtocolQuery>(pdns::channel::SenderBlockingMode::SenderNonBlocking, pdns::channel::ReceiverBlockingMode::ReceiverNonBlocking, internalPipeBufferSize);
- auto [crossProtocolResponseChannelSender, crossProtocolResponseChannelReceiver] = pdns::channel::createObjectQueue<TCPCrossProtocolResponse>(pdns::channel::SenderBlockingMode::SenderNonBlocking, pdns::channel::ReceiverBlockingMode::ReceiverNonBlocking, g_tcpInternalPipeBufferSize);
+ auto [crossProtocolResponseChannelSender, crossProtocolResponseChannelReceiver] = pdns::channel::createObjectQueue<TCPCrossProtocolResponse>(pdns::channel::SenderBlockingMode::SenderNonBlocking, pdns::channel::ReceiverBlockingMode::ReceiverNonBlocking, internalPipeBufferSize);
vinfolog("Adding TCP Client thread");
return false;
}
- if (g_maxTCPQueriesPerConn != 0 && d_queriesCount > g_maxTCPQueriesPerConn) {
- vinfolog("not accepting new queries from %s because it reached the maximum number of queries per conn (%d / %d)", d_ci.remote.toStringWithPort(), d_queriesCount, g_maxTCPQueriesPerConn);
+ const auto& currentConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ if (currentConfig.d_maxTCPQueriesPerConn != 0 && d_queriesCount > currentConfig.d_maxTCPQueriesPerConn) {
+ vinfolog("not accepting new queries from %s because it reached the maximum number of queries per conn (%d / %d)", d_ci.remote.toStringWithPort(), d_queriesCount, currentConfig.d_maxTCPQueriesPerConn);
return false;
}
- if (maxConnectionDurationReached(g_maxTCPConnectionDuration, now)) {
+ if (maxConnectionDurationReached(currentConfig.d_maxTCPConnectionDuration, now)) {
vinfolog("not accepting new queries from %s because it reached the maximum TCP connection duration", d_ci.remote.toStringWithPort());
return false;
}
return;
}
+ const auto config = dnsdist::configuration::getCurrentRuntimeConfiguration();
if (!response.isAsync()) {
try {
auto& ids = response.d_idstate;
std::shared_ptr<DownstreamState> backend = response.d_ds ? response.d_ds : (response.d_connection ? response.d_connection->getDS() : nullptr);
- if (backend == nullptr || !responseContentMatches(response.d_buffer, ids.qname, ids.qtype, ids.qclass, backend)) {
+ if (backend == nullptr || !responseContentMatches(response.d_buffer, ids.qname, ids.qtype, ids.qclass, backend, config.d_allowEmptyResponse)) {
state->terminateClientConnection();
return;
}
iostate = IOState::Done;
IOStateGuard ioGuard(d_ioState);
- if (maxConnectionDurationReached(g_maxTCPConnectionDuration, now)) {
+ const auto& currentConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ if (maxConnectionDurationReached(currentConfig.d_maxTCPConnectionDuration, now)) {
vinfolog("Terminating TCP connection from %s because it reached the maximum TCP connection duration", d_ci.remote.toStringWithPort());
// will be handled by the ioGuard
// handleNewIOState(state, IOState::Done, fd, handleIOCallback);
setTCPNoDelay(connInfo.fd); // disable NAGLE
- if (g_maxTCPQueuedConnections > 0 && g_tcpclientthreads->getQueuedCount() >= g_maxTCPQueuedConnections) {
+ const auto maxTCPQueuedConnections = dnsdist::configuration::getImmutableConfiguration().d_maxTCPQueuedConnections;
+ if (maxTCPQueuedConnections > 0 && g_tcpclientthreads->getQueuedCount() >= maxTCPQueuedConnections) {
vinfolog("Dropping TCP connection from %s because we have too many queued already", remote.toStringWithPort());
return;
}
#include "base64.hh"
#include "connection-management.hh"
#include "dnsdist.hh"
+#include "dnsdist-configuration.hh"
#include "dnsdist-dynblocks.hh"
#include "dnsdist-healthchecks.hh"
#include "dnsdist-metrics.hh"
bool statsRequireAuthentication{true};
};
-bool g_apiReadWrite{false};
LockGuarded<WebserverConfig> g_webserverConfig;
-std::string g_apiConfigDirectory;
static ConcurrentConnectionManager s_connManager(100);
out << "Password: " << (config->password ? "set" : "unset") << endl;
out << "API key: " << (config->apiKey ? "set" : "unset") << endl;
}
- out << "API writable: " << (g_apiReadWrite ? "yes" : "no") << endl;
- out << "API configuration directory: " << g_apiConfigDirectory << endl;
- out << "Maximum concurrent connections: " << s_connManager.getMaxConcurrentConnections() << endl;
+ {
+ const auto& config = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ out << "API writable: " << (config.d_apiReadWrite ? "yes" : "no") << endl;
+ out << "API configuration directory: " << config.d_apiConfigDirectory << endl;
+ out << "Maximum concurrent connections: " << s_connManager.getMaxConcurrentConnections() << endl;
+ }
return out.str();
}
#ifndef DISABLE_WEB_CONFIG
static bool apiWriteConfigFile(const string& filebasename, const string& content)
{
- if (!g_apiReadWrite) {
+ const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ if (!runtimeConfig.d_apiReadWrite) {
warnlog("Not writing content to %s since the API is read-only", filebasename);
return false;
}
- if (g_apiConfigDirectory.empty()) {
+ if (runtimeConfig.d_apiConfigDirectory.empty()) {
vinfolog("Not writing content to %s since the API configuration directory is not set", filebasename);
return false;
}
- string filename = g_apiConfigDirectory + "/" + filebasename + ".conf";
+ string filename = runtimeConfig.d_apiConfigDirectory + "/" + filebasename + ".conf";
ofstream ofconf(filename.c_str());
if (!ofconf) {
errlog("Could not open configuration fragment file '%s' for writing: %s", filename, stringerror());
if (req.method == "GET") {
return true;
}
- if (req.method == "PUT" && g_apiReadWrite) {
+ if (req.method == "PUT" && dnsdist::configuration::getCurrentRuntimeConfiguration().d_apiReadWrite) {
if (req.url.path == "/api/v1/servers/localhost/config/allow-from") {
return true;
}
if (origin != req.headers.end()) {
if (req.method == "OPTIONS") {
/* Pre-flight request */
- if (g_apiReadWrite) {
+ if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_apiReadWrite) {
resp.headers["Access-Control-Allow-Methods"] = "GET, PUT";
}
else {
}
const string& command = req.getvars.at("command");
+ const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
if (command == "stats") {
auto obj = Json::object{
{"reason", entry.second.reason},
{"seconds", static_cast<double>(entry.second.until.tv_sec - now.tv_sec)},
{"blocks", static_cast<double>(counter)},
- {"action", DNSAction::typeToString(entry.second.action != DNSAction::Action::None ? entry.second.action : g_dynBlockAction)},
+ {"action", DNSAction::typeToString(entry.second.action != DNSAction::Action::None ? entry.second.action : runtimeConfig.d_dynBlockAction)},
{"warning", entry.second.warning},
{"ebpf", entry.second.bpf}};
obj.emplace(entry.first.toString(), thing);
}
auto smt = g_dynblockSMT.getLocal();
- smt->visit([&now, &obj](const SuffixMatchTree<DynBlock>& node) {
+ smt->visit([&now, &obj, &runtimeConfig](const SuffixMatchTree<DynBlock>& node) {
if (!(now < node.d_value.until)) {
return;
}
{"reason", node.d_value.reason},
{"seconds", static_cast<double>(node.d_value.until.tv_sec - now.tv_sec)},
{"blocks", static_cast<double>(node.d_value.blocks)},
- {"action", DNSAction::typeToString(node.d_value.action != DNSAction::Action::None ? node.d_value.action : g_dynBlockAction)},
+ {"action", DNSAction::typeToString(node.d_value.action != DNSAction::Action::None ? node.d_value.action : runtimeConfig.d_dynBlockAction)},
{"ebpf", node.d_value.bpf}};
obj.emplace(dom, thing);
});
{"reason", entry.second.reason},
{"seconds", static_cast<double>(entry.second.until.tv_sec - now.tv_sec)},
{"blocks", static_cast<double>(counter)},
- {"action", DNSAction::typeToString(entry.second.action != DNSAction::Action::None ? entry.second.action : g_dynBlockAction)},
+ {"action", DNSAction::typeToString(entry.second.action != DNSAction::Action::None ? entry.second.action : runtimeConfig.d_dynBlockAction)},
{"warning", entry.second.warning},
};
obj.emplace(entry.first.toString(), thing);
resp.status = 200;
Json::array doc;
- typedef boost::variant<bool, double, std::string> configentry_t;
+ const auto runtimeConfiguration = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ const auto immutableConfig = dnsdist::configuration::getImmutableConfiguration();
+ using configentry_t = boost::variant<bool, double, std::string>;
std::vector<std::pair<std::string, configentry_t>> configEntries{
{"acl", g_ACL.getLocal()->toString()},
- {"allow-empty-response", g_allowEmptyResponse},
+ {"allow-empty-response", runtimeConfiguration.d_allowEmptyResponse},
{"control-socket", g_serverControl.toStringWithPort()},
- {"ecs-override", g_ECSOverride},
- {"ecs-source-prefix-v4", (double)g_ECSSourcePrefixV4},
- {"ecs-source-prefix-v6", (double)g_ECSSourcePrefixV6},
- {"fixup-case", g_fixupCase},
- {"max-outstanding", (double)g_maxOutstanding},
+ {"ecs-override", runtimeConfiguration.d_ecsOverride},
+ {"ecs-source-prefix-v4", static_cast<double>(runtimeConfiguration.d_ECSSourcePrefixV4)},
+ {"ecs-source-prefix-v6", static_cast<double>(runtimeConfiguration.d_ECSSourcePrefixV6)},
+ {"fixup-case", runtimeConfiguration.d_fixupCase},
+ {"max-outstanding", static_cast<double>(immutableConfig.d_maxUDPOutstanding)},
{"server-policy", g_policy.getLocal()->getName()},
- {"stale-cache-entries-ttl", (double)g_staleCacheEntriesTTL},
- {"tcp-recv-timeout", (double)g_tcpRecvTimeout},
- {"tcp-send-timeout", (double)g_tcpSendTimeout},
- {"truncate-tc", g_truncateTC},
- {"verbose", g_verbose},
- {"verbose-health-checks", g_verboseHealthChecks}};
+ {"stale-cache-entries-ttl", static_cast<double>(runtimeConfiguration.d_staleCacheEntriesTTL)},
+ {"tcp-recv-timeout", static_cast<double>(runtimeConfiguration.d_tcpRecvTimeout)},
+ {"tcp-send-timeout", static_cast<double>(runtimeConfiguration.d_tcpSendTimeout)},
+ {"truncate-tc", runtimeConfiguration.d_truncateTC},
+ {"verbose", runtimeConfiguration.d_verbose},
+ {"verbose-health-checks", runtimeConfiguration.d_verboseHealthChecks}};
for (const auto& item : configEntries) {
if (const auto& bval = boost::get<bool>(&item.second)) {
doc.emplace_back(Json::object{
#include "dnsdist-async.hh"
#include "dnsdist-cache.hh"
#include "dnsdist-carbon.hh"
+#include "dnsdist-configuration.hh"
#include "dnsdist-console.hh"
#include "dnsdist-crypto.hh"
#include "dnsdist-discovery.hh"
#include "dnsdist-random.hh"
#include "dnsdist-rings.hh"
#include "dnsdist-secpoll.hh"
+#include "dnsdist-snmp.hh"
#include "dnsdist-tcp.hh"
#include "dnsdist-web.hh"
#include "dnsdist-xsk.hh"
on the Lua side we can't do that. */
using std::thread;
-bool g_verbose;
-
-uint16_t g_maxOutstanding{std::numeric_limits<uint16_t>::max()};
-uint32_t g_staleCacheEntriesTTL{0};
-bool g_allowEmptyResponse{false};
GlobalStateHolder<NetmaskGroup> g_ACL;
string g_outputBuffer;
std::vector<std::unique_ptr<ClientState>> g_frontends;
GlobalStateHolder<pools_t> g_pools;
-size_t g_udpVectorSize{1};
std::vector<uint32_t> g_TCPFastOpenKey;
/* UDP: the grand design. Per socket we listen on for incoming queries there is one thread.
Then we have a bunch of connected sockets for talking to downstream servers.
*/
Rings g_rings;
-QueryCount g_qcount;
GlobalStateHolder<servers_t> g_dstates;
-bool g_servFailOnNoPolicy{false};
-bool g_truncateTC{false};
-bool g_fixupCase{false};
-bool g_dropEmptyQueries{false};
-uint32_t g_socketUDPSendBuffer{0};
-uint32_t g_socketUDPRecvBuffer{0};
-
std::set<std::string> g_capabilitiesToRetain;
// we are not willing to receive a bigger UDP response than that, no matter what
}
}
-static void truncateTC(PacketBuffer& packet, size_t maximumSize, unsigned int qnameWireLength)
+static void truncateTC(PacketBuffer& packet, size_t maximumSize, unsigned int qnameWireLength, bool addEDNSToSelfGeneratedResponses)
{
try {
bool hadEDNS = false;
uint16_t payloadSize = 0;
uint16_t zValue = 0;
- if (g_addEDNSToSelfGeneratedResponses) {
+ if (addEDNSToSelfGeneratedResponses) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
hadEDNS = getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(packet.data()), packet.size(), &payloadSize, &zValue);
}
}
}
-bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr<DownstreamState>& remote)
+bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr<DownstreamState>& remote, bool allowEmptyResponse)
{
if (response.size() < sizeof(dnsheader)) {
return false;
}
if (dnsHeader->qdcount == 0) {
- if ((dnsHeader->rcode != RCode::NoError && dnsHeader->rcode != RCode::NXDomain) || g_allowEmptyResponse) {
+ if ((dnsHeader->rcode != RCode::NoError && dnsHeader->rcode != RCode::NXDomain) || allowEmptyResponse) {
return true;
}
return true;
}
- if (g_fixupCase) {
+ if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_fixupCase) {
const auto& realname = qname.getStorage();
if (response.size() >= (sizeof(dnsheader) + realname.length())) {
memcpy(&response.at(sizeof(dnsheader)), realname.c_str(), realname.length());
header.qr = true;
return true;
});
- truncateTC(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), dnsResponse.ids.qname.wirelength());
+ truncateTC(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), dnsResponse.ids.qname.wirelength(), dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses);
++dnsdist::metrics::g_stats.ruleTruncated;
return true;
}
static size_t getInitialUDPPacketBufferSize(bool expectProxyProtocol)
{
- static_assert(s_udpIncomingBufferSize <= s_initialUDPPacketBufferSize, "The incoming buffer size should not be larger than s_initialUDPPacketBufferSize");
+ static_assert(dnsdist::configuration::s_udpIncomingBufferSize <= s_initialUDPPacketBufferSize, "The incoming buffer size should not be larger than s_initialUDPPacketBufferSize");
- if (!expectProxyProtocol || g_proxyProtocolACL.empty()) {
+ const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ if (!expectProxyProtocol || runtimeConfig.d_proxyProtocolACL.empty()) {
return s_initialUDPPacketBufferSize;
}
- return s_initialUDPPacketBufferSize + g_proxyProtocolMaximumSize;
+ return s_initialUDPPacketBufferSize + runtimeConfig.d_proxyProtocolMaximumSize;
}
static size_t getMaximumIncomingPacketSize(const ClientState& clientState)
return getInitialUDPPacketBufferSize(clientState.d_enableProxyProtocol);
}
- if (!clientState.d_enableProxyProtocol || g_proxyProtocolACL.empty()) {
- return s_udpIncomingBufferSize;
+ const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ if (!clientState.d_enableProxyProtocol || runtimeConfig.d_proxyProtocolACL.empty()) {
+ return dnsdist::configuration::s_udpIncomingBufferSize;
}
- return s_udpIncomingBufferSize + g_proxyProtocolMaximumSize;
+ return dnsdist::configuration::s_udpIncomingBufferSize + runtimeConfig.d_proxyProtocolMaximumSize;
}
bool sendUDPResponse(int origFD, const PacketBuffer& response, const int delayMsec, const ComboAddress& origDest, const ComboAddress& origRemote)
if (ids.udpPayloadSize > 0 && response.size() > ids.udpPayloadSize) {
vinfolog("Got a response of size %d while the initial UDP payload size was %d, truncating", response.size(), ids.udpPayloadSize);
- truncateTC(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), dnsResponse.ids.qname.wirelength());
+ truncateTC(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), dnsResponse.ids.qname.wirelength(), dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses);
dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsResponse.getMutableData(), [](dnsheader& header) {
header.tc = true;
return true;
});
}
- else if (dnsResponse.getHeader()->tc && g_truncateTC) {
- truncateTC(response, dnsResponse.getMaximumSize(), dnsResponse.ids.qname.wirelength());
+ else if (dnsResponse.getHeader()->tc && dnsdist::configuration::getCurrentRuntimeConfiguration().d_truncateTC) {
+ truncateTC(response, dnsResponse.getMaximumSize(), dnsResponse.ids.qname.wirelength(), dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses);
}
/* when the answer is encrypted in place, we need to get a copy
const dnsheader_aligned dnsHeader(response.data());
auto queryId = dnsHeader->id;
- if (!responseContentMatches(response, ids.qname, ids.qtype, ids.qclass, dss)) {
+ if (!responseContentMatches(response, ids.qname, ids.qtype, ids.qclass, dss, dnsdist::configuration::getCurrentRuntimeConfiguration().d_allowEmptyResponse)) {
dss->restoreState(queryId, std::move(ids));
return false;
}
g_rings.insertQuery(now, dnsQuestion.ids.origRemote, dnsQuestion.ids.qname, dnsQuestion.ids.qtype, dnsQuestion.getData().size(), *dnsQuestion.getHeader(), dnsQuestion.getProtocol());
}
- if (g_qcount.enabled) {
+ const auto runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
+ if (runtimeConfig.d_queryCountConfig.d_enabled) {
string qname = dnsQuestion.ids.qname.toLogString();
bool countQuery{true};
- if (g_qcount.filter) {
+ if (runtimeConfig.d_queryCountConfig.d_filter) {
auto lock = g_lua.lock();
- std::tie(countQuery, qname) = g_qcount.filter(&dnsQuestion);
+ std::tie(countQuery, qname) = runtimeConfig.d_queryCountConfig.d_filter(&dnsQuestion);
}
if (countQuery) {
- auto records = g_qcount.records.write_lock();
+ auto records = dnsdist::QueryCount::g_queryCountRecords.write_lock();
if (records->count(qname) == 0) {
(*records)[qname] = 0;
}
if (now < got->second.until) {
DNSAction::Action action = got->second.action;
if (action == DNSAction::Action::None) {
- action = g_dynBlockAction;
+ action = runtimeConfig.d_dynBlockAction;
}
switch (action) {
if (now < got->until) {
DNSAction::Action action = got->action;
if (action == DNSAction::Action::None) {
- action = g_dynBlockAction;
+ action = runtimeConfig.d_dynBlockAction;
}
switch (action) {
case DNSAction::Action::NoOp:
if (dnsHeader.qdcount == 0) {
++dnsdist::metrics::g_stats.emptyQueries;
- if (g_dropEmptyQueries) {
+ if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_dropEmptyQueries) {
return false;
}
}
dnsQuestion.ids.packetCache = serverPool->packetCache;
selectBackendForOutgoingQuery(dnsQuestion, serverPool, holders, selectedBackend);
- uint32_t allowExpired = selectedBackend ? 0 : g_staleCacheEntriesTTL;
+ uint32_t allowExpired = selectedBackend ? 0 : dnsdist::configuration::getCurrentRuntimeConfiguration().d_staleCacheEntriesTTL;
if (dnsQuestion.ids.packetCache && !dnsQuestion.ids.skipCache) {
dnsQuestion.ids.dnssecOK = (getEDNSZ(dnsQuestion) & EDNS_HEADER_FLAG_DO) != 0;
}
if (!selectedBackend) {
+ auto servFailOnNoPolicy = dnsdist::configuration::getCurrentRuntimeConfiguration().d_servFailOnNoPolicy;
++dnsdist::metrics::g_stats.noPolicy;
- vinfolog("%s query for %s|%s from %s, no downstream server available", g_servFailOnNoPolicy ? "ServFailed" : "Dropped", dnsQuestion.ids.qname.toLogString(), QType(dnsQuestion.ids.qtype).toString(), dnsQuestion.ids.origRemote.toStringWithPort());
- if (g_servFailOnNoPolicy) {
+ vinfolog("%s query for %s|%s from %s, no downstream server available", servFailOnNoPolicy ? "ServFailed" : "Dropped", dnsQuestion.ids.qname.toLogString(), QType(dnsQuestion.ids.qtype).toString(), dnsQuestion.ids.origRemote.toStringWithPort());
+ if (servFailOnNoPolicy) {
dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
header.rcode = RCode::ServFail;
header.qr = true;
/* used by HarvestDestinationAddress */
cmsgbuf_aligned cbuf{};
};
- const size_t vectSize = g_udpVectorSize;
+ const size_t vectSize = dnsdist::configuration::getImmutableConfiguration().d_udpVectorSize;
if (vectSize > std::numeric_limits<uint16_t>::max()) {
throw std::runtime_error("The value of setUDPMultipleMessagesVectorSize is too high, the maximum value is " + std::to_string(std::numeric_limits<uint16_t>::max()));
LocalHolders holders;
#ifndef DISABLE_RECVMMSG
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
- if (g_udpVectorSize > 1) {
+ if (dnsdist::configuration::getImmutableConfiguration().d_udpVectorSize > 1) {
MultipleMessagesUDPClientThread(states.at(0), holders);
}
else
}
}
-boost::optional<uint64_t> g_maxTCPClientThreads{boost::none};
-pdns::stat16_t g_cacheCleaningDelay{60};
-pdns::stat16_t g_cacheCleaningPercentage{100};
-
static void maintThread()
{
setThreadName("dnsdist/main");
for (;;) {
std::this_thread::sleep_for(std::chrono::seconds(interval));
+ const auto& config = dnsdist::configuration::getCurrentRuntimeConfiguration();
{
auto lua = g_lua.lock();
}
counter++;
- if (counter >= g_cacheCleaningDelay) {
+ if (counter >= config.d_cacheCleaningDelay) {
/* keep track, for each cache, of whether we should keep
expired entries */
std::map<std::shared_ptr<DNSDistPacketCache>, bool> caches;
continue;
}
const auto& packetCache = pair.first;
- size_t upTo = (packetCache->getMaxEntries() * (100 - g_cacheCleaningPercentage)) / 100;
+ size_t upTo = (packetCache->getMaxEntries() * (100 - config.d_cacheCleaningPercentage)) / 100;
packetCache->purgeExpired(upTo, now);
}
counter = 0;
setThreadName("dnsdist/secpoll");
for (;;) {
+ const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
+
try {
- doSecPoll(g_secPollSuffix);
+ dnsdist::secpoll::doSecPoll(runtimeConfig.d_secPollSuffix);
}
catch (...) {
}
// coverity[store_truncates_time_t]
- std::this_thread::sleep_for(std::chrono::seconds(g_secPollInterval));
+ std::this_thread::sleep_for(std::chrono::seconds(runtimeConfig.d_secPollInterval));
}
}
#endif /* DISABLE_SECPOLL */
static void checkFileDescriptorsLimits(size_t udpBindsCount, size_t tcpBindsCount)
{
+ const auto immutableConfig = dnsdist::configuration::getImmutableConfiguration();
/* stdin, stdout, stderr */
rlim_t requiredFDsCount = 3;
auto backends = g_dstates.getLocal();
}
requiredFDsCount += backendUDPSocketsCount;
/* TCP sockets to backends */
- if (g_maxTCPClientThreads) {
- requiredFDsCount += (backends->size() * (*g_maxTCPClientThreads));
+ if (immutableConfig.d_maxTCPClientThreads > 0) {
+ requiredFDsCount += (backends->size() * immutableConfig.d_maxTCPClientThreads);
}
/* listening sockets */
requiredFDsCount += udpBindsCount;
requiredFDsCount += tcpBindsCount;
/* number of TCP connections currently served, assuming 1 connection per worker thread which is of course not right */
- if (g_maxTCPClientThreads) {
- requiredFDsCount += *g_maxTCPClientThreads;
+ if (immutableConfig.d_maxTCPClientThreads > 0) {
+ requiredFDsCount += immutableConfig.d_maxTCPClientThreads;
/* max pipes for communicating between TCP acceptors and client threads */
- requiredFDsCount += (*g_maxTCPClientThreads * 2);
+ requiredFDsCount += (immutableConfig.d_maxTCPClientThreads * 2);
}
/* max TCP queued connections */
- requiredFDsCount += g_maxTCPQueuedConnections;
+ requiredFDsCount += immutableConfig.d_maxTCPQueuedConnections;
/* DelayPipe pipe */
requiredFDsCount += 2;
/* syslog socket */
}
}
-static bool g_warned_ipv6_recvpktinfo = false;
-
static void setupLocalSocket(ClientState& clientState, const ComboAddress& addr, int& socket, bool tcp, bool warn)
{
+ const auto immutableConfig = dnsdist::configuration::getImmutableConfiguration();
+ static bool s_warned_ipv6_recvpktinfo = false;
(void)warn;
socket = SSocket(addr.sin4.sin_family, !tcp ? SOCK_DGRAM : SOCK_STREAM, 0);
int one = 1;
(void)setsockopt(socket, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)); // linux supports this, so why not - might fail on other systems
#ifdef IPV6_RECVPKTINFO
- if (addr.isIPv6() && setsockopt(socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)) < 0 && !g_warned_ipv6_recvpktinfo) {
+ if (addr.isIPv6() && setsockopt(socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)) < 0 && !s_warned_ipv6_recvpktinfo) {
warnlog("Warning: IPV6_RECVPKTINFO setsockopt failed: %s", stringerror());
- g_warned_ipv6_recvpktinfo = true;
+ s_warned_ipv6_recvpktinfo = true;
}
#endif
}
}
if (!tcp) {
- if (g_socketUDPSendBuffer > 0) {
+ if (immutableConfig.d_socketUDPSendBuffer > 0) {
try {
- setSocketSendBuffer(socket, g_socketUDPSendBuffer);
+ setSocketSendBuffer(socket, immutableConfig.d_socketUDPSendBuffer);
}
catch (const std::exception& e) {
warnlog(e.what());
}
}
- if (g_socketUDPRecvBuffer > 0) {
+ if (immutableConfig.d_socketUDPRecvBuffer > 0) {
try {
- setSocketReceiveBuffer(socket, g_socketUDPRecvBuffer);
+ setSocketReceiveBuffer(socket, immutableConfig.d_socketUDPRecvBuffer);
}
catch (const std::exception& e) {
warnlog(e.what());
{nullptr, 0, nullptr, 0}}};
int longindex = 0;
string optstring;
+ dnsdist::configuration::RuntimeConfiguration newConfig;
+
while (true) {
// NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
int gotChar = getopt_long(argc, argv, "a:cC:e:g:hk:l:u:vV", longopts.data(), &longindex);
break;
case 'k':
#if defined HAVE_LIBSODIUM || defined(HAVE_LIBCRYPTO)
- if (B64Decode(string(optarg), g_consoleKey) < 0) {
+ {
+ std::string consoleKey;
+ if (B64Decode(string(optarg), consoleKey) < 0) {
cerr << "Unable to decode key '" << optarg << "'." << endl;
// NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
exit(EXIT_FAILURE);
}
+ dnsdist::configuration::updateImmutableConfiguration([&consoleKey](dnsdist::configuration::Configuration& config) {
+ config.d_consoleKey = std::move(consoleKey);
+ });
+ }
#else
cerr << "dnsdist has been built without libsodium or libcrypto, -k/--setkey is unsupported." << endl;
// NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
exit(EXIT_FAILURE);
#endif
- break;
+ break;
case 'l':
g_cmdLine.locals.push_back(boost::trim_copy(string(optarg)));
break;
g_cmdLine.uid = optarg;
break;
case 'v':
- g_verbose = true;
+ newConfig.d_verbose = true;
break;
case 'V':
reportFeatures();
g_cmdLine.remotes.emplace_back(*ptr);
}
}
+
+ dnsdist::configuration::updateRuntimeConfiguration([&newConfig](dnsdist::configuration::RuntimeConfiguration& config) {
+ config = std::move(newConfig);
+ });
}
static void setupPools()
{
}
#endif
dnsdist::initRandom();
- g_hashperturb = dnsdist::getRandomValue(0xffffffff);
+ dnsdist::configuration::updateImmutableConfiguration([](dnsdist::configuration::Configuration& config) {
+ config.d_hashPerturbation = dnsdist::getRandomValue(0xffffffff);
+ });
#ifdef HAVE_XSK
try {
g_ACL.setState(acl);
}
- auto consoleACL = g_consoleACL.getCopy();
- for (const auto& mask : {"127.0.0.1/8", "::1/128"}) {
- consoleACL.addMask(mask);
- }
- g_consoleACL.setState(consoleACL);
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ for (const auto& mask : {"127.0.0.1/8", "::1/128"}) {
+ config.d_consoleACL.addMask(mask);
+ }
+ });
+
registerBuiltInWebHandlers();
if (g_cmdLine.checkConfig) {
initFrontends();
- g_configurationDone = true;
-
- g_rings.init();
-
- for (auto& frontend : g_frontends) {
- setUpLocalBind(frontend);
-
+ for (const auto& frontend : g_frontends) {
if (!frontend->tcp) {
++udpBindsCount;
}
}
}
+ if (dnsdist::configuration::getImmutableConfiguration().d_maxTCPClientThreads == 0 && tcpBindsCount > 0) {
+ dnsdist::configuration::updateImmutableConfiguration([](dnsdist::configuration::Configuration& config) {
+ config.d_maxTCPClientThreads = static_cast<size_t>(10);
+ });
+ }
+
+ g_configurationDone = true;
+
+ g_rings.init();
+
+ for (auto& frontend : g_frontends) {
+ setUpLocalBind(frontend);
+ }
+
{
std::string acls;
auto aclEntries = g_ACL.getLocal()->toStringVector();
}
{
std::string acls;
- auto aclEntries = g_consoleACL.getLocal()->toStringVector();
+ auto aclEntries = dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleACL.toStringVector();
for (const auto& entry : aclEntries) {
if (!acls.empty()) {
acls += ", ";
}
#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO)
- if (g_consoleEnabled && g_consoleKey.empty()) {
+ if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleEnabled && dnsdist::configuration::getImmutableConfiguration().d_consoleKey.empty()) {
warnlog("Warning, the console has been enabled via 'controlSocket()' but no key has been set with 'setKey()' so all connections will fail until a key has been set");
}
#endif
g_snmpAgent->run();
}
- if (!g_maxTCPClientThreads) {
- g_maxTCPClientThreads = static_cast<size_t>(10);
- }
- else if (*g_maxTCPClientThreads == 0 && tcpBindsCount > 0) {
- warnlog("setMaxTCPClientThreads() has been set to 0 while we are accepting TCP connections, raising to 1");
- g_maxTCPClientThreads = 1;
- }
-
/* we need to create the TCP worker threads before the
acceptor ones, otherwise we might crash when processing
the first TCP query */
#ifndef USE_SINGLE_ACCEPTOR_THREAD
- g_tcpclientthreads = std::make_unique<TCPClientCollection>(*g_maxTCPClientThreads, std::vector<ClientState*>());
+ g_tcpclientthreads = std::make_unique<TCPClientCollection>(dnsdist::configuration::getImmutableConfiguration().d_maxTCPClientThreads, std::vector<ClientState*>());
#endif
#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
#endif /* DISABLE_DYNBLOCKS */
#ifndef DISABLE_SECPOLL
- if (!g_secPollSuffix.empty()) {
+ if (!dnsdist::configuration::getCurrentRuntimeConfiguration().d_secPollSuffix.empty()) {
thread secpollthread(secPollThread);
secpollthread.detach();
}
#include "circular_buffer.hh"
#include "dnscrypt.hh"
#include "dnsdist-cache.hh"
+#include "dnsdist-configuration.hh"
#include "dnsdist-dynbpf.hh"
#include "dnsdist-idstate.hh"
#include "dnsdist-lbpolicies.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 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)
+ data(data_), ids(ids_), ecsPrefixLength(ids.origRemote.sin4.sin_family == AF_INET ? dnsdist::configuration::getCurrentRuntimeConfiguration().d_ECSSourcePrefixV4 : dnsdist::configuration::getCurrentRuntimeConfiguration().d_ECSSourcePrefixV6), ecsOverride(dnsdist::configuration::getCurrentRuntimeConfiguration().d_ecsOverride)
{
}
DNSQuestion(const DNSQuestion&) = delete;
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,
- SetTag,
- };
- 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::SetTag:
- return "Set Tag";
- 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()
extern GlobalStateHolder<NetmaskTree<DynBlock, AddressAndPortRange>> g_dynblockNMG;
-extern vector<pair<struct timeval, std::string>> g_confDelta;
-
using pdns::stat_t;
class BasicQPSLimiter
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;
-
class XskPacket;
class XskSocket;
class XskWorker;
}
return latencyUsec;
}
-
- static int s_udpTimeout;
- static bool s_randomizeSockets;
- static bool s_randomizeIDs;
};
using servers_t = vector<std::shared_ptr<DownstreamState>>;
};
extern GlobalStateHolder<SuffixMatchTree<DynBlock>> g_dynblockSMT;
-extern DNSAction::Action g_dynBlockAction;
extern GlobalStateHolder<ServerPolicy> g_policy;
extern GlobalStateHolder<servers_t> g_dstates;
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;
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);
+bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr<DownstreamState>& remote, bool allowEmptyResponse);
bool checkQueryHeaders(const struct dnsheader& dnsHeader, ClientState& clientState);
bool handleDNSCryptQuery(PacketBuffer& packet, DNSCryptQuery& query, bool tcp, time_t now, PacketBuffer& response);
bool checkDNSCryptQuery(const ClientState& clientState, 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 std::unique_ptr<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
{
#include "dolog.hh"
#include <unistd.h>
-bool g_verbose{false};
-
BOOST_AUTO_TEST_SUITE(test_dnscrypt_cc)
#ifdef HAVE_DNSCRYPT
}
{
- BOOST_CHECK_EQUAL(dnsdist_ffi_dnsquestion_get_ecs_prefix_length(&lightDQ), g_ECSSourcePrefixV4);
+ BOOST_CHECK_EQUAL(dnsdist_ffi_dnsquestion_get_ecs_prefix_length(&lightDQ), dnsdist::configuration::getCurrentRuntimeConfiguration().d_ECSSourcePrefixV4);
dnsdist_ffi_dnsquestion_set_ecs_prefix_length(&lightDQ, 65535);
BOOST_CHECK_EQUAL(dnsdist_ffi_dnsquestion_get_ecs_prefix_length(&lightDQ), 65535U);
}
#include "dnsdist.hh"
#include "dnsdist-ecs.hh"
#include "dnsdist-internal-queries.hh"
+#include "dnsdist-snmp.hh"
#include "dnsdist-tcp.hh"
#include "dnsdist-xsk.hh"
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dnsQuestion.getData().data()), dnsQuestion.getData().size(), &udpPayloadSize, &zValue), true);
BOOST_CHECK_EQUAL(zValue, 0);
- BOOST_CHECK_EQUAL(udpPayloadSize, g_PayloadSizeSelfGenAnswers);
+ BOOST_CHECK_EQUAL(udpPayloadSize, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers);
}
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dnsQuestion.getData().data()), dnsQuestion.getData().size(), &udpPayloadSize, &zValue), true);
BOOST_CHECK_EQUAL(zValue, EDNS_HEADER_FLAG_DO);
- BOOST_CHECK_EQUAL(udpPayloadSize, g_PayloadSizeSelfGenAnswers);
+ BOOST_CHECK_EQUAL(udpPayloadSize, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers);
}
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dnsQuestion.getData().data()), dnsQuestion.getData().size(), &udpPayloadSize, &zValue), true);
BOOST_CHECK_EQUAL(zValue, 0);
- BOOST_CHECK_EQUAL(udpPayloadSize, g_PayloadSizeSelfGenAnswers);
+ BOOST_CHECK_EQUAL(udpPayloadSize, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers);
}
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
BOOST_CHECK_EQUAL(getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(dnsQuestion.getData().data()), dnsQuestion.getData().size(), &udpPayloadSize, &zValue), true);
BOOST_CHECK_EQUAL(zValue, EDNS_HEADER_FLAG_DO);
- BOOST_CHECK_EQUAL(udpPayloadSize, g_PayloadSizeSelfGenAnswers);
+ BOOST_CHECK_EQUAL(udpPayloadSize, dnsdist::configuration::getCurrentRuntimeConfiguration().d_payloadSizeSelfGenAnswers);
}
}
#include "dnsdist.hh"
#include "dnsdist-lua.hh"
#include "dnsdist-lua-ffi.hh"
+#include "dnsdist-snmp.hh"
#include "dolog.hh"
-uint16_t g_maxOutstanding{std::numeric_limits<uint16_t>::max()};
-
#include "ext/luawrapper/include/LuaContext.hpp"
RecursiveLockGuarded<LuaContext> g_lua{LuaContext()};
-bool g_snmpEnabled{false};
-bool g_snmpTrapsEnabled{false};
std::unique_ptr<DNSDistSNMPAgent> g_snmpAgent{nullptr};
#if BENCH_POLICIES
-bool g_verbose{true};
#include "dnsdist-rings.hh"
Rings g_rings;
GlobalStateHolder<NetmaskTree<DynBlock>> g_dynblockNMG;
static void benchPolicy(const ServerPolicy& pol)
{
#if BENCH_POLICIES
- bool existingVerboseValue = g_verbose;
- g_verbose = false;
-
std::vector<DNSName> names;
names.reserve(1000);
for (size_t idx = 0; idx < 1000; idx++) {
}
}
cerr << pol.name << " took " << std::to_string(sw.udiff()) << " us for " << names.size() << endl;
-
- g_verbose = existingVerboseValue;
#endif /* BENCH_POLICIES */
}
ServerPolicy::NumberedServerVector servers;
/* selecting a server on an empty server list */
- g_roundrobinFailOnNoServer = false;
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_roundrobinFailOnNoServer = false;
+ });
auto server = pol.getSelectedBackend(servers, dnsQuestion);
BOOST_CHECK(server == nullptr);
servers.emplace_back(1, std::make_shared<DownstreamState>(ComboAddress("192.0.2.1:53")));
- /* servers start as 'down' but the RR policy returns a server unless g_roundrobinFailOnNoServer is set */
- g_roundrobinFailOnNoServer = true;
+ /* servers start as 'down' but the RR policy returns a server unless d_roundrobinFailOnNoServer is set */
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_roundrobinFailOnNoServer = true;
+ });
server = pol.getSelectedBackend(servers, dnsQuestion);
BOOST_CHECK(server == nullptr);
- g_roundrobinFailOnNoServer = false;
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_roundrobinFailOnNoServer = false;
+ });
server = pol.getSelectedBackend(servers, dnsQuestion);
BOOST_CHECK(server != nullptr);
BOOST_AUTO_TEST_CASE(test_chashed)
{
- bool existingVerboseValue = g_verbose;
- g_verbose = false;
+ bool existingVerboseValue = dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose;
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_verbose = false;
+ });
std::vector<DNSName> names;
names.reserve(1000);
BOOST_CHECK_GT(got, expected / 2);
BOOST_CHECK_LT(got, expected * 2);
- g_verbose = existingVerboseValue;
+ dnsdist::configuration::updateRuntimeConfiguration([existingVerboseValue](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_verbose = existingVerboseValue;
+ });
}
BOOST_AUTO_TEST_CASE(test_lua)
BOOST_AUTO_TEST_CASE(test_lua_ffi_chashed)
{
- bool existingVerboseValue = g_verbose;
- g_verbose = false;
-
std::vector<DNSName> names;
names.reserve(1000);
for (size_t idx = 0; idx < 1000; idx++) {
benchPolicy(pol);
}
- g_verbose = existingVerboseValue;
resetLuaContext();
}
/* we _NEED_ to set this function to empty otherwise we might get what was set
by the last test, and we might not like it at all */
s_processQuery = nullptr;
- g_proxyProtocolACL.clear();
}
};
GlobalStateHolder<NetmaskGroup> g_ACL;
GlobalStateHolder<servers_t> g_dstates;
-QueryCount g_qcount;
-
const bool TCPIOHandler::s_disableConnectForUnitTests = true;
bool checkDNSCryptQuery(const ClientState& cs, PacketBuffer& query, std::unique_ptr<DNSCryptQuery>& dnsCryptQuery, time_t now, bool tcp)
return ProcessQueryResult::Drop;
}
-bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr<DownstreamState>& remote)
+bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr<DownstreamState>& remote, bool allowEmptyResponse)
{
return true;
}
s_backendReadBuffer.clear();
s_backendWriteBuffer.clear();
- g_proxyProtocolACL.clear();
- g_verbose = false;
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_proxyProtocolACL.clear();
+ });
IncomingTCPConnectionState::clearAllDownstreamConnections();
/* we _NEED_ to set this function to empty otherwise we might get what was set
BOOST_FIXTURE_TEST_CASE(test_IncomingConnection_SelfAnswered, TestFixture)
{
+ const auto tcpRecvTimeout = dnsdist::configuration::getCurrentRuntimeConfiguration().d_tcpRecvTimeout;
auto local = getBackendAddress("1", 80);
ClientState localCS(local, true, false, 0, "", {}, true);
auto tlsCtx = std::make_shared<MockupTLSCtx>();
state->handleIO();
BOOST_CHECK_EQUAL(threadData.mplexer->run(&now), 0);
struct timeval later = now;
- later.tv_sec += g_tcpRecvTimeout + 1;
+ later.tv_sec += tcpRecvTimeout + 1;
auto expiredReadConns = threadData.mplexer->getTimeouts(later, false);
for (const auto& cbData : expiredReadConns) {
BOOST_CHECK_EQUAL(cbData.first, state->d_handler.getDescriptor());
state->handleIO();
BOOST_CHECK_EQUAL(threadData.mplexer->run(&now), 0);
struct timeval later = now;
- later.tv_sec += g_tcpRecvTimeout + 1;
+ later.tv_sec += tcpRecvTimeout + 1;
auto expiredWriteConns = threadData.mplexer->getTimeouts(later, true);
for (const auto& cbData : expiredWriteConns) {
BOOST_CHECK_EQUAL(cbData.first, state->d_handler.getDescriptor());
BOOST_FIXTURE_TEST_CASE(test_IncomingConnectionWithProxyProtocol_SelfAnswered, TestFixture)
{
+ const auto tcpRecvTimeout = dnsdist::configuration::getCurrentRuntimeConfiguration().d_tcpRecvTimeout;
auto local = getBackendAddress("1", 80);
ClientState localCS(local, true, false, 0, "", {}, true);
auto tlsCtx = std::make_shared<MockupTLSCtx>();
{
TEST_INIT("=> reading PP");
- g_proxyProtocolACL.addMask("0.0.0.0/0");
- g_proxyProtocolACL.addMask("::0/0");
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_proxyProtocolACL.addMask("0.0.0.0/0");
+ config.d_proxyProtocolACL.addMask("::0/0");
+ });
auto proxyPayload = makeProxyHeader(true, ComboAddress("192.0.2.1"), ComboAddress("192.0.2.2"), {});
BOOST_REQUIRE_GT(proxyPayload.size(), s_proxyProtocolMinimumHeaderSize);
{
TEST_INIT("=> Invalid PP");
- g_proxyProtocolACL.addMask("0.0.0.0/0");
- g_proxyProtocolACL.addMask("::0/0");
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_proxyProtocolACL.addMask("0.0.0.0/0");
+ config.d_proxyProtocolACL.addMask("::0/0");
+ });
+
auto proxyPayload = std::vector<uint8_t>(s_proxyProtocolMinimumHeaderSize);
std::fill(proxyPayload.begin(), proxyPayload.end(), 0);
{
TEST_INIT("=> timeout while reading PP");
- g_proxyProtocolACL.addMask("0.0.0.0/0");
- g_proxyProtocolACL.addMask("::0/0");
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_proxyProtocolACL.addMask("0.0.0.0/0");
+ config.d_proxyProtocolACL.addMask("::0/0");
+ });
+
auto proxyPayload = makeProxyHeader(true, ComboAddress("192.0.2.1"), ComboAddress("192.0.2.2"), {});
BOOST_REQUIRE_GT(proxyPayload.size(), s_proxyProtocolMinimumHeaderSize);
s_readBuffer = query;
state->handleIO();
BOOST_CHECK_EQUAL(threadData.mplexer->run(&now), 0);
struct timeval later = now;
- later.tv_sec += g_tcpRecvTimeout + 1;
+ later.tv_sec += tcpRecvTimeout + 1;
auto expiredReadConns = threadData.mplexer->getTimeouts(later, false);
for (const auto& cbData : expiredReadConns) {
BOOST_CHECK_EQUAL(cbData.first, state->d_handler.getDescriptor());
/* 101 queries on the same connection, check that the maximum number of queries kicks in */
TEST_INIT("=> 101 queries on the same connection");
- g_maxTCPQueriesPerConn = 100;
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_maxTCPQueriesPerConn = 100;
+ });
size_t count = 101;
/* we need to clear them now, otherwise we end up with dangling pointers to the steps via the TLS context, etc */
IncomingTCPConnectionState::clearAllDownstreamConnections();
- g_maxTCPQueriesPerConn = 0;
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_maxTCPQueriesPerConn = 0;
+ });
#endif
}
BOOST_FIXTURE_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR, TestFixture)
{
+ const auto tcpRecvTimeout = dnsdist::configuration::getCurrentRuntimeConfiguration().d_tcpRecvTimeout;
auto local = getBackendAddress("1", 80);
ClientState localCS(local, true, false, 0, "", {}, true);
/* enable out-of-order on the front side */
TEST_INIT("=> 3 queries sent to the backend, 1 self-answered, 1 new query sent to the backend which responds to the first query right away, then to the last one, then the connection to the backend times out");
// increase the client timeout for that test, we want the backend to timeout first
- g_tcpRecvTimeout = 5;
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_tcpRecvTimeout = 5;
+ });
PacketBuffer expectedWriteBuffer;
PacketBuffer expectedBackendWriteBuffer;
IncomingTCPConnectionState::clearAllDownstreamConnections();
// restore the client timeout
- g_tcpRecvTimeout = 2;
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_tcpRecvTimeout = 2;
+ });
}
{
}
struct timeval later = now;
- later.tv_sec += g_tcpRecvTimeout + 1;
+ later.tv_sec += tcpRecvTimeout + 1;
auto expiredConns = threadData.mplexer->getTimeouts(later, false);
BOOST_CHECK_EQUAL(expiredConns.size(), 1U);
for (const auto& cbData : expiredConns) {
}
struct timeval later = now;
- later.tv_sec += g_tcpRecvTimeout + 1;
+ later.tv_sec += tcpRecvTimeout + 1;
auto expiredConns = threadData.mplexer->getTimeouts(later, false);
BOOST_CHECK_EQUAL(expiredConns.size(), 1U);
for (const auto& cbData : expiredConns) {
}
later = now;
- later.tv_sec += g_tcpRecvTimeout + 1;
+ later.tv_sec += tcpRecvTimeout + 1;
expiredConns = threadData.mplexer->getTimeouts(later, false);
BOOST_CHECK_EQUAL(expiredConns.size(), 1U);
for (const auto& cbData : expiredConns) {
/* make sure that the backend's timeout is shorter than the client's */
backend->d_config.tcpConnectTimeout = 1;
- g_tcpRecvTimeout = 5;
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_tcpRecvTimeout = 5;
+ });
bool timeout = false;
s_steps = {
/* restore */
backend->d_config.tcpSendTimeout = 30;
- g_tcpRecvTimeout = 2;
+ dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+ config.d_tcpRecvTimeout = 2;
+ });
/* we need to clear them now, otherwise we end up with dangling pointers to the steps via the TLS context, etc */
/* we have no connection to clear, because there was a timeout! */
}
struct timeval later = now;
- later.tv_sec += g_tcpRecvTimeout + 1;
+ later.tv_sec += tcpRecvTimeout + 1;
auto expiredConns = threadData.mplexer->getTimeouts(later);
BOOST_CHECK_EQUAL(expiredConns.size(), 1U);
for (const auto& cbData : expiredConns) {
BOOST_FIXTURE_TEST_CASE(test_IncomingConnectionOOOR_BackendNotOOOR, TestFixture)
{
+ const auto tcpRecvTimeout = dnsdist::configuration::getCurrentRuntimeConfiguration().d_tcpRecvTimeout;
auto local = getBackendAddress("1", 80);
ClientState localCS(local, true, false, 0, "", {}, true);
/* enable out-of-order on the front side */
}
struct timeval later = now;
- later.tv_sec += g_tcpRecvTimeout + 1;
+ later.tv_sec += tcpRecvTimeout + 1;
auto expiredConns = threadData.mplexer->getTimeouts(later);
BOOST_CHECK_EQUAL(expiredConns.size(), 1U);
for (const auto& cbData : expiredConns) {
errlog("Unable to bind to %s: %s", ca.toStringWithPort(), strerr(errno));
Will log to stdout. Will syslog in any case with LOG_INFO,
- LOG_WARNING, LOG_ERR respectively. If g_verbose=false, vinfolog is a noop.
+ LOG_WARNING, LOG_ERR respectively. If verbose=false, vinfolog is a noop.
More generically, dolog(someiostream, "Hello %s", stream) will log to someiostream
This will happily print a string to %d! Doesn't do further format processing.
}
#if !defined(RECURSOR)
-// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
-extern bool g_verbose;
-
#ifdef DNSDIST
namespace dnsdist::logging
{
ISO8601
};
- static void setVerbose(bool value = true)
- {
- g_verbose = value;
- }
static void setSyslog(bool value = true)
{
s_syslog = value;
{
s_verboseStream = std::move(stream);
}
- static bool getVerbose()
- {
- return g_verbose;
- }
static bool getSyslog()
{
return s_syslog;
#endif /* DNSDIST */
}
-#define vinfolog \
- if (g_verbose) \
+#ifdef DNSDIST
+#include "dnsdist-configuration.hh"
+
+#define vinfolog \
+ if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose) \
verboselog
+#else
+#define vinfolog \
+ infolog
+#endif
template <typename... Args>
void infolog(const char* formatStr, const Args&... args)
}
#else // RECURSOR
-#define g_verbose 0
#define vinfolog \
- if (g_verbose) \
+ if (false) \
infolog
template <typename... Args>
enum class Types : uint8_t { PP_TLV_ALPN = 0x01, PP_TLV_SSL = 0x20 };
};
-static const size_t s_proxyProtocolMinimumHeaderSize = 16;
+static constexpr size_t s_proxyProtocolMinimumHeaderSize = 16;
std::string makeLocalProxyHeader();
std::string makeProxyHeader(bool tcp, const ComboAddress& source, const ComboAddress& destination, const std::vector<ProxyProtocolValue>& values);
const bool TCPIOHandler::s_disableConnectForUnitTests = false;
+namespace {
+bool shouldDoVerboseLogging()
+{
+#ifdef DNSDIST
+ return dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose;
+#elif defined(RECURSOR)
+ return false;
+#else
+ return true;
+#endif
+}
+}
+
#ifdef HAVE_LIBSODIUM
#include <sodium.h>
#endif /* HAVE_LIBSODIUM */
if (!d_conn) {
vinfolog("Error creating TLS object");
- if (g_verbose) {
+ if (shouldDoVerboseLogging()) {
ERR_print_errors_fp(stderr);
}
throw std::runtime_error("Error creating TLS object");
if (!d_conn) {
vinfolog("Error creating TLS object");
- if (g_verbose) {
+ if (shouldDoVerboseLogging()) {
ERR_print_errors_fp(stderr);
}
throw std::runtime_error("Error creating TLS object");
}
#endif
else {
- if (g_verbose) {
+ if (shouldDoVerboseLogging()) {
throw std::runtime_error("Error while processing TLS connection: (" + std::to_string(error) + ") " + libssl_get_error_string());
} else {
throw std::runtime_error("Error while processing TLS connection: " + std::to_string(error));
return d_conn->getAsyncFDs();
}
- const static bool s_disableConnectForUnitTests;
+ static const bool s_disableConnectForUnitTests;
private:
std::unique_ptr<TLSConnection> d_conn{nullptr};