diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 21:14:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 21:14:48 +0000 |
commit | e10ff189aca57bba91933088195d4edda199cb20 (patch) | |
tree | 056237559582eba27e68fa864434436ac5b7f535 /dnsdist-lua.cc | |
parent | Adding upstream version 1.8.3. (diff) | |
download | dnsdist-e10ff189aca57bba91933088195d4edda199cb20.tar.xz dnsdist-e10ff189aca57bba91933088195d4edda199cb20.zip |
Adding upstream version 1.9.3.upstream/1.9.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dnsdist-lua.cc')
-rw-r--r-- | dnsdist-lua.cc | 1081 |
1 files changed, 795 insertions, 286 deletions
diff --git a/dnsdist-lua.cc b/dnsdist-lua.cc index fa1b8b6..73a8567 100644 --- a/dnsdist-lua.cc +++ b/dnsdist-lua.cc @@ -21,6 +21,7 @@ */ #include <cstdint> +#include <cstdio> #include <dirent.h> #include <fstream> #include <cinttypes> @@ -39,11 +40,14 @@ #include "dnsdist-carbon.hh" #include "dnsdist-concurrent-connections.hh" #include "dnsdist-console.hh" +#include "dnsdist-crypto.hh" #include "dnsdist-dynblocks.hh" #include "dnsdist-discovery.hh" #include "dnsdist-ecs.hh" #include "dnsdist-healthchecks.hh" #include "dnsdist-lua.hh" +#include "dnsdist-lua-hooks.hh" +#include "xsk.hh" #ifdef LUAJIT_VERSION #include "dnsdist-lua-ffi.hh" #endif /* LUAJIT_VERSION */ @@ -57,8 +61,10 @@ #include "dnsdist-web.hh" #include "base64.hh" +#include "coverage.hh" +#include "doh.hh" +#include "doq-common.hh" #include "dolog.hh" -#include "sodcrypto.hh" #include "threadname.hh" #ifdef HAVE_LIBSSL @@ -107,14 +113,15 @@ void resetLuaSideEffect() g_noLuaSideEffect = boost::logic::indeterminate; } -using localbind_t = LuaAssociativeTable<boost::variant<bool, int, std::string, LuaArray<int>, LuaArray<std::string>, LuaAssociativeTable<std::string>>>; +using localbind_t = LuaAssociativeTable<boost::variant<bool, int, std::string, LuaArray<int>, LuaArray<std::string>, LuaAssociativeTable<std::string>, std::shared_ptr<XskSocket>>>; -static void parseLocalBindVars(boost::optional<localbind_t>& vars, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface, std::set<int>& cpus, int& tcpListenQueueSize, uint64_t& maxInFlightQueriesPerConnection, uint64_t& tcpMaxConcurrentConnections) +static void parseLocalBindVars(boost::optional<localbind_t>& vars, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface, std::set<int>& cpus, int& tcpListenQueueSize, uint64_t& maxInFlightQueriesPerConnection, uint64_t& tcpMaxConcurrentConnections, bool& enableProxyProtocol) { if (vars) { LuaArray<int> setCpus; getOptionalValue<bool>(vars, "reusePort", reusePort); + getOptionalValue<bool>(vars, "enableProxyProtocol", enableProxyProtocol); getOptionalValue<int>(vars, "tcpFastOpenQueueSize", tcpFastOpenQueueSize); getOptionalValue<int>(vars, "tcpListenQueueSize", tcpListenQueueSize); getOptionalValue<int>(vars, "maxConcurrentTCPConnections", tcpMaxConcurrentConnections); @@ -127,9 +134,19 @@ static void parseLocalBindVars(boost::optional<localbind_t>& vars, bool& reusePo } } } +#ifdef HAVE_XSK +static void parseXskVars(boost::optional<localbind_t>& vars, std::shared_ptr<XskSocket>& socket) +{ + if (!vars) { + return; + } -#if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS) -static bool loadTLSCertificateAndKeys(const std::string& context, std::vector<TLSCertKeyPair>& pairs, boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>> certFiles, LuaTypeOrArrayOf<std::string> keyFiles) + getOptionalValue<std::shared_ptr<XskSocket>>(vars, "xskSocket", socket); +} +#endif /* HAVE_XSK */ + +#if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS) || defined(HAVE_DNS_OVER_QUIC) +static bool loadTLSCertificateAndKeys(const std::string& context, std::vector<TLSCertKeyPair>& pairs, const boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>>& certFiles, const LuaTypeOrArrayOf<std::string>& keyFiles) { if (certFiles.type() == typeid(std::string) && keyFiles.type() == typeid(std::string)) { auto certFile = boost::get<std::string>(certFiles); @@ -225,6 +242,7 @@ static void parseTLSConfig(TLSConfig& config, const std::string& context, boost: getOptionalValue<bool>(vars, "enableRenegotiation", config.d_enableRenegotiation); getOptionalValue<bool>(vars, "tlsAsyncMode", config.d_asyncMode); getOptionalValue<bool>(vars, "ktls", config.d_ktls); + getOptionalValue<bool>(vars, "readAhead", config.d_readAhead); } #endif // defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS) @@ -256,7 +274,7 @@ static void LuaThread(const std::string& code) // maybe offer more than `void` auto func = lua->readVariable<boost::optional<std::function<void(std::string cmd, LuaAssociativeTable<std::string> data)>>>("threadmessage"); if (func) { - func.get()(cmd, data); + func.get()(std::move(cmd), std::move(data)); } else { errlog("Lua thread called submitToMainThread but no threadmessage receiver is defined"); @@ -280,13 +298,6 @@ static void LuaThread(const std::string& code) } } -#ifdef COVERAGE -extern "C" -{ - void __gcov_dump(void); -} -#endif - static bool checkConfigurationTime(const std::string& name) { if (!g_configurationDone) { @@ -297,9 +308,101 @@ static bool checkConfigurationTime(const std::string& name) return false; } +using newserver_t = LuaAssociativeTable<boost::variant<bool, std::string, LuaArray<std::string>, LuaArray<std::shared_ptr<XskSocket>>, DownstreamState::checkfunc_t>>; + +static void handleNewServerHealthCheckParameters(boost::optional<newserver_t>& vars, DownstreamState::Config& config) +{ + std::string valueStr; + + if (getOptionalValue<std::string>(vars, "checkInterval", valueStr) > 0) { + config.checkInterval = static_cast<unsigned int>(std::stoul(valueStr)); + } + + if (getOptionalValue<std::string>(vars, "healthCheckMode", valueStr) > 0) { + const auto& mode = valueStr; + if (pdns_iequals(mode, "auto")) { + config.availability = DownstreamState::Availability::Auto; + } + else if (pdns_iequals(mode, "lazy")) { + config.availability = DownstreamState::Availability::Lazy; + } + else if (pdns_iequals(mode, "up")) { + config.availability = DownstreamState::Availability::Up; + } + else if (pdns_iequals(mode, "down")) { + config.availability = DownstreamState::Availability::Down; + } + else { + warnlog("Ignoring unknown value '%s' for 'healthCheckMode' on 'newServer'", mode); + } + } + + if (getOptionalValue<std::string>(vars, "checkName", valueStr) > 0) { + config.checkName = DNSName(valueStr); + } + + getOptionalValue<std::string>(vars, "checkType", config.checkType); + getOptionalIntegerValue("newServer", vars, "checkClass", config.checkClass); + getOptionalValue<DownstreamState::checkfunc_t>(vars, "checkFunction", config.checkFunction); + getOptionalIntegerValue("newServer", vars, "checkTimeout", config.checkTimeout); + getOptionalValue<bool>(vars, "checkTCP", config.d_tcpCheck); + getOptionalValue<bool>(vars, "setCD", config.setCD); + getOptionalValue<bool>(vars, "mustResolve", config.mustResolve); + + if (getOptionalValue<std::string>(vars, "lazyHealthCheckSampleSize", valueStr) > 0) { + const auto& value = std::stoi(valueStr); + checkParameterBound("lazyHealthCheckSampleSize", value); + config.d_lazyHealthCheckSampleSize = value; + } + + if (getOptionalValue<std::string>(vars, "lazyHealthCheckMinSampleCount", valueStr) > 0) { + const auto& value = std::stoi(valueStr); + checkParameterBound("lazyHealthCheckMinSampleCount", value); + config.d_lazyHealthCheckMinSampleCount = value; + } + + if (getOptionalValue<std::string>(vars, "lazyHealthCheckThreshold", valueStr) > 0) { + const auto& value = std::stoi(valueStr); + checkParameterBound("lazyHealthCheckThreshold", value, std::numeric_limits<uint8_t>::max()); + config.d_lazyHealthCheckThreshold = value; + } + + if (getOptionalValue<std::string>(vars, "lazyHealthCheckFailedInterval", valueStr) > 0) { + const auto& value = std::stoi(valueStr); + checkParameterBound("lazyHealthCheckFailedInterval", value); + config.d_lazyHealthCheckFailedInterval = value; + } + + getOptionalValue<bool>(vars, "lazyHealthCheckUseExponentialBackOff", config.d_lazyHealthCheckUseExponentialBackOff); + + if (getOptionalValue<std::string>(vars, "lazyHealthCheckMaxBackOff", valueStr) > 0) { + const auto& value = std::stoi(valueStr); + checkParameterBound("lazyHealthCheckMaxBackOff", value); + config.d_lazyHealthCheckMaxBackOff = value; + } + + if (getOptionalValue<std::string>(vars, "lazyHealthCheckMode", valueStr) > 0) { + const auto& mode = valueStr; + if (pdns_iequals(mode, "TimeoutOnly")) { + config.d_lazyHealthCheckMode = DownstreamState::LazyHealthCheckMode::TimeoutOnly; + } + else if (pdns_iequals(mode, "TimeoutOrServFail")) { + config.d_lazyHealthCheckMode = DownstreamState::LazyHealthCheckMode::TimeoutOrServFail; + } + else { + warnlog("Ignoring unknown value '%s' for 'lazyHealthCheckMode' on 'newServer'", mode); + } + } + + getOptionalValue<bool>(vars, "lazyHealthCheckWhenUpgraded", config.d_upgradeToLazyHealthChecks); + + getOptionalIntegerValue("newServer", vars, "maxCheckFailures", config.maxCheckFailures); + getOptionalIntegerValue("newServer", vars, "rise", config.minRiseSuccesses); +} + +// NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) { - typedef LuaAssociativeTable<boost::variant<bool, std::string, LuaArray<std::string>, DownstreamState::checkfunc_t>> newserver_t; luaCtx.writeFunction("inClientStartup", [client]() { return client && !g_configurationDone; }); @@ -395,9 +498,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) getOptionalIntegerValue("newServer", vars, "tcpSendTimeout", config.tcpSendTimeout); getOptionalIntegerValue("newServer", vars, "tcpRecvTimeout", config.tcpRecvTimeout); - if (getOptionalValue<std::string>(vars, "checkInterval", valueStr) > 0) { - config.checkInterval = static_cast<unsigned int>(std::stoul(valueStr)); - } + handleNewServerHealthCheckParameters(vars, config); bool fastOpen{false}; if (getOptionalValue<bool>(vars, "tcpFastOpen", fastOpen) > 0) { @@ -419,92 +520,13 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) config.id = boost::uuids::string_generator()(valueStr); } - if (getOptionalValue<std::string>(vars, "healthCheckMode", valueStr) > 0) { - const auto& mode = valueStr; - if (pdns_iequals(mode, "auto")) { - config.availability = DownstreamState::Availability::Auto; - } - else if (pdns_iequals(mode, "lazy")) { - config.availability = DownstreamState::Availability::Lazy; - } - else if (pdns_iequals(mode, "up")) { - config.availability = DownstreamState::Availability::Up; - } - else if (pdns_iequals(mode, "down")) { - config.availability = DownstreamState::Availability::Down; - } - else { - warnlog("Ignoring unknown value '%s' for 'healthCheckMode' on 'newServer'", mode); - } - } - - if (getOptionalValue<std::string>(vars, "checkName", valueStr) > 0) { - config.checkName = DNSName(valueStr); - } - - getOptionalValue<std::string>(vars, "checkType", config.checkType); - getOptionalIntegerValue("newServer", vars, "checkClass", config.checkClass); - getOptionalValue<DownstreamState::checkfunc_t>(vars, "checkFunction", config.checkFunction); - getOptionalIntegerValue("newServer", vars, "checkTimeout", config.checkTimeout); - getOptionalValue<bool>(vars, "checkTCP", config.d_tcpCheck); - getOptionalValue<bool>(vars, "setCD", config.setCD); - getOptionalValue<bool>(vars, "mustResolve", config.mustResolve); - - if (getOptionalValue<std::string>(vars, "lazyHealthCheckSampleSize", valueStr) > 0) { - const auto& value = std::stoi(valueStr); - checkParameterBound("lazyHealthCheckSampleSize", value); - config.d_lazyHealthCheckSampleSize = value; - } - - if (getOptionalValue<std::string>(vars, "lazyHealthCheckMinSampleCount", valueStr) > 0) { - const auto& value = std::stoi(valueStr); - checkParameterBound("lazyHealthCheckMinSampleCount", value); - config.d_lazyHealthCheckMinSampleCount = value; - } - - if (getOptionalValue<std::string>(vars, "lazyHealthCheckThreshold", valueStr) > 0) { - const auto& value = std::stoi(valueStr); - checkParameterBound("lazyHealthCheckThreshold", value, std::numeric_limits<uint8_t>::max()); - config.d_lazyHealthCheckThreshold = value; - } - - if (getOptionalValue<std::string>(vars, "lazyHealthCheckFailedInterval", valueStr) > 0) { - const auto& value = std::stoi(valueStr); - checkParameterBound("lazyHealthCheckFailedInterval", value); - config.d_lazyHealthCheckFailedInterval = value; - } - - getOptionalValue<bool>(vars, "lazyHealthCheckUseExponentialBackOff", config.d_lazyHealthCheckUseExponentialBackOff); - - if (getOptionalValue<std::string>(vars, "lazyHealthCheckMaxBackOff", valueStr) > 0) { - const auto& value = std::stoi(valueStr); - checkParameterBound("lazyHealthCheckMaxBackOff", value); - config.d_lazyHealthCheckMaxBackOff = value; - } - - if (getOptionalValue<std::string>(vars, "lazyHealthCheckMode", valueStr) > 0) { - const auto& mode = valueStr; - if (pdns_iequals(mode, "TimeoutOnly")) { - config.d_lazyHealthCheckMode = DownstreamState::LazyHealthCheckMode::TimeoutOnly; - } - else if (pdns_iequals(mode, "TimeoutOrServFail")) { - config.d_lazyHealthCheckMode = DownstreamState::LazyHealthCheckMode::TimeoutOrServFail; - } - else { - warnlog("Ignoring unknown value '%s' for 'lazyHealthCheckMode' on 'newServer'", mode); - } - } - - getOptionalValue<bool>(vars, "lazyHealthCheckWhenUpgraded", config.d_upgradeToLazyHealthChecks); - getOptionalValue<bool>(vars, "useClientSubnet", config.useECS); getOptionalValue<bool>(vars, "useProxyProtocol", config.useProxyProtocol); + getOptionalValue<bool>(vars, "proxyProtocolAdvertiseTLS", config.d_proxyProtocolAdvertiseTLS); getOptionalValue<bool>(vars, "disableZeroScope", config.disableZeroScope); getOptionalValue<bool>(vars, "ipBindAddrNoPort", config.ipBindAddrNoPort); getOptionalIntegerValue("newServer", vars, "addXPF", config.xpfRRCode); - getOptionalIntegerValue("newServer", vars, "maxCheckFailures", config.maxCheckFailures); - getOptionalIntegerValue("newServer", vars, "rise", config.minRiseSuccesses); getOptionalValue<bool>(vars, "reconnectOnUp", config.reconnectOnUp); @@ -547,8 +569,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) tlsCtx = getTLSContext(config.d_tlsParams); if (getOptionalValue<std::string>(vars, "dohPath", valueStr) > 0) { -#ifndef HAVE_NGHTTP2 - throw std::runtime_error("Outgoing DNS over HTTPS support requested (via 'dohPath' on newServer()) but nghttp2 support is not available"); +#if !defined(HAVE_DNS_OVER_HTTPS) || !defined(HAVE_NGHTTP2) + throw std::runtime_error("Outgoing DNS over HTTPS support requested (via 'dohPath' on newServer()) but it is not available"); #endif serverPort = 443; @@ -618,10 +640,39 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) // create but don't connect the socket in client or check-config modes auto ret = std::make_shared<DownstreamState>(std::move(config), std::move(tlsCtx), !(client || configCheck)); - if (!(client || 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) { + throw std::runtime_error("Adding a server with xsk at runtime is not supported"); + } + std::vector<std::shared_ptr<XskSocket>> xskSockets; + for (auto& socket : luaXskSockets) { + xskSockets.push_back(socket.second); + } + ret->registerXsk(xskSockets); + std::string mac; + if (getOptionalValue<std::string>(vars, "MACAddr", mac) > 0) { + auto* addr = &ret->d_config.destMACAddr[0]; + sscanf(mac.c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", addr, addr + 1, addr + 2, addr + 3, addr + 4, addr + 5); + } + else { + mac = getMACAddress(ret->d_config.remote); + if (mac.size() != ret->d_config.destMACAddr.size()) { + throw runtime_error("Field 'MACAddr' is not set on 'newServer' directive for '" + ret->d_config.remote.toStringWithPort() + "' and cannot be retrieved from the system either!"); + } + memcpy(ret->d_config.destMACAddr.data(), mac.data(), ret->d_config.destMACAddr.size()); + } + infolog("Added downstream server %s via XSK in %s mode", ret->d_config.remote.toStringWithPort(), xskSockets.at(0)->getXDPMode()); + } + else if (!(client || configCheck)) { infolog("Added downstream server %s", ret->d_config.remote.toStringWithPort()); } - +#else /* HAVE_XSK */ + if (!(client || configCheck)) { + infolog("Added downstream server %s", ret->d_config.remote.toStringWithPort()); + } +#endif /* HAVE_XSK */ if (autoUpgrade && ret->getProtocol() != dnsdist::Protocol::DoT && ret->getProtocol() != dnsdist::Protocol::DoH) { dnsdist::ServiceDiscovery::addUpgradeableServer(ret, upgradeInterval, upgradePool, upgradeDoHKey, keepAfterUpgrade); } @@ -725,10 +776,9 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) uint64_t tcpMaxConcurrentConnections = 0; std::string interface; std::set<int> cpus; + bool enableProxyProtocol = true; - parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections); - - checkAllParametersConsumed("setLocal", vars); + parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol); try { ComboAddress loc(addr, 53); @@ -743,8 +793,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) } // only works pre-startup, so no sync necessary - g_frontends.push_back(std::make_unique<ClientState>(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus)); - auto tcpCS = std::make_unique<ClientState>(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus); + auto udpCS = std::make_unique<ClientState>(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + auto tcpCS = std::make_unique<ClientState>(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); if (tcpListenQueueSize > 0) { tcpCS->tcpListenQueueSize = tcpListenQueueSize; } @@ -755,7 +805,21 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) tcpCS->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections; } +#ifdef HAVE_XSK + std::shared_ptr<XskSocket> socket; + parseXskVars(vars, socket); + if (socket) { + udpCS->xskInfo = XskWorker::create(); + udpCS->xskInfo->sharedEmptyFrameOffset = socket->sharedEmptyFrameOffset; + socket->addWorker(udpCS->xskInfo); + socket->addWorkerRoute(udpCS->xskInfo, loc); + vinfolog("Enabling XSK in %s mode for incoming UDP packets to %s", socket->getXDPMode(), loc.toStringWithPort()); + } +#endif /* HAVE_XSK */ + g_frontends.push_back(std::move(udpCS)); g_frontends.push_back(std::move(tcpCS)); + + checkAllParametersConsumed("setLocal", vars); } catch (const std::exception& e) { g_outputBuffer = "Error: " + string(e.what()) + "\n"; @@ -777,15 +841,15 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) uint64_t tcpMaxConcurrentConnections = 0; std::string interface; std::set<int> cpus; + bool enableProxyProtocol = true; - parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections); - checkAllParametersConsumed("addLocal", vars); + parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol); try { ComboAddress loc(addr, 53); // only works pre-startup, so no sync necessary - g_frontends.push_back(std::make_unique<ClientState>(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus)); - auto tcpCS = std::make_unique<ClientState>(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus); + auto udpCS = std::make_unique<ClientState>(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + auto tcpCS = std::make_unique<ClientState>(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); if (tcpListenQueueSize > 0) { tcpCS->tcpListenQueueSize = tcpListenQueueSize; } @@ -795,7 +859,21 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) if (tcpMaxConcurrentConnections > 0) { tcpCS->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections; } +#ifdef HAVE_XSK + std::shared_ptr<XskSocket> socket; + parseXskVars(vars, socket); + if (socket) { + udpCS->xskInfo = XskWorker::create(); + udpCS->xskInfo->sharedEmptyFrameOffset = socket->sharedEmptyFrameOffset; + socket->addWorker(udpCS->xskInfo); + socket->addWorkerRoute(udpCS->xskInfo, loc); + vinfolog("Enabling XSK in %s mode for incoming UDP packets to %s", socket->getXDPMode(), loc.toStringWithPort()); + } +#endif /* HAVE_XSK */ + g_frontends.push_back(std::move(udpCS)); g_frontends.push_back(std::move(tcpCS)); + + checkAllParametersConsumed("addLocal", vars); } catch (std::exception& e) { g_outputBuffer = "Error: " + string(e.what()) + "\n"; @@ -843,12 +921,11 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) luaCtx.writeFunction("showACL", []() { setLuaNoSideEffect(); - vector<string> vec; - - g_ACL.getLocal()->toStringVector(&vec); + auto aclEntries = g_ACL.getLocal()->toStringVector(); - for (const auto& s : vec) - g_outputBuffer += s + "\n"; + for (const auto& entry : aclEntries) { + g_outputBuffer += entry + "\n"; + } }); luaCtx.writeFunction("shutdown", []() { @@ -864,9 +941,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) g_tlslocals.clear(); g_rings.clear(); #endif /* 0 */ -#ifdef COVERAGE - __gcov_dump(); -#endif + pdns::coverage::dumpCoverageData(); _exit(0); }); @@ -942,7 +1017,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) LuaArray<std::shared_ptr<DownstreamState>> ret; int count = 1; for (const auto& s : g_dstates.getCopy()) { - ret.push_back(make_pair(count++, s)); + ret.emplace_back(count++, s); } return ret; }); @@ -1107,23 +1182,22 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) } g_consoleEnabled = true; -#ifdef HAVE_LIBSODIUM +#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO) if (g_configurationDone && g_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 try { - int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0); - SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1); - SBind(sock, local); - SListen(sock, 5); - auto launch = [sock, local]() { - thread t(controlThread, sock, local); - t.detach(); + auto sock = std::make_shared<Socket>(local.sin4.sin_family, SOCK_STREAM, 0); + sock->bind(local, true); + sock->listen(5); + auto launch = [sock = std::move(sock), local]() { + std::thread consoleControlThread(controlThread, sock, local); + consoleControlThread.detach(); }; if (g_launchWork) { - g_launchWork->push_back(launch); + g_launchWork->emplace_back(std::move(launch)); } else { launch(); @@ -1137,8 +1211,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) luaCtx.writeFunction("addConsoleACL", [](const std::string& netmask) { setLuaSideEffect(); -#ifndef HAVE_LIBSODIUM - warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications"); +#if !defined(HAVE_LIBSODIUM) && !defined(HAVE_LIBCRYPTO) + 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); }); @@ -1147,8 +1221,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) luaCtx.writeFunction("setConsoleACL", [](LuaTypeOrArrayOf<std::string> inp) { setLuaSideEffect(); -#ifndef HAVE_LIBSODIUM - warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications"); +#if !defined(HAVE_LIBSODIUM) && !defined(HAVE_LIBCRYPTO) + 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 NetmaskGroup nmg; @@ -1165,15 +1239,14 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) luaCtx.writeFunction("showConsoleACL", []() { setLuaNoSideEffect(); -#ifndef HAVE_LIBSODIUM - warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications"); +#if !defined(HAVE_LIBSODIUM) && !defined(HAVE_LIBCRYPTO) + 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 - vector<string> vec; - g_consoleACL.getLocal()->toStringVector(&vec); + auto aclEntries = g_consoleACL.getLocal()->toStringVector(); - for (const auto& s : vec) { - g_outputBuffer += s + "\n"; + for (const auto& entry : aclEntries) { + g_outputBuffer += entry + "\n"; } }); @@ -1212,20 +1285,20 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) luaCtx.writeFunction("setQueryCount", [](bool enabled) { g_qcount.enabled = enabled; }); luaCtx.writeFunction("setQueryCountFilter", [](QueryCountFilter func) { - g_qcount.filter = func; + g_qcount.filter = std::move(func); }); luaCtx.writeFunction("makeKey", []() { setLuaNoSideEffect(); - g_outputBuffer = "setKey(" + newKey() + ")\n"; + g_outputBuffer = "setKey(" + dnsdist::crypto::authenticated::newKey() + ")\n"; }); luaCtx.writeFunction("setKey", [](const std::string& key) { if (!g_configurationDone && !g_consoleKey.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf return; // but later setKeys() trump the -k value again } -#ifndef HAVE_LIBSODIUM - warnlog("Calling setKey() while libsodium support has not been enabled is not secure, and will result in cleartext communications"); +#if !defined(HAVE_LIBSODIUM) && !defined(HAVE_LIBCRYPTO) + warnlog("Calling setKey() while neither libsodium nor libcrypto support has been enabled is not secure, and will result in cleartext communications"); #endif setLuaSideEffect(); @@ -1235,7 +1308,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) errlog("%s", g_outputBuffer); } else - g_consoleKey = newkey; + g_consoleKey = std::move(newkey); }); luaCtx.writeFunction("clearConsoleHistory", []() { @@ -1244,7 +1317,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) luaCtx.writeFunction("testCrypto", [](boost::optional<string> optTestMsg) { setLuaNoSideEffect(); -#ifdef HAVE_LIBSODIUM +#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO) try { string testmsg; @@ -1255,22 +1328,25 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) testmsg = "testStringForCryptoTests"; } - SodiumNonce sn, sn2; - sn.init(); - sn2 = sn; - string encrypted = sodEncryptSym(testmsg, g_consoleKey, sn); - string decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2); + dnsdist::crypto::authenticated::Nonce nonce1; + 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); - sn.increment(); - sn2.increment(); + nonce1.increment(); + nonce2.increment(); - encrypted = sodEncryptSym(testmsg, g_consoleKey, sn); - decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2); + encrypted = dnsdist::crypto::authenticated::encryptSym(testmsg, g_consoleKey, nonce1); + decrypted = dnsdist::crypto::authenticated::decryptSym(encrypted, g_consoleKey, nonce2); - if (testmsg == decrypted) + if (testmsg == decrypted) { g_outputBuffer = "Everything is ok!\n"; - else + } + else { g_outputBuffer = "Crypto failed.. (the decoded value does not match the cleartext one)\n"; + } } catch (const std::exception& e) { g_outputBuffer = "Crypto failed: " + std::string(e.what()) + "\n"; @@ -1337,6 +1413,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) setTCPDownstreamMaxIdleConnectionsPerBackend(max); }); +#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2) luaCtx.writeFunction("setMaxIdleDoHConnectionsPerDownstream", [](uint64_t max) { setDoHDownstreamMaxIdleConnectionsPerBackend(max); }); @@ -1347,6 +1424,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) } g_outgoingDoHWorkerThreads = workers; }); +#endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */ luaCtx.writeFunction("setOutgoingTLSSessionsCacheMaxTicketsPerBackend", [](uint64_t max) { if (!checkConfigurationTime("setOutgoingTLSSessionsCacheMaxTicketsPerBackend")) { @@ -1421,6 +1499,58 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) }); }); + luaCtx.writeFunction("getDynamicBlocks", []() { + setLuaNoSideEffect(); + struct timespec now + { + }; + gettime(&now); + + LuaAssociativeTable<DynBlock> entries; + auto fullCopy = g_dynblockNMG.getCopy(); + for (const auto& blockPair : fullCopy) { + const auto& requestor = blockPair.first; + if (!(now < blockPair.second.until)) { + continue; + } + auto entry = blockPair.second; + if (g_defaultBPFFilter && entry.bpf) { + entry.blocks += g_defaultBPFFilter->getHits(requestor.getNetwork()); + } + if (entry.action == DNSAction::Action::None) { + entry.action = g_dynBlockAction; + } + entries.emplace(requestor.toString(), std::move(entry)); + } + return entries; + }); + + luaCtx.writeFunction("getDynamicBlocksSMT", []() { + setLuaNoSideEffect(); + struct timespec now + { + }; + gettime(&now); + + LuaAssociativeTable<DynBlock> entries; + auto fullCopy = g_dynblockSMT.getCopy(); + fullCopy.visit([&now, &entries](const SuffixMatchTree<DynBlock>& node) { + if (!(now < node.d_value.until)) { + return; + } + auto entry = node.d_value; + string key("empty"); + if (!entry.domain.empty()) { + key = entry.domain.toString(); + } + if (entry.action == DNSAction::Action::None) { + entry.action = g_dynBlockAction; + } + entries.emplace(std::move(key), std::move(entry)); + }); + return entries; + }); + luaCtx.writeFunction("clearDynBlocks", []() { setLuaSideEffect(); nmts_t nmg; @@ -1466,61 +1596,87 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) if (!got || expired) { warnlog("Inserting dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg); } - slow.insert(requestor).second = db; + slow.insert(requestor).second = std::move(db); } g_dynblockNMG.setState(slow); }); + 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; + } + else { + errlog("Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!"); + g_outputBuffer = "Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!\n"; + } + }); +#endif /* DISABLE_DEPRECATED_DYNBLOCK */ + luaCtx.writeFunction("addDynBlockSMT", [](const LuaArray<std::string>& names, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) { if (names.empty()) { return; } setLuaSideEffect(); - auto slow = g_dynblockSMT.getCopy(); - struct timespec until, now; + struct timespec now + { + }; gettime(&now); - until = now; - int actualSeconds = seconds ? *seconds : 10; - until.tv_sec += actualSeconds; + unsigned int actualSeconds = seconds ? *seconds : 10; + bool needUpdate = false; + auto slow = g_dynblockSMT.getCopy(); for (const auto& capair : names) { - unsigned int count = 0; DNSName domain(capair.second); domain.makeUsLowerCase(); - auto got = slow.lookup(domain); - bool expired = false; - if (got) { - if (until < got->until) // had a longer policy - continue; - if (now < got->until) // only inherit count on fresh query we are extending - count = got->blocks; - else - expired = true; + + if (dnsdist::DynamicBlocks::addOrRefreshBlockSMT(slow, now, domain, msg, actualSeconds, action ? *action : DNSAction::Action::None, false)) { + needUpdate = true; } + } - DynBlock db{msg, until, domain, (action ? *action : DNSAction::Action::None)}; - db.blocks = count; - if (!got || expired) - warnlog("Inserting dynamic block for %s for %d seconds: %s", domain, actualSeconds, msg); - slow.add(domain, std::move(db)); + if (needUpdate) { + g_dynblockSMT.setState(slow); } - g_dynblockSMT.setState(slow); }); - 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; - } - else { - errlog("Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!"); - g_outputBuffer = "Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!\n"; - } - }); -#endif /* DISABLE_DEPRECATED_DYNBLOCK */ + luaCtx.writeFunction("addDynamicBlock", + [](const boost::variant<ComboAddress, std::string>& clientIP, const std::string& msg, const boost::optional<DNSAction::Action> action, const boost::optional<int> seconds, boost::optional<uint8_t> clientIPMask, boost::optional<uint8_t> clientIPPortMask) { + setLuaSideEffect(); + + ComboAddress clientIPCA; + if (clientIP.type() == typeid(ComboAddress)) { + clientIPCA = boost::get<ComboAddress>(clientIP); + } + else { + const auto& clientIPStr = boost::get<std::string>(clientIP); + try { + clientIPCA = ComboAddress(clientIPStr); + } + catch (const std::exception& exp) { + errlog("addDynamicBlock: Unable to parse '%s': %s", clientIPStr, exp.what()); + return; + } + catch (const PDNSException& exp) { + errlog("addDynamicBlock: Unable to parse '%s': %s", clientIPStr, exp.reason); + return; + } + } + AddressAndPortRange target(clientIPCA, clientIPMask ? *clientIPMask : (clientIPCA.isIPv4() ? 32 : 128), clientIPPortMask ? *clientIPPortMask : 0); + unsigned int actualSeconds = seconds ? *seconds : 10; + + struct timespec now + { + }; + gettime(&now); + auto slow = g_dynblockNMG.getCopy(); + if (dnsdist::DynamicBlocks::addOrRefreshBlock(slow, now, target, msg, actualSeconds, action ? *action : DNSAction::Action::None, false, false)) { + g_dynblockNMG.setState(slow); + } + }); luaCtx.writeFunction("setDynBlocksPurgeInterval", [](uint64_t interval) { DynBlockMaintenance::s_expiredDynBlocksPurgeInterval = interval; @@ -1540,14 +1696,15 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) std::string interface; std::set<int> cpus; std::vector<DNSCryptContext::CertKeyPaths> certKeys; + bool enableProxyProtocol = true; - parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections); + parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol); checkAllParametersConsumed("addDNSCryptBind", vars); if (certFiles.type() == typeid(std::string) && keyFiles.type() == typeid(std::string)) { auto certFile = boost::get<std::string>(certFiles); auto keyFile = boost::get<std::string>(keyFiles); - certKeys.push_back({certFile, keyFile}); + certKeys.push_back({std::move(certFile), std::move(keyFile)}); } else if (certFiles.type() == typeid(LuaArray<std::string>) && keyFiles.type() == typeid(LuaArray<std::string>)) { auto certFilesVect = boost::get<LuaArray<std::string>>(certFiles); @@ -1573,29 +1730,29 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) auto ctx = std::make_shared<DNSCryptContext>(providerName, certKeys); /* UDP */ - auto cs = std::make_unique<ClientState>(ComboAddress(addr, 443), false, reusePort, tcpFastOpenQueueSize, interface, cpus); - cs->dnscryptCtx = ctx; + auto clientState = std::make_unique<ClientState>(ComboAddress(addr, 443), false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + clientState->dnscryptCtx = ctx; g_dnsCryptLocals.push_back(ctx); - g_frontends.push_back(std::move(cs)); + g_frontends.push_back(std::move(clientState)); /* TCP */ - cs = std::make_unique<ClientState>(ComboAddress(addr, 443), true, reusePort, tcpFastOpenQueueSize, interface, cpus); - cs->dnscryptCtx = ctx; + clientState = std::make_unique<ClientState>(ComboAddress(addr, 443), true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + clientState->dnscryptCtx = std::move(ctx); if (tcpListenQueueSize > 0) { - cs->tcpListenQueueSize = tcpListenQueueSize; + clientState->tcpListenQueueSize = tcpListenQueueSize; } if (maxInFlightQueriesPerConn > 0) { - cs->d_maxInFlightQueriesPerConn = maxInFlightQueriesPerConn; + clientState->d_maxInFlightQueriesPerConn = maxInFlightQueriesPerConn; } if (tcpMaxConcurrentConnections > 0) { - cs->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections; + clientState->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections; } - g_frontends.push_back(std::move(cs)); + g_frontends.push_back(std::move(clientState)); } - catch (std::exception& e) { - errlog(e.what()); - g_outputBuffer = "Error: " + string(e.what()) + "\n"; + catch (const std::exception& e) { + errlog("Error during addDNSCryptBind() processing: %s", e.what()); + g_outputBuffer = "Error during addDNSCryptBind() processing: " + string(e.what()) + "\n"; } }); @@ -1683,7 +1840,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) const auto localPools = g_pools.getCopy(); for (const auto& entry : localPools) { const string& name = entry.first; - ret.push_back(make_pair(count++, name)); + ret.emplace_back(count++, name); } return ret; }); @@ -1707,12 +1864,33 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) } try { auto stream = std::ofstream(dest.c_str()); - g_verboseStream = std::move(stream); + dnsdist::logging::LoggingConfiguration::setVerboseStream(std::move(stream)); } catch (const std::exception& e) { errlog("Error while opening the verbose logging destination file %s: %s", dest, e.what()); } }); + luaCtx.writeFunction("setStructuredLogging", [](bool enable, boost::optional<LuaAssociativeTable<std::string>> options) { + std::string levelPrefix; + std::string timeFormat; + if (options) { + getOptionalValue<std::string>(options, "levelPrefix", levelPrefix); + if (getOptionalValue<std::string>(options, "timeFormat", timeFormat) == 1) { + if (timeFormat == "numeric") { + dnsdist::logging::LoggingConfiguration::setStructuredTimeFormat(dnsdist::logging::LoggingConfiguration::TimeFormat::Numeric); + } + else if (timeFormat == "ISO8601") { + dnsdist::logging::LoggingConfiguration::setStructuredTimeFormat(dnsdist::logging::LoggingConfiguration::TimeFormat::ISO8601); + } + else { + warnlog("Unknown value '%s' to setStructuredLogging's 'timeFormat' parameter", timeFormat); + } + } + checkAllParametersConsumed("setStructuredLogging", options); + } + + dnsdist::logging::LoggingConfiguration::setStructuredLogging(enable, levelPrefix); + }); luaCtx.writeFunction("setStaleCacheEntriesTTL", [](uint64_t ttl) { checkParameterBound("setStaleCacheEntriesTTL", ttl, std::numeric_limits<uint32_t>::max()); @@ -1783,12 +1961,12 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) if (!checkConfigurationTime("setDefaultBPFFilter")) { return; } - g_defaultBPFFilter = bpf; + g_defaultBPFFilter = std::move(bpf); }); luaCtx.writeFunction("registerDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) { if (dbpf) { - g_dynBPFFilters.push_back(dbpf); + g_dynBPFFilters.push_back(std::move(dbpf)); } }); @@ -1830,10 +2008,10 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) setLuaNoSideEffect(); std::unordered_map<string, uint64_t> res; { - auto entries = g_stats.entries.read_lock(); + auto entries = dnsdist::metrics::g_stats.entries.read_lock(); res.reserve(entries->size()); for (const auto& entry : *entries) { - if (const auto& val = boost::get<pdns::stat_t*>(&entry.d_value)) { + if (const auto& val = std::get_if<pdns::stat_t*>(&entry.d_value)) { res[entry.d_name] = (*val)->load(); } } @@ -1864,33 +2042,30 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) return; } - DIR* dirp; - struct dirent* ent; std::vector<std::string> files; - if (!(dirp = opendir(dirname.c_str()))) { - errlog("Error opening the included directory %s!", dirname.c_str()); - g_outputBuffer = "Error opening the included directory " + dirname + "!"; - return; - } - - while ((ent = readdir(dirp)) != NULL) { - if (ent->d_name[0] == '.') { - continue; + auto directoryError = pdns::visit_directory(dirname, [&dirname, &files]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) { + if (boost::starts_with(name, ".")) { + return true; } - - if (boost::ends_with(ent->d_name, ".conf")) { + if (boost::ends_with(name, ".conf")) { std::ostringstream namebuf; - namebuf << dirname << "/" << ent->d_name; - - if (stat(namebuf.str().c_str(), &st) || !S_ISREG(st.st_mode)) { - continue; + namebuf << dirname << "/" << name; + struct stat fileStat + { + }; + if (stat(namebuf.str().c_str(), &fileStat) == 0 && S_ISREG(fileStat.st_mode)) { + files.push_back(namebuf.str()); } - - files.push_back(namebuf.str()); } + return true; + }); + + if (directoryError) { + errlog("Error opening included directory: %s!", *directoryError); + g_outputBuffer = "Error opening included directory: " + *directoryError + "!"; + return; } - closedir(dirp); std::sort(files.begin(), files.end()); g_included = true; @@ -2051,9 +2226,9 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) #endif /* HAVE_NET_SNMP */ #ifndef DISABLE_POLICIES_BINDINGS - luaCtx.writeFunction("setServerPolicy", [](const ServerPolicy& policy) { + luaCtx.writeFunction("setServerPolicy", [](const std::shared_ptr<ServerPolicy>& policy) { setLuaSideEffect(); - g_policy.setState(policy); + g_policy.setState(*policy); }); luaCtx.writeFunction("setServerPolicyLua", [](const string& name, ServerPolicy::policyfunc_t policy) { @@ -2078,24 +2253,24 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) g_outputBuffer = g_policy.getLocal()->getName() + "\n"; }); - luaCtx.writeFunction("setPoolServerPolicy", [](ServerPolicy policy, const string& pool) { + luaCtx.writeFunction("setPoolServerPolicy", [](const std::shared_ptr<ServerPolicy>& policy, const string& pool) { setLuaSideEffect(); auto localPools = g_pools.getCopy(); - setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(policy)); + setPoolPolicy(localPools, pool, policy); g_pools.setState(localPools); }); luaCtx.writeFunction("setPoolServerPolicyLua", [](const string& name, ServerPolicy::policyfunc_t policy, const string& pool) { setLuaSideEffect(); auto localPools = g_pools.getCopy(); - setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, policy, true})); + setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, std::move(policy), true})); g_pools.setState(localPools); }); luaCtx.writeFunction("setPoolServerPolicyLuaFFI", [](const string& name, ServerPolicy::ffipolicyfunc_t policy, const string& pool) { setLuaSideEffect(); auto localPools = g_pools.getCopy(); - setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, policy})); + setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, std::move(policy)})); g_pools.setState(localPools); }); @@ -2125,11 +2300,13 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) setTCPDownstreamCleanupInterval(interval); }); +#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2) luaCtx.writeFunction("setDoHDownstreamCleanupInterval", [](uint64_t interval) { setLuaSideEffect(); checkParameterBound("setDoHDownstreamCleanupInterval", interval); setDoHDownstreamCleanupInterval(interval); }); +#endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */ luaCtx.writeFunction("setTCPDownstreamMaxIdleTime", [](uint64_t max) { setLuaSideEffect(); @@ -2137,11 +2314,13 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) setTCPDownstreamMaxIdleTime(max); }); +#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2) luaCtx.writeFunction("setDoHDownstreamMaxIdleTime", [](uint64_t max) { setLuaSideEffect(); checkParameterBound("setDoHDownstreamMaxIdleTime", max); setDoHDownstreamMaxIdleTime(max); }); +#endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */ luaCtx.writeFunction("setConsoleConnectionsLogging", [](bool enabled) { g_logConsoleConnections = enabled; @@ -2221,7 +2400,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) #ifndef DISABLE_SECPOLL luaCtx.writeFunction("showSecurityStatus", []() { setLuaNoSideEffect(); - g_outputBuffer = std::to_string(g_stats.securityStatus) + "\n"; + g_outputBuffer = std::to_string(dnsdist::metrics::g_stats.securityStatus) + "\n"; }); luaCtx.writeFunction("setSecurityPollSuffix", [](const std::string& suffix) { @@ -2320,7 +2499,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) password = boost::get<const string>((*opts)["password"]); } } - result = std::make_shared<TLSCertKeyPair>(TLSCertKeyPair{cert, key, password}); + result = std::make_shared<TLSCertKeyPair>(cert, std::move(key), std::move(password)); #endif return result; }); @@ -2336,31 +2515,61 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) setLuaSideEffect(); auto frontend = std::make_shared<DOHFrontend>(); + if (getOptionalValue<std::string>(vars, "library", frontend->d_library) == 0) { +#ifdef HAVE_NGHTTP2 + frontend->d_library = "nghttp2"; +#else /* HAVE_NGHTTP2 */ + frontend->d_library = "h2o"; +#endif /* HAVE_NGHTTP2 */ + } + if (frontend->d_library == "h2o") { +#ifdef HAVE_LIBH2OEVLOOP + frontend = std::make_shared<H2ODOHFrontend>(); + // we _really_ need to set it again, as we just replaced the generic frontend by a new one + frontend->d_library = "h2o"; +#else /* HAVE_LIBH2OEVLOOP */ + errlog("DOH bind %s is configured to use libh2o but the library is not available", addr); + return; +#endif /* HAVE_LIBH2OEVLOOP */ + } + else if (frontend->d_library == "nghttp2") { +#ifndef HAVE_NGHTTP2 + errlog("DOH bind %s is configured to use nghttp2 but the library is not available", addr); + return; +#endif /* HAVE_NGHTTP2 */ + } + else { + errlog("DOH bind %s is configured to use an unknown library ('%s')", addr, frontend->d_library); + return; + } + + bool useTLS = true; if (certFiles && !certFiles->empty()) { - if (!loadTLSCertificateAndKeys("addDOHLocal", frontend->d_tlsConfig.d_certKeyPairs, *certFiles, *keyFiles)) { + if (!loadTLSCertificateAndKeys("addDOHLocal", frontend->d_tlsContext.d_tlsConfig.d_certKeyPairs, *certFiles, *keyFiles)) { return; } - frontend->d_local = ComboAddress(addr, 443); + frontend->d_tlsContext.d_addr = ComboAddress(addr, 443); } else { - frontend->d_local = ComboAddress(addr, 80); - infolog("No certificate provided for DoH endpoint %s, running in DNS over HTTP mode instead of DNS over HTTPS", frontend->d_local.toStringWithPort()); + frontend->d_tlsContext.d_addr = ComboAddress(addr, 80); + infolog("No certificate provided for DoH endpoint %s, running in DNS over HTTP mode instead of DNS over HTTPS", frontend->d_tlsContext.d_addr.toStringWithPort()); + useTLS = false; } if (urls) { if (urls->type() == typeid(std::string)) { - frontend->d_urls.push_back(boost::get<std::string>(*urls)); + frontend->d_urls.insert(boost::get<std::string>(*urls)); } else if (urls->type() == typeid(LuaArray<std::string>)) { auto urlsVect = boost::get<LuaArray<std::string>>(*urls); for (const auto& p : urlsVect) { - frontend->d_urls.push_back(p.second); + frontend->d_urls.insert(p.second); } } } else { - frontend->d_urls = {"/dns-query"}; + frontend->d_urls.insert("/dns-query"); } bool reusePort = false; @@ -2371,16 +2580,20 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) std::string interface; std::set<int> cpus; std::vector<std::pair<ComboAddress, int>> additionalAddresses; + bool enableProxyProtocol = true; if (vars) { - parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections); + parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol); getOptionalValue<int>(vars, "idleTimeout", frontend->d_idleTimeout); getOptionalValue<std::string>(vars, "serverTokens", frontend->d_serverTokens); + getOptionalValue<std::string>(vars, "provider", frontend->d_tlsContext.d_provider); + boost::algorithm::to_lower(frontend->d_tlsContext.d_provider); + getOptionalValue<bool>(vars, "proxyProtocolOutsideTLS", frontend->d_tlsContext.d_proxyProtocolOutsideTLS); LuaAssociativeTable<std::string> customResponseHeaders; if (getOptionalValue<decltype(customResponseHeaders)>(vars, "customResponseHeaders", customResponseHeaders) > 0) { for (auto const& headerMap : customResponseHeaders) { - std::pair<std::string, std::string> headerResponse = std::make_pair(boost::to_lower_copy(headerMap.first), headerMap.second); + auto headerResponse = std::pair(boost::to_lower_copy(headerMap.first), headerMap.second); frontend->d_customResponseHeaders.insert(headerResponse); } } @@ -2388,6 +2601,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) getOptionalValue<bool>(vars, "sendCacheControlHeaders", frontend->d_sendCacheControlHeaders); getOptionalValue<bool>(vars, "keepIncomingHeaders", frontend->d_keepIncomingHeaders); getOptionalValue<bool>(vars, "trustForwardedForHeader", frontend->d_trustForwardedForHeader); + getOptionalValue<bool>(vars, "earlyACLDrop", frontend->d_earlyACLDrop); getOptionalValue<int>(vars, "internalPipeBufferSize", frontend->d_internalPipeBufferSize); getOptionalValue<bool>(vars, "exactPathMatching", frontend->d_exactPathMatching); @@ -2405,7 +2619,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) } } - parseTLSConfig(frontend->d_tlsConfig, "addDOHLocal", vars); + parseTLSConfig(frontend->d_tlsContext.d_tlsConfig, "addDOHLocal", vars); bool ignoreTLSConfigurationErrors = false; if (getOptionalValue<bool>(vars, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors) > 0 && ignoreTLSConfigurationErrors) { @@ -2413,7 +2627,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) // and properly ignore the frontend before actually launching it try { std::map<int, std::string> ocspResponses = {}; - auto ctx = libssl_init_server_context(frontend->d_tlsConfig, ocspResponses); + auto ctx = libssl_init_server_context(frontend->d_tlsContext.d_tlsConfig, ocspResponses); } catch (const std::runtime_error& e) { errlog("Ignoring DoH frontend: '%s'", e.what()); @@ -2423,23 +2637,246 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) checkAllParametersConsumed("addDOHLocal", vars); } + + if (useTLS && frontend->d_library == "nghttp2") { + if (!frontend->d_tlsContext.d_provider.empty()) { + vinfolog("Loading TLS provider '%s'", frontend->d_tlsContext.d_provider); + } + else { +#ifdef HAVE_LIBSSL + const std::string provider("openssl"); +#else + const std::string provider("gnutls"); +#endif + vinfolog("Loading default TLS provider '%s'", provider); + } + } + g_dohlocals.push_back(frontend); - auto cs = std::make_unique<ClientState>(frontend->d_local, true, reusePort, tcpFastOpenQueueSize, interface, cpus); - cs->dohFrontend = frontend; - cs->d_additionalAddresses = std::move(additionalAddresses); + auto clientState = std::make_unique<ClientState>(frontend->d_tlsContext.d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + clientState->dohFrontend = std::move(frontend); + clientState->d_additionalAddresses = std::move(additionalAddresses); if (tcpListenQueueSize > 0) { - cs->tcpListenQueueSize = tcpListenQueueSize; + clientState->tcpListenQueueSize = tcpListenQueueSize; } if (tcpMaxConcurrentConnections > 0) { - cs->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections; + clientState->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections; } - g_frontends.push_back(std::move(cs)); -#else + g_frontends.push_back(std::move(clientState)); +#else /* HAVE_DNS_OVER_HTTPS */ throw std::runtime_error("addDOHLocal() called but DNS over HTTPS support is not present!"); +#endif /* HAVE_DNS_OVER_HTTPS */ + }); + + // NOLINTNEXTLINE(performance-unnecessary-value-param): somehow clang-tidy gets confused about the fact vars could be const while it cannot + luaCtx.writeFunction("addDOH3Local", [client](const std::string& addr, const boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>>& certFiles, const boost::variant<std::string, LuaArray<std::string>>& keyFiles, boost::optional<localbind_t> vars) { + if (client) { + return; + } +#ifdef HAVE_DNS_OVER_HTTP3 + if (!checkConfigurationTime("addDOH3Local")) { + return; + } + setLuaSideEffect(); + + auto frontend = std::make_shared<DOH3Frontend>(); + if (!loadTLSCertificateAndKeys("addDOH3Local", frontend->d_quicheParams.d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) { + return; + } + frontend->d_local = ComboAddress(addr, 443); + + bool reusePort = false; + int tcpFastOpenQueueSize = 0; + int tcpListenQueueSize = 0; + uint64_t maxInFlightQueriesPerConn = 0; + uint64_t tcpMaxConcurrentConnections = 0; + std::string interface; + std::set<int> cpus; + std::vector<std::pair<ComboAddress, int>> additionalAddresses; + bool enableProxyProtocol = true; + + if (vars) { + parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol); + if (maxInFlightQueriesPerConn > 0) { + frontend->d_quicheParams.d_maxInFlight = maxInFlightQueriesPerConn; + } + getOptionalValue<int>(vars, "internalPipeBufferSize", frontend->d_internalPipeBufferSize); + getOptionalValue<int>(vars, "idleTimeout", frontend->d_quicheParams.d_idleTimeout); + getOptionalValue<std::string>(vars, "keyLogFile", frontend->d_quicheParams.d_keyLogFile); + { + std::string valueStr; + if (getOptionalValue<std::string>(vars, "congestionControlAlgo", valueStr) > 0) { + if (dnsdist::doq::s_available_cc_algorithms.count(valueStr) > 0) { + frontend->d_quicheParams.d_ccAlgo = valueStr; + } + else { + warnlog("Ignoring unknown value '%s' for 'congestionControlAlgo' on 'addDOH3Local'", valueStr); + } + } + } + parseTLSConfig(frontend->d_quicheParams.d_tlsConfig, "addDOH3Local", vars); + + bool ignoreTLSConfigurationErrors = false; + if (getOptionalValue<bool>(vars, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors) > 0 && ignoreTLSConfigurationErrors) { + // we are asked to try to load the certificates so we can return a potential error + // and properly ignore the frontend before actually launching it + try { + std::map<int, std::string> ocspResponses = {}; + auto ctx = libssl_init_server_context(frontend->d_quicheParams.d_tlsConfig, ocspResponses); + } + catch (const std::runtime_error& e) { + errlog("Ignoring DoH3 frontend: '%s'", e.what()); + return; + } + } + + checkAllParametersConsumed("addDOH3Local", vars); + } + g_doh3locals.push_back(frontend); + auto clientState = std::make_unique<ClientState>(frontend->d_local, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + clientState->doh3Frontend = frontend; + clientState->d_additionalAddresses = std::move(additionalAddresses); + + g_frontends.push_back(std::move(clientState)); +#else + throw std::runtime_error("addDOH3Local() called but DNS over HTTP/3 support is not present!"); #endif }); + // NOLINTNEXTLINE(performance-unnecessary-value-param): somehow clang-tidy gets confused about the fact vars could be const while it cannot + luaCtx.writeFunction("addDOQLocal", [client](const std::string& addr, const boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>>& certFiles, const boost::variant<std::string, LuaArray<std::string>>& keyFiles, boost::optional<localbind_t> vars) { + if (client) { + return; + } +#ifdef HAVE_DNS_OVER_QUIC + if (!checkConfigurationTime("addDOQLocal")) { + return; + } + setLuaSideEffect(); + + auto frontend = std::make_shared<DOQFrontend>(); + if (!loadTLSCertificateAndKeys("addDOQLocal", frontend->d_quicheParams.d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) { + return; + } + frontend->d_local = ComboAddress(addr, 853); + + bool reusePort = false; + int tcpFastOpenQueueSize = 0; + int tcpListenQueueSize = 0; + uint64_t maxInFlightQueriesPerConn = 0; + uint64_t tcpMaxConcurrentConnections = 0; + std::string interface; + std::set<int> cpus; + std::vector<std::pair<ComboAddress, int>> additionalAddresses; + bool enableProxyProtocol = true; + + if (vars) { + parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol); + if (maxInFlightQueriesPerConn > 0) { + frontend->d_quicheParams.d_maxInFlight = maxInFlightQueriesPerConn; + } + getOptionalValue<int>(vars, "internalPipeBufferSize", frontend->d_internalPipeBufferSize); + getOptionalValue<int>(vars, "idleTimeout", frontend->d_quicheParams.d_idleTimeout); + getOptionalValue<std::string>(vars, "keyLogFile", frontend->d_quicheParams.d_keyLogFile); + { + std::string valueStr; + if (getOptionalValue<std::string>(vars, "congestionControlAlgo", valueStr) > 0) { + if (dnsdist::doq::s_available_cc_algorithms.count(valueStr) > 0) { + frontend->d_quicheParams.d_ccAlgo = std::move(valueStr); + } + else { + warnlog("Ignoring unknown value '%s' for 'congestionControlAlgo' on 'addDOQLocal'", valueStr); + } + } + } + parseTLSConfig(frontend->d_quicheParams.d_tlsConfig, "addDOQLocal", vars); + + bool ignoreTLSConfigurationErrors = false; + if (getOptionalValue<bool>(vars, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors) > 0 && ignoreTLSConfigurationErrors) { + // we are asked to try to load the certificates so we can return a potential error + // and properly ignore the frontend before actually launching it + try { + std::map<int, std::string> ocspResponses = {}; + auto ctx = libssl_init_server_context(frontend->d_quicheParams.d_tlsConfig, ocspResponses); + } + catch (const std::runtime_error& e) { + errlog("Ignoring DoQ frontend: '%s'", e.what()); + return; + } + } + + checkAllParametersConsumed("addDOQLocal", vars); + } + g_doqlocals.push_back(frontend); + auto clientState = std::make_unique<ClientState>(frontend->d_local, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + clientState->doqFrontend = std::move(frontend); + clientState->d_additionalAddresses = std::move(additionalAddresses); + + g_frontends.push_back(std::move(clientState)); +#else + throw std::runtime_error("addDOQLocal() called but DNS over QUIC support is not present!"); +#endif + }); + + luaCtx.writeFunction("showDOQFrontends", []() { +#ifdef HAVE_DNS_OVER_QUIC + setLuaNoSideEffect(); + try { + ostringstream ret; + boost::format fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d"); + ret << (fmt % "#" % "Address" % "Bad Version" % "Invalid Token" % "Errors" % "Valid") << endl; + size_t counter = 0; + for (const auto& ctx : g_doqlocals) { + ret << (fmt % counter % ctx->d_local.toStringWithPort() % ctx->d_doqUnsupportedVersionErrors % ctx->d_doqInvalidTokensReceived % ctx->d_errorResponses % ctx->d_validResponses) << endl; + counter++; + } + g_outputBuffer = ret.str(); + } + catch (const std::exception& e) { + g_outputBuffer = e.what(); + throw; + } +#else + g_outputBuffer = "DNS over QUIC support is not present!\n"; +#endif + }); + +#ifdef HAVE_DNS_OVER_QUIC + luaCtx.writeFunction("getDOQFrontend", [client](uint64_t index) { + std::shared_ptr<DOQFrontend> result = nullptr; + if (client) { + return result; + } + setLuaNoSideEffect(); + try { + if (index < g_doqlocals.size()) { + result = g_doqlocals.at(index); + } + else { + errlog("Error: trying to get DOQ frontend with index %d but we only have %d frontend(s)\n", index, g_doqlocals.size()); + g_outputBuffer = "Error: trying to get DOQ frontend with index " + std::to_string(index) + " but we only have " + std::to_string(g_doqlocals.size()) + " frontend(s)\n"; + } + } + catch (const std::exception& e) { + g_outputBuffer = "Error while trying to get DOQ frontend with index " + std::to_string(index) + ": " + string(e.what()) + "\n"; + errlog("Error while trying to get DOQ frontend with index %d: %s\n", index, string(e.what())); + } + return result; + }); + + luaCtx.writeFunction("getDOQFrontendCount", []() { + setLuaNoSideEffect(); + return g_doqlocals.size(); + }); + + luaCtx.registerFunction<void (std::shared_ptr<DOQFrontend>::*)()>("reloadCertificates", [](const std::shared_ptr<DOQFrontend>& frontend) { + if (frontend != nullptr) { + frontend->reloadCertificates(); + } + }); +#endif + luaCtx.writeFunction("showDOHFrontends", []() { #ifdef HAVE_DNS_OVER_HTTPS setLuaNoSideEffect(); @@ -2449,7 +2886,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) ret << (fmt % "#" % "Address" % "HTTP" % "HTTP/1" % "HTTP/2" % "GET" % "POST" % "Bad" % "Errors" % "Redirects" % "Valid" % "# ticket keys" % "Rotation delay" % "Next rotation") << endl; size_t counter = 0; for (const auto& ctx : g_dohlocals) { - ret << (fmt % counter % ctx->d_local.toStringWithPort() % ctx->d_httpconnects % ctx->d_http1Stats.d_nbQueries % ctx->d_http2Stats.d_nbQueries % ctx->d_getqueries % ctx->d_postqueries % ctx->d_badrequests % ctx->d_errorresponses % ctx->d_redirectresponses % ctx->d_validresponses % ctx->getTicketsKeysCount() % ctx->getTicketsKeyRotationDelay() % ctx->getNextTicketsKeyRotation()) << endl; + ret << (fmt % counter % ctx->d_tlsContext.d_addr.toStringWithPort() % ctx->d_httpconnects % ctx->d_http1Stats.d_nbQueries % ctx->d_http2Stats.d_nbQueries % ctx->d_getqueries % ctx->d_postqueries % ctx->d_badrequests % ctx->d_errorresponses % ctx->d_redirectresponses % ctx->d_validresponses % ctx->getTicketsKeysCount() % ctx->getTicketsKeyRotationDelay() % ctx->getNextTicketsKeyRotation()) << endl; counter++; } g_outputBuffer = ret.str(); @@ -2463,6 +2900,64 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) #endif }); + luaCtx.writeFunction("showDOH3Frontends", []() { +#ifdef HAVE_DNS_OVER_HTTP3 + setLuaNoSideEffect(); + try { + ostringstream ret; + boost::format fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d"); + ret << (fmt % "#" % "Address" % "Bad Version" % "Invalid Token" % "Errors" % "Valid") << endl; + size_t counter = 0; + for (const auto& ctx : g_doh3locals) { + ret << (fmt % counter % ctx->d_local.toStringWithPort() % ctx->d_doh3UnsupportedVersionErrors % ctx->d_doh3InvalidTokensReceived % ctx->d_errorResponses % ctx->d_validResponses) << endl; + counter++; + } + g_outputBuffer = ret.str(); + } + catch (const std::exception& e) { + g_outputBuffer = e.what(); + throw; + } +#else + g_outputBuffer = "DNS over HTTP3 support is not present!\n"; +#endif + }); + +#ifdef HAVE_DNS_OVER_HTTP3 + luaCtx.writeFunction("getDOH3Frontend", [client](uint64_t index) { + std::shared_ptr<DOH3Frontend> result = nullptr; + if (client) { + return result; + } + setLuaNoSideEffect(); + try { + if (index < g_doh3locals.size()) { + result = g_doh3locals.at(index); + } + else { + errlog("Error: trying to get DOH3 frontend with index %d but we only have %d frontend(s)\n", index, g_doh3locals.size()); + g_outputBuffer = "Error: trying to get DOH3 frontend with index " + std::to_string(index) + " but we only have " + std::to_string(g_doh3locals.size()) + " frontend(s)\n"; + } + } + catch (const std::exception& e) { + g_outputBuffer = "Error while trying to get DOH3 frontend with index " + std::to_string(index) + ": " + string(e.what()) + "\n"; + errlog("Error while trying to get DOH3 frontend with index %d: %s\n", index, string(e.what())); + } + return result; + }); + + luaCtx.writeFunction("getDOH3FrontendCount", []() { + setLuaNoSideEffect(); + return g_doh3locals.size(); + }); + + luaCtx.registerFunction<void (std::shared_ptr<DOH3Frontend>::*)()>("reloadCertificates", [](const std::shared_ptr<DOH3Frontend>& frontend) { + if (frontend != nullptr) { + frontend->reloadCertificates(); + } + }); +#endif + luaCtx.writeFunction("showDOHResponseCodes", []() { #ifdef HAVE_DNS_OVER_HTTPS setLuaNoSideEffect(); @@ -2473,7 +2968,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) ret << (fmt % "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others") << endl; size_t counter = 0; for (const auto& ctx : g_dohlocals) { - ret << (fmt % counter % ctx->d_local.toStringWithPort() % ctx->d_http1Stats.d_nb200Responses % ctx->d_http1Stats.d_nb400Responses % ctx->d_http1Stats.d_nb403Responses % ctx->d_http1Stats.d_nb500Responses % ctx->d_http1Stats.d_nb502Responses % ctx->d_http1Stats.d_nbOtherResponses) << endl; + ret << (fmt % counter % ctx->d_tlsContext.d_addr.toStringWithPort() % ctx->d_http1Stats.d_nb200Responses % ctx->d_http1Stats.d_nb400Responses % ctx->d_http1Stats.d_nb403Responses % ctx->d_http1Stats.d_nb500Responses % ctx->d_http1Stats.d_nb502Responses % ctx->d_http1Stats.d_nbOtherResponses) << endl; counter++; } g_outputBuffer += ret.str(); @@ -2483,7 +2978,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) ret << (fmt % "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others") << endl; counter = 0; for (const auto& ctx : g_dohlocals) { - ret << (fmt % counter % ctx->d_local.toStringWithPort() % ctx->d_http2Stats.d_nb200Responses % ctx->d_http2Stats.d_nb400Responses % ctx->d_http2Stats.d_nb403Responses % ctx->d_http2Stats.d_nb500Responses % ctx->d_http2Stats.d_nb502Responses % ctx->d_http2Stats.d_nbOtherResponses) << endl; + ret << (fmt % counter % ctx->d_tlsContext.d_addr.toStringWithPort() % ctx->d_http2Stats.d_nb200Responses % ctx->d_http2Stats.d_nb400Responses % ctx->d_http2Stats.d_nb403Responses % ctx->d_http2Stats.d_nb500Responses % ctx->d_http2Stats.d_nb502Responses % ctx->d_http2Stats.d_nbOtherResponses) << endl; counter++; } g_outputBuffer += ret.str(); @@ -2509,13 +3004,13 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) result = g_dohlocals.at(index); } else { - errlog("Error: trying to get DOH frontend with index %zu but we only have %zu frontend(s)\n", index, g_dohlocals.size()); + errlog("Error: trying to get DOH frontend with index %d but we only have %d frontend(s)\n", index, g_dohlocals.size()); g_outputBuffer = "Error: trying to get DOH frontend with index " + std::to_string(index) + " but we only have " + std::to_string(g_dohlocals.size()) + " frontend(s)\n"; } } catch (const std::exception& e) { g_outputBuffer = "Error while trying to get DOH frontend with index " + std::to_string(index) + ": " + string(e.what()) + "\n"; - errlog("Error while trying to get DOH frontend with index %zu: %s\n", index, string(e.what())); + errlog("Error while trying to get DOH frontend with index %d: %s\n", index, string(e.what())); } #else g_outputBuffer="DNS over HTTPS support is not present!\n"; @@ -2528,7 +3023,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) return g_dohlocals.size(); }); - luaCtx.registerFunction<void (std::shared_ptr<DOHFrontend>::*)()>("reloadCertificates", [](std::shared_ptr<DOHFrontend> frontend) { + luaCtx.registerFunction<void (std::shared_ptr<DOHFrontend>::*)()>("reloadCertificates", [](const std::shared_ptr<DOHFrontend>& frontend) { if (frontend != nullptr) { frontend->reloadCertificates(); } @@ -2537,7 +3032,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) luaCtx.registerFunction<void (std::shared_ptr<DOHFrontend>::*)(boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>> certFiles, boost::variant<std::string, LuaArray<std::string>> keyFiles)>("loadNewCertificatesAndKeys", [](std::shared_ptr<DOHFrontend> frontend, boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>> certFiles, boost::variant<std::string, LuaArray<std::string>> keyFiles) { #ifdef HAVE_DNS_OVER_HTTPS if (frontend != nullptr) { - if (loadTLSCertificateAndKeys("DOHFrontend::loadNewCertificatesAndKeys", frontend->d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) { + if (loadTLSCertificateAndKeys("DOHFrontend::loadNewCertificatesAndKeys", frontend->d_tlsContext.d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) { frontend->reloadCertificates(); } } @@ -2579,7 +3074,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) } setLuaSideEffect(); - shared_ptr<TLSFrontend> frontend = std::make_shared<TLSFrontend>(); + auto frontend = std::make_shared<TLSFrontend>(TLSFrontend::ALPN::DoT); if (!loadTLSCertificateAndKeys("addTLSLocal", frontend->d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) { return; } @@ -2592,12 +3087,14 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) std::string interface; std::set<int> cpus; std::vector<std::pair<ComboAddress, int>> additionalAddresses; + bool enableProxyProtocol = true; if (vars) { - parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConns); + parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConns, enableProxyProtocol); getOptionalValue<std::string>(vars, "provider", frontend->d_provider); boost::algorithm::to_lower(frontend->d_provider); + getOptionalValue<bool>(vars, "proxyProtocolOutsideTLS", frontend->d_proxyProtocolOutsideTLS); LuaArray<std::string> addresses; if (getOptionalValue<decltype(addresses)>(vars, "additionalAddresses", addresses) > 0) { @@ -2639,27 +3136,28 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) } else { #ifdef HAVE_LIBSSL - vinfolog("Loading default TLS provider 'openssl'"); + const std::string provider("openssl"); #else - vinfolog("Loading default TLS provider 'gnutls'"); + const std::string provider("gnutls"); #endif + vinfolog("Loading default TLS provider '%s'", provider); } // only works pre-startup, so no sync necessary - auto cs = std::make_unique<ClientState>(frontend->d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus); - cs->tlsFrontend = frontend; - cs->d_additionalAddresses = std::move(additionalAddresses); + auto clientState = std::make_unique<ClientState>(frontend->d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + clientState->tlsFrontend = frontend; + clientState->d_additionalAddresses = std::move(additionalAddresses); if (tcpListenQueueSize > 0) { - cs->tcpListenQueueSize = tcpListenQueueSize; + clientState->tcpListenQueueSize = tcpListenQueueSize; } if (maxInFlightQueriesPerConn > 0) { - cs->d_maxInFlightQueriesPerConn = maxInFlightQueriesPerConn; + clientState->d_maxInFlightQueriesPerConn = maxInFlightQueriesPerConn; } if (tcpMaxConcurrentConns > 0) { - cs->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConns; + clientState->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConns; } - g_tlslocals.push_back(cs->tlsFrontend); - g_frontends.push_back(std::move(cs)); + g_tlslocals.push_back(clientState->tlsFrontend); + g_frontends.push_back(std::move(clientState)); } catch (const std::exception& e) { g_outputBuffer = "Error: " + string(e.what()) + "\n"; @@ -2702,13 +3200,13 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) result = g_tlslocals.at(index)->getContext(); } else { - errlog("Error: trying to get TLS context with index %zu but we only have %zu context(s)\n", index, g_tlslocals.size()); + errlog("Error: trying to get TLS context with index %d but we only have %d context(s)\n", index, g_tlslocals.size()); g_outputBuffer = "Error: trying to get TLS context with index " + std::to_string(index) + " but we only have " + std::to_string(g_tlslocals.size()) + " context(s)\n"; } } catch (const std::exception& e) { g_outputBuffer = "Error while trying to get TLS context with index " + std::to_string(index) + ": " + string(e.what()) + "\n"; - errlog("Error while trying to get TLS context with index %zu: %s\n", index, string(e.what())); + errlog("Error while trying to get TLS context with index %d: %s\n", index, string(e.what())); } #else g_outputBuffer="DNS over TLS support is not present!\n"; @@ -2725,13 +3223,13 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) result = g_tlslocals.at(index); } else { - errlog("Error: trying to get TLS frontend with index %zu but we only have %zu frontends\n", index, g_tlslocals.size()); + errlog("Error: trying to get TLS frontend with index %d but we only have %d frontends\n", index, g_tlslocals.size()); g_outputBuffer = "Error: trying to get TLS frontend with index " + std::to_string(index) + " but we only have " + std::to_string(g_tlslocals.size()) + " frontend(s)\n"; } } catch (const std::exception& e) { g_outputBuffer = "Error while trying to get TLS frontend with index " + std::to_string(index) + ": " + string(e.what()) + "\n"; - errlog("Error while trying to get TLS frontend with index %zu: %s\n", index, string(e.what())); + errlog("Error while trying to get TLS frontend with index %d: %s\n", index, string(e.what())); } #else g_outputBuffer="DNS over TLS support is not present!\n"; @@ -2783,7 +3281,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) } }); - luaCtx.registerFunction<void (std::shared_ptr<TLSFrontend>::*)()>("reloadCertificates", [](std::shared_ptr<TLSFrontend>& frontend) { + luaCtx.registerFunction<void (std::shared_ptr<TLSFrontend>::*)()>("reloadCertificates", [](const std::shared_ptr<TLSFrontend>& frontend) { if (frontend == nullptr) { return; } @@ -2819,6 +3317,16 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) frontend->dohFrontend->reloadCertificates(); } #endif /* HAVE_DNS_OVER_HTTPS */ +#ifdef HAVE_DNS_OVER_QUIC + if (frontend->doqFrontend) { + frontend->doqFrontend->reloadCertificates(); + } +#endif /* HAVE_DNS_OVER_QUIC */ +#ifdef HAVE_DNS_OVER_HTTP3 + if (frontend->doh3Frontend) { + frontend->doh3Frontend->reloadCertificates(); + } +#endif /* HAVE_DNS_OVER_HTTP3 */ } catch (const std::exception& e) { errlog("Error reloading certificates for frontend %s: %s", frontend->local.toStringWithPort(), e.what()); @@ -2918,7 +3426,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) auto result = dnsdist::metrics::declareCustomMetric(name, type, description, customName ? std::optional<std::string>(*customName) : std::nullopt); if (result) { g_outputBuffer += *result + "\n"; - errlog("%s", *result); + errlog("Error in declareMetric: %s", *result); return false; } return true; @@ -2927,7 +3435,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) auto result = dnsdist::metrics::incrementCustomCounter(name, step ? *step : 1); if (const auto* errorStr = std::get_if<dnsdist::metrics::Error>(&result)) { g_outputBuffer = *errorStr + "'\n"; - errlog("%s", *errorStr); + errlog("Error in incMetric: %s", *errorStr); return static_cast<uint64_t>(0); } return std::get<uint64_t>(result); @@ -2936,7 +3444,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) auto result = dnsdist::metrics::decrementCustomCounter(name, step ? *step : 1); if (const auto* errorStr = std::get_if<dnsdist::metrics::Error>(&result)) { g_outputBuffer = *errorStr + "'\n"; - errlog("%s", *errorStr); + errlog("Error in decMetric: %s", *errorStr); return static_cast<uint64_t>(0); } return std::get<uint64_t>(result); @@ -2945,7 +3453,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) auto result = dnsdist::metrics::setCustomGauge(name, value); if (const auto* errorStr = std::get_if<dnsdist::metrics::Error>(&result)) { g_outputBuffer = *errorStr + "'\n"; - errlog("%s", *errorStr); + errlog("Error in setMetric: %s", *errorStr); return 0.; } return std::get<double>(result); @@ -2954,7 +3462,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) auto result = dnsdist::metrics::getCustomMetric(name); if (const auto* errorStr = std::get_if<dnsdist::metrics::Error>(&result)) { g_outputBuffer = *errorStr + "'\n"; - errlog("%s", *errorStr); + errlog("Error in getMetric: %s", *errorStr); return 0.; } return std::get<double>(result); @@ -2969,7 +3477,7 @@ vector<std::function<void(void)>> setupLua(LuaContext& luaCtx, bool client, bool setupLuaActions(luaCtx); setupLuaConfig(luaCtx, client, configCheck); - setupLuaBindings(luaCtx, client); + setupLuaBindings(luaCtx, client, configCheck); setupLuaBindingsDNSCrypt(luaCtx, client); setupLuaBindingsDNSParser(luaCtx); setupLuaBindingsDNSQuestion(luaCtx); @@ -2978,6 +3486,7 @@ vector<std::function<void(void)>> setupLua(LuaContext& luaCtx, bool client, bool setupLuaBindingsPacketCache(luaCtx, client); setupLuaBindingsProtoBuf(luaCtx, client, configCheck); setupLuaBindingsRings(luaCtx, client); + dnsdist::lua::hooks::setupLuaHooks(luaCtx); setupLuaInspection(luaCtx); setupLuaRules(luaCtx); setupLuaVars(luaCtx); |