2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 // for OpenBSD, sys/socket.h needs to come before net/if.h
29 #include <sys/socket.h>
33 #include <sys/types.h>
39 #include "dnsdist-carbon.hh"
40 #include "dnsdist-concurrent-connections.hh"
41 #include "dnsdist-console.hh"
42 #include "dnsdist-crypto.hh"
43 #include "dnsdist-dynblocks.hh"
44 #include "dnsdist-discovery.hh"
45 #include "dnsdist-ecs.hh"
46 #include "dnsdist-healthchecks.hh"
47 #include "dnsdist-lua.hh"
49 #include "dnsdist-lua-ffi.hh"
50 #endif /* LUAJIT_VERSION */
51 #include "dnsdist-metrics.hh"
52 #include "dnsdist-nghttp2.hh"
53 #include "dnsdist-proxy-protocol.hh"
54 #include "dnsdist-rings.hh"
55 #include "dnsdist-secpoll.hh"
56 #include "dnsdist-session-cache.hh"
57 #include "dnsdist-tcp-downstream.hh"
58 #include "dnsdist-web.hh"
61 #include "coverage.hh"
63 #include "doq-common.hh"
65 #include "threadname.hh"
71 #include <boost/logic/tribool.hpp>
72 #include <boost/uuid/string_generator.hpp>
75 #include <systemd/sd-daemon.h>
80 static boost::optional
<std::vector
<std::function
<void(void)>>> g_launchWork
= boost::none
;
82 boost::tribool g_noLuaSideEffect
;
83 static bool g_included
{false};
85 /* this is a best effort way to prevent logging calls with no side-effects in the output of delta()
86 Functions can declare setLuaNoSideEffect() and if nothing else does declare a side effect, or nothing
87 has done so before on this invocation, this call won't be part of delta() output */
88 void setLuaNoSideEffect()
90 if (g_noLuaSideEffect
== false) // there has been a side effect already
92 g_noLuaSideEffect
= true;
95 void setLuaSideEffect()
97 g_noLuaSideEffect
= false;
100 bool getLuaNoSideEffect()
102 if (g_noLuaSideEffect
) {
108 void resetLuaSideEffect()
110 g_noLuaSideEffect
= boost::logic::indeterminate
;
113 using localbind_t
= LuaAssociativeTable
<boost::variant
<bool, int, std::string
, LuaArray
<int>, LuaArray
<std::string
>, LuaAssociativeTable
<std::string
>>>;
115 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
)
118 LuaArray
<int> setCpus
;
120 getOptionalValue
<bool>(vars
, "reusePort", reusePort
);
121 getOptionalValue
<bool>(vars
, "enableProxyProtocol", enableProxyProtocol
);
122 getOptionalValue
<int>(vars
, "tcpFastOpenQueueSize", tcpFastOpenQueueSize
);
123 getOptionalValue
<int>(vars
, "tcpListenQueueSize", tcpListenQueueSize
);
124 getOptionalValue
<int>(vars
, "maxConcurrentTCPConnections", tcpMaxConcurrentConnections
);
125 getOptionalValue
<int>(vars
, "maxInFlight", maxInFlightQueriesPerConnection
);
126 getOptionalValue
<std::string
>(vars
, "interface", interface
);
127 if (getOptionalValue
<decltype(setCpus
)>(vars
, "cpus", setCpus
) > 0) {
128 for (const auto& cpu
: setCpus
) {
129 cpus
.insert(cpu
.second
);
135 #if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS) || defined(HAVE_DNS_OVER_QUIC)
136 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
)
138 if (certFiles
.type() == typeid(std::string
) && keyFiles
.type() == typeid(std::string
)) {
139 auto certFile
= boost::get
<std::string
>(certFiles
);
140 auto keyFile
= boost::get
<std::string
>(keyFiles
);
142 pairs
.emplace_back(certFile
, keyFile
);
144 else if (certFiles
.type() == typeid(std::shared_ptr
<TLSCertKeyPair
>)) {
145 auto cert
= boost::get
<std::shared_ptr
<TLSCertKeyPair
>>(certFiles
);
147 pairs
.emplace_back(*cert
);
149 else if (certFiles
.type() == typeid(LuaArray
<std::shared_ptr
<TLSCertKeyPair
>>)) {
150 auto certs
= boost::get
<LuaArray
<std::shared_ptr
<TLSCertKeyPair
>>>(certFiles
);
152 for (const auto& cert
: certs
) {
153 pairs
.emplace_back(*(cert
.second
));
156 else if (certFiles
.type() == typeid(LuaArray
<std::string
>) && keyFiles
.type() == typeid(LuaArray
<std::string
>)) {
157 auto certFilesVect
= boost::get
<LuaArray
<std::string
>>(certFiles
);
158 auto keyFilesVect
= boost::get
<LuaArray
<std::string
>>(keyFiles
);
159 if (certFilesVect
.size() == keyFilesVect
.size()) {
161 for (size_t idx
= 0; idx
< certFilesVect
.size(); idx
++) {
162 pairs
.emplace_back(certFilesVect
.at(idx
).second
, keyFilesVect
.at(idx
).second
);
166 errlog("Error, mismatching number of certificates and keys in call to %s()!", context
);
167 g_outputBuffer
= "Error, mismatching number of certificates and keys in call to " + context
+ "()!";
172 errlog("Error, mismatching number of certificates and keys in call to %s()!", context
);
173 g_outputBuffer
= "Error, mismatching number of certificates and keys in call to " + context
+ "()!";
180 static void parseTLSConfig(TLSConfig
& config
, const std::string
& context
, boost::optional
<localbind_t
>& vars
)
182 getOptionalValue
<std::string
>(vars
, "ciphers", config
.d_ciphers
);
183 getOptionalValue
<std::string
>(vars
, "ciphersTLS13", config
.d_ciphers13
);
186 std::string minVersion
;
187 if (getOptionalValue
<std::string
>(vars
, "minTLSVersion", minVersion
) > 0) {
188 config
.d_minTLSVersion
= libssl_tls_version_from_string(minVersion
);
190 #else /* HAVE_LIBSSL */
191 if (vars
->erase("minTLSVersion") > 0)
192 warnlog("minTLSVersion has no effect with chosen TLS library");
193 #endif /* HAVE_LIBSSL */
195 getOptionalValue
<std::string
>(vars
, "ticketKeyFile", config
.d_ticketKeyFile
);
196 getOptionalValue
<int>(vars
, "ticketsKeysRotationDelay", config
.d_ticketsKeyRotationDelay
);
197 getOptionalValue
<int>(vars
, "numberOfTicketsKeys", config
.d_numberOfTicketsKeys
);
198 getOptionalValue
<bool>(vars
, "preferServerCiphers", config
.d_preferServerCiphers
);
199 getOptionalValue
<int>(vars
, "sessionTimeout", config
.d_sessionTimeout
);
200 getOptionalValue
<bool>(vars
, "sessionTickets", config
.d_enableTickets
);
201 int numberOfStoredSessions
{0};
202 if (getOptionalValue
<int>(vars
, "numberOfStoredSessions", numberOfStoredSessions
) > 0) {
203 if (numberOfStoredSessions
< 0) {
204 errlog("Invalid value '%d' for %s() parameter 'numberOfStoredSessions', should be >= 0, dismissing", numberOfStoredSessions
, context
);
205 g_outputBuffer
= "Invalid value '" + std::to_string(numberOfStoredSessions
) + "' for " + context
+ "() parameter 'numberOfStoredSessions', should be >= 0, dimissing";
208 config
.d_maxStoredSessions
= numberOfStoredSessions
;
212 LuaArray
<std::string
> files
;
213 if (getOptionalValue
<decltype(files
)>(vars
, "ocspResponses", files
) > 0) {
214 for (const auto& file
: files
) {
215 config
.d_ocspFiles
.push_back(file
.second
);
219 if (vars
->count("keyLogFile") > 0) {
220 #ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
221 getOptionalValue
<std::string
>(vars
, "keyLogFile", config
.d_keyLogFile
);
223 errlog("TLS Key logging has been enabled using the 'keyLogFile' parameter to %s(), but this version of OpenSSL does not support it", context
);
224 g_outputBuffer
= "TLS Key logging has been enabled using the 'keyLogFile' parameter to " + context
+ "(), but this version of OpenSSL does not support it";
228 getOptionalValue
<bool>(vars
, "releaseBuffers", config
.d_releaseBuffers
);
229 getOptionalValue
<bool>(vars
, "enableRenegotiation", config
.d_enableRenegotiation
);
230 getOptionalValue
<bool>(vars
, "tlsAsyncMode", config
.d_asyncMode
);
231 getOptionalValue
<bool>(vars
, "ktls", config
.d_ktls
);
232 getOptionalValue
<bool>(vars
, "readAhead", config
.d_readAhead
);
235 #endif // defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
237 void checkParameterBound(const std::string
& parameter
, uint64_t value
, size_t max
)
240 throw std::runtime_error("The value (" + std::to_string(value
) + ") passed to " + parameter
+ " is too large, the maximum is " + std::to_string(max
));
244 static void LuaThread(const std::string
& code
)
246 setThreadName("dnsdist/lua-bg");
249 // mask SIGTERM on threads so the signal always comes to dnsdist itself
250 sigset_t blockSignals
;
252 sigemptyset(&blockSignals
);
253 sigaddset(&blockSignals
, SIGTERM
);
255 pthread_sigmask(SIG_BLOCK
, &blockSignals
, nullptr);
257 // submitToMainThread is camelcased, threadmessage is not.
258 // This follows our tradition of hooks we call being lowercased but functions the user can call being camelcased.
259 l
.writeFunction("submitToMainThread", [](std::string cmd
, LuaAssociativeTable
<std::string
> data
) {
260 auto lua
= g_lua
.lock();
261 // maybe offer more than `void`
262 auto func
= lua
->readVariable
<boost::optional
<std::function
<void(std::string cmd
, LuaAssociativeTable
<std::string
> data
)>>>("threadmessage");
264 func
.get()(std::move(cmd
), std::move(data
));
267 errlog("Lua thread called submitToMainThread but no threadmessage receiver is defined");
271 // function threadmessage(cmd, data) print("got thread data:", cmd) for k,v in pairs(data) do print(k,v) end end
276 errlog("Lua thread exited, restarting in 5 seconds");
278 catch (const std::exception
& e
) {
279 errlog("Lua thread crashed, restarting in 5 seconds: %s", e
.what());
282 errlog("Lua thread crashed, restarting in 5 seconds");
288 static bool checkConfigurationTime(const std::string
& name
)
290 if (!g_configurationDone
) {
293 g_outputBuffer
= name
+ " cannot be used at runtime!\n";
294 errlog("%s cannot be used at runtime!", name
);
298 // NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold
299 static void setupLuaConfig(LuaContext
& luaCtx
, bool client
, bool configCheck
)
301 typedef LuaAssociativeTable
<boost::variant
<bool, std::string
, LuaArray
<std::string
>, DownstreamState::checkfunc_t
>> newserver_t
;
302 luaCtx
.writeFunction("inClientStartup", [client
]() {
303 return client
&& !g_configurationDone
;
306 luaCtx
.writeFunction("inConfigCheck", [configCheck
]() {
310 luaCtx
.writeFunction("newServer",
311 [client
, configCheck
](boost::variant
<string
, newserver_t
> pvars
, boost::optional
<int> qps
) {
314 boost::optional
<newserver_t
> vars
= newserver_t();
315 DownstreamState::Config config
;
317 std::string serverAddressStr
;
318 if (auto addrStr
= boost::get
<string
>(&pvars
)) {
319 serverAddressStr
= *addrStr
;
321 (*vars
)["qps"] = std::to_string(*qps
);
325 vars
= boost::get
<newserver_t
>(pvars
);
326 getOptionalValue
<std::string
>(vars
, "address", serverAddressStr
);
330 if (getOptionalValue
<std::string
>(vars
, "source", source
) > 0) {
331 /* handle source in the following forms:
332 - v4 address ("192.0.2.1")
333 - v6 address ("2001:DB8::1")
334 - interface name ("eth0")
335 - v4 address and interface name ("192.0.2.1@eth0")
336 - v6 address and interface name ("2001:DB8::1@eth0")
339 std::string::size_type pos
= source
.find("@");
340 if (pos
== std::string::npos
) {
341 /* no '@', try to parse that as a valid v4/v6 address */
343 config
.sourceAddr
= ComboAddress(source
);
350 if (parsed
== false) {
351 /* try to parse as interface name, or v4/v6@itf */
352 config
.sourceItfName
= source
.substr(pos
== std::string::npos
? 0 : pos
+ 1);
353 unsigned int itfIdx
= if_nametoindex(config
.sourceItfName
.c_str());
355 if (pos
== 0 || pos
== std::string::npos
) {
356 /* "eth0" or "@eth0" */
357 config
.sourceItf
= itfIdx
;
360 /* "192.0.2.1@eth0" */
361 config
.sourceAddr
= ComboAddress(source
.substr(0, pos
));
362 config
.sourceItf
= itfIdx
;
364 #ifdef SO_BINDTODEVICE
365 /* we need to retain CAP_NET_RAW to be able to set SO_BINDTODEVICE in the health checks */
366 g_capabilitiesToRetain
.insert("CAP_NET_RAW");
370 warnlog("Dismissing source %s because '%s' is not a valid interface name", source
, config
.sourceItfName
);
375 std::string valueStr
;
376 if (getOptionalValue
<std::string
>(vars
, "sockets", valueStr
) > 0) {
377 config
.d_numberOfSockets
= std::stoul(valueStr
);
378 if (config
.d_numberOfSockets
== 0) {
379 warnlog("Dismissing invalid number of sockets '%s', using 1 instead", valueStr
);
380 config
.d_numberOfSockets
= 1;
384 getOptionalIntegerValue("newServer", vars
, "qps", config
.d_qpsLimit
);
385 getOptionalIntegerValue("newServer", vars
, "order", config
.order
);
386 getOptionalIntegerValue("newServer", vars
, "weight", config
.d_weight
);
387 if (config
.d_weight
< 1) {
388 errlog("Error creating new server: downstream weight value must be greater than 0.");
389 return std::shared_ptr
<DownstreamState
>();
392 getOptionalIntegerValue("newServer", vars
, "retries", config
.d_retries
);
393 getOptionalIntegerValue("newServer", vars
, "tcpConnectTimeout", config
.tcpConnectTimeout
);
394 getOptionalIntegerValue("newServer", vars
, "tcpSendTimeout", config
.tcpSendTimeout
);
395 getOptionalIntegerValue("newServer", vars
, "tcpRecvTimeout", config
.tcpRecvTimeout
);
397 if (getOptionalValue
<std::string
>(vars
, "checkInterval", valueStr
) > 0) {
398 config
.checkInterval
= static_cast<unsigned int>(std::stoul(valueStr
));
401 bool fastOpen
{false};
402 if (getOptionalValue
<bool>(vars
, "tcpFastOpen", fastOpen
) > 0) {
405 config
.tcpFastOpen
= true;
407 warnlog("TCP Fast Open has been configured on downstream server %s but is not supported", serverAddressStr
);
412 getOptionalIntegerValue("newServer", vars
, "maxInFlight", config
.d_maxInFlightQueriesPerConn
);
413 getOptionalIntegerValue("newServer", vars
, "maxConcurrentTCPConnections", config
.d_tcpConcurrentConnectionsLimit
);
415 getOptionalValue
<std::string
>(vars
, "name", config
.name
);
417 if (getOptionalValue
<std::string
>(vars
, "id", valueStr
) > 0) {
418 config
.id
= boost::uuids::string_generator()(valueStr
);
421 if (getOptionalValue
<std::string
>(vars
, "healthCheckMode", valueStr
) > 0) {
422 const auto& mode
= valueStr
;
423 if (pdns_iequals(mode
, "auto")) {
424 config
.availability
= DownstreamState::Availability::Auto
;
426 else if (pdns_iequals(mode
, "lazy")) {
427 config
.availability
= DownstreamState::Availability::Lazy
;
429 else if (pdns_iequals(mode
, "up")) {
430 config
.availability
= DownstreamState::Availability::Up
;
432 else if (pdns_iequals(mode
, "down")) {
433 config
.availability
= DownstreamState::Availability::Down
;
436 warnlog("Ignoring unknown value '%s' for 'healthCheckMode' on 'newServer'", mode
);
440 if (getOptionalValue
<std::string
>(vars
, "checkName", valueStr
) > 0) {
441 config
.checkName
= DNSName(valueStr
);
444 getOptionalValue
<std::string
>(vars
, "checkType", config
.checkType
);
445 getOptionalIntegerValue("newServer", vars
, "checkClass", config
.checkClass
);
446 getOptionalValue
<DownstreamState::checkfunc_t
>(vars
, "checkFunction", config
.checkFunction
);
447 getOptionalIntegerValue("newServer", vars
, "checkTimeout", config
.checkTimeout
);
448 getOptionalValue
<bool>(vars
, "checkTCP", config
.d_tcpCheck
);
449 getOptionalValue
<bool>(vars
, "setCD", config
.setCD
);
450 getOptionalValue
<bool>(vars
, "mustResolve", config
.mustResolve
);
452 if (getOptionalValue
<std::string
>(vars
, "lazyHealthCheckSampleSize", valueStr
) > 0) {
453 const auto& value
= std::stoi(valueStr
);
454 checkParameterBound("lazyHealthCheckSampleSize", value
);
455 config
.d_lazyHealthCheckSampleSize
= value
;
458 if (getOptionalValue
<std::string
>(vars
, "lazyHealthCheckMinSampleCount", valueStr
) > 0) {
459 const auto& value
= std::stoi(valueStr
);
460 checkParameterBound("lazyHealthCheckMinSampleCount", value
);
461 config
.d_lazyHealthCheckMinSampleCount
= value
;
464 if (getOptionalValue
<std::string
>(vars
, "lazyHealthCheckThreshold", valueStr
) > 0) {
465 const auto& value
= std::stoi(valueStr
);
466 checkParameterBound("lazyHealthCheckThreshold", value
, std::numeric_limits
<uint8_t>::max());
467 config
.d_lazyHealthCheckThreshold
= value
;
470 if (getOptionalValue
<std::string
>(vars
, "lazyHealthCheckFailedInterval", valueStr
) > 0) {
471 const auto& value
= std::stoi(valueStr
);
472 checkParameterBound("lazyHealthCheckFailedInterval", value
);
473 config
.d_lazyHealthCheckFailedInterval
= value
;
476 getOptionalValue
<bool>(vars
, "lazyHealthCheckUseExponentialBackOff", config
.d_lazyHealthCheckUseExponentialBackOff
);
478 if (getOptionalValue
<std::string
>(vars
, "lazyHealthCheckMaxBackOff", valueStr
) > 0) {
479 const auto& value
= std::stoi(valueStr
);
480 checkParameterBound("lazyHealthCheckMaxBackOff", value
);
481 config
.d_lazyHealthCheckMaxBackOff
= value
;
484 if (getOptionalValue
<std::string
>(vars
, "lazyHealthCheckMode", valueStr
) > 0) {
485 const auto& mode
= valueStr
;
486 if (pdns_iequals(mode
, "TimeoutOnly")) {
487 config
.d_lazyHealthCheckMode
= DownstreamState::LazyHealthCheckMode::TimeoutOnly
;
489 else if (pdns_iequals(mode
, "TimeoutOrServFail")) {
490 config
.d_lazyHealthCheckMode
= DownstreamState::LazyHealthCheckMode::TimeoutOrServFail
;
493 warnlog("Ignoring unknown value '%s' for 'lazyHealthCheckMode' on 'newServer'", mode
);
497 getOptionalValue
<bool>(vars
, "lazyHealthCheckWhenUpgraded", config
.d_upgradeToLazyHealthChecks
);
499 getOptionalValue
<bool>(vars
, "useClientSubnet", config
.useECS
);
500 getOptionalValue
<bool>(vars
, "useProxyProtocol", config
.useProxyProtocol
);
501 getOptionalValue
<bool>(vars
, "proxyProtocolAdvertiseTLS", config
.d_proxyProtocolAdvertiseTLS
);
502 getOptionalValue
<bool>(vars
, "disableZeroScope", config
.disableZeroScope
);
503 getOptionalValue
<bool>(vars
, "ipBindAddrNoPort", config
.ipBindAddrNoPort
);
505 getOptionalIntegerValue("newServer", vars
, "addXPF", config
.xpfRRCode
);
506 getOptionalIntegerValue("newServer", vars
, "maxCheckFailures", config
.maxCheckFailures
);
507 getOptionalIntegerValue("newServer", vars
, "rise", config
.minRiseSuccesses
);
509 getOptionalValue
<bool>(vars
, "reconnectOnUp", config
.reconnectOnUp
);
511 LuaArray
<string
> cpuMap
;
512 if (getOptionalValue
<decltype(cpuMap
)>(vars
, "cpus", cpuMap
) > 0) {
513 for (const auto& cpu
: cpuMap
) {
514 config
.d_cpus
.insert(std::stoi(cpu
.second
));
518 getOptionalValue
<bool>(vars
, "tcpOnly", config
.d_tcpOnly
);
520 std::shared_ptr
<TLSCtx
> tlsCtx
;
521 getOptionalValue
<std::string
>(vars
, "ciphers", config
.d_tlsParams
.d_ciphers
);
522 getOptionalValue
<std::string
>(vars
, "ciphers13", config
.d_tlsParams
.d_ciphers13
);
523 getOptionalValue
<std::string
>(vars
, "caStore", config
.d_tlsParams
.d_caStore
);
524 getOptionalValue
<bool>(vars
, "validateCertificates", config
.d_tlsParams
.d_validateCertificates
);
525 getOptionalValue
<bool>(vars
, "releaseBuffers", config
.d_tlsParams
.d_releaseBuffers
);
526 getOptionalValue
<bool>(vars
, "enableRenegotiation", config
.d_tlsParams
.d_enableRenegotiation
);
527 getOptionalValue
<bool>(vars
, "ktls", config
.d_tlsParams
.d_ktls
);
528 getOptionalValue
<std::string
>(vars
, "subjectName", config
.d_tlsSubjectName
);
530 if (getOptionalValue
<std::string
>(vars
, "subjectAddr", valueStr
) > 0) {
532 ComboAddress
ca(valueStr
);
533 config
.d_tlsSubjectName
= ca
.toString();
534 config
.d_tlsSubjectIsAddr
= true;
536 catch (const std::exception
& e
) {
537 errlog("Error creating new server: downstream subjectAddr value must be a valid IP address");
538 return std::shared_ptr
<DownstreamState
>();
542 uint16_t serverPort
= 53;
544 if (getOptionalValue
<std::string
>(vars
, "tls", valueStr
) > 0) {
546 config
.d_tlsParams
.d_provider
= valueStr
;
547 tlsCtx
= getTLSContext(config
.d_tlsParams
);
549 if (getOptionalValue
<std::string
>(vars
, "dohPath", valueStr
) > 0) {
550 #if !defined(HAVE_DNS_OVER_HTTPS) || !defined(HAVE_NGHTTP2)
551 throw std::runtime_error("Outgoing DNS over HTTPS support requested (via 'dohPath' on newServer()) but it is not available");
555 config
.d_dohPath
= valueStr
;
557 getOptionalValue
<bool>(vars
, "addXForwardedHeaders", config
.d_addXForwardedHeaders
);
562 config
.remote
= ComboAddress(serverAddressStr
, serverPort
);
564 catch (const PDNSException
& e
) {
565 g_outputBuffer
= "Error creating new server: " + string(e
.reason
);
566 errlog("Error creating new server with address %s: %s", serverAddressStr
, e
.reason
);
567 return std::shared_ptr
<DownstreamState
>();
569 catch (const std::exception
& e
) {
570 g_outputBuffer
= "Error creating new server: " + string(e
.what());
571 errlog("Error creating new server with address %s: %s", serverAddressStr
, e
.what());
572 return std::shared_ptr
<DownstreamState
>();
575 if (IsAnyAddress(config
.remote
)) {
576 g_outputBuffer
= "Error creating new server: invalid address for a downstream server.";
577 errlog("Error creating new server: %s is not a valid address for a downstream server", serverAddressStr
);
578 return std::shared_ptr
<DownstreamState
>();
581 LuaArray
<std::string
> pools
;
582 if (getOptionalValue
<std::string
>(vars
, "pool", valueStr
, false) > 0) {
583 config
.pools
.insert(valueStr
);
585 else if (getOptionalValue
<decltype(pools
)>(vars
, "pool", pools
) > 0) {
586 for (auto& p
: pools
) {
587 config
.pools
.insert(p
.second
);
591 bool autoUpgrade
= false;
592 bool keepAfterUpgrade
= false;
593 uint32_t upgradeInterval
= 3600;
594 uint16_t upgradeDoHKey
= dnsdist::ServiceDiscovery::s_defaultDoHSVCKey
;
595 std::string upgradePool
;
597 getOptionalValue
<bool>(vars
, "autoUpgrade", autoUpgrade
);
599 if (getOptionalValue
<std::string
>(vars
, "autoUpgradeInterval", valueStr
) > 0) {
601 upgradeInterval
= static_cast<uint32_t>(std::stoul(valueStr
));
603 catch (const std::exception
& e
) {
604 warnlog("Error parsing 'autoUpgradeInterval' value: %s", e
.what());
607 getOptionalValue
<bool>(vars
, "autoUpgradeKeep", keepAfterUpgrade
);
608 getOptionalValue
<std::string
>(vars
, "autoUpgradePool", upgradePool
);
609 if (getOptionalValue
<std::string
>(vars
, "autoUpgradeDoHKey", valueStr
) > 0) {
611 upgradeDoHKey
= static_cast<uint16_t>(std::stoul(valueStr
));
613 catch (const std::exception
& e
) {
614 warnlog("Error parsing 'autoUpgradeDoHKey' value: %s", e
.what());
619 // create but don't connect the socket in client or check-config modes
620 auto ret
= std::make_shared
<DownstreamState
>(std::move(config
), std::move(tlsCtx
), !(client
|| configCheck
));
621 if (!(client
|| configCheck
)) {
622 infolog("Added downstream server %s", ret
->d_config
.remote
.toStringWithPort());
625 if (autoUpgrade
&& ret
->getProtocol() != dnsdist::Protocol::DoT
&& ret
->getProtocol() != dnsdist::Protocol::DoH
) {
626 dnsdist::ServiceDiscovery::addUpgradeableServer(ret
, upgradeInterval
, upgradePool
, upgradeDoHKey
, keepAfterUpgrade
);
629 /* this needs to be done _AFTER_ the order has been set,
630 since the server are kept ordered inside the pool */
631 auto localPools
= g_pools
.getCopy();
632 if (!ret
->d_config
.pools
.empty()) {
633 for (const auto& poolName
: ret
->d_config
.pools
) {
634 addServerToPool(localPools
, poolName
, ret
);
638 addServerToPool(localPools
, "", ret
);
640 g_pools
.setState(localPools
);
642 if (ret
->connected
) {
644 g_launchWork
->push_back([ret
]() {
653 auto states
= g_dstates
.getCopy();
654 states
.push_back(ret
);
655 std::stable_sort(states
.begin(), states
.end(), [](const decltype(ret
)& a
, const decltype(ret
)& b
) {
656 return a
->d_config
.order
< b
->d_config
.order
;
658 g_dstates
.setState(states
);
659 checkAllParametersConsumed("newServer", vars
);
663 luaCtx
.writeFunction("rmServer",
664 [](boost::variant
<std::shared_ptr
<DownstreamState
>, int, std::string
> var
) {
666 shared_ptr
<DownstreamState
> server
= nullptr;
667 auto states
= g_dstates
.getCopy();
668 if (auto* rem
= boost::get
<shared_ptr
<DownstreamState
>>(&var
)) {
671 else if (auto str
= boost::get
<std::string
>(&var
)) {
672 const auto uuid
= getUniqueID(*str
);
673 for (auto& state
: states
) {
674 if (*state
->d_config
.id
== uuid
) {
680 int idx
= boost::get
<int>(var
);
681 server
= states
.at(idx
);
684 throw std::runtime_error("unable to locate the requested server");
686 auto localPools
= g_pools
.getCopy();
687 for (const string
& poolName
: server
->d_config
.pools
) {
688 removeServerFromPool(localPools
, poolName
, server
);
690 /* the server might also be in the default pool */
691 removeServerFromPool(localPools
, "", server
);
692 g_pools
.setState(localPools
);
693 states
.erase(remove(states
.begin(), states
.end(), server
), states
.end());
694 g_dstates
.setState(states
);
698 luaCtx
.writeFunction("truncateTC", [](bool tc
) { setLuaSideEffect(); g_truncateTC
=tc
; });
699 luaCtx
.writeFunction("fixupCase", [](bool fu
) { setLuaSideEffect(); g_fixupCase
=fu
; });
701 luaCtx
.writeFunction("addACL", [](const std::string
& domain
) {
703 g_ACL
.modify([domain
](NetmaskGroup
& nmg
) { nmg
.addMask(domain
); });
706 luaCtx
.writeFunction("rmACL", [](const std::string
& netmask
) {
708 g_ACL
.modify([netmask
](NetmaskGroup
& nmg
) { nmg
.deleteMask(netmask
); });
711 luaCtx
.writeFunction("setLocal", [client
](const std::string
& addr
, boost::optional
<localbind_t
> vars
) {
717 if (!checkConfigurationTime("setLocal")) {
721 bool reusePort
= false;
722 int tcpFastOpenQueueSize
= 0;
723 int tcpListenQueueSize
= 0;
724 uint64_t maxInFlightQueriesPerConn
= 0;
725 uint64_t tcpMaxConcurrentConnections
= 0;
726 std::string interface
;
728 bool enableProxyProtocol
= true;
730 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConnections
, enableProxyProtocol
);
732 checkAllParametersConsumed("setLocal", vars
);
735 ComboAddress
loc(addr
, 53);
736 for (auto it
= g_frontends
.begin(); it
!= g_frontends
.end();) {
737 /* DoH, DoT and DNSCrypt frontends are separate */
738 if ((*it
)->tlsFrontend
== nullptr && (*it
)->dnscryptCtx
== nullptr && (*it
)->dohFrontend
== nullptr) {
739 it
= g_frontends
.erase(it
);
746 // only works pre-startup, so no sync necessary
747 g_frontends
.push_back(std::make_unique
<ClientState
>(loc
, false, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, enableProxyProtocol
));
748 auto tcpCS
= std::make_unique
<ClientState
>(loc
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, enableProxyProtocol
);
749 if (tcpListenQueueSize
> 0) {
750 tcpCS
->tcpListenQueueSize
= tcpListenQueueSize
;
752 if (maxInFlightQueriesPerConn
> 0) {
753 tcpCS
->d_maxInFlightQueriesPerConn
= maxInFlightQueriesPerConn
;
755 if (tcpMaxConcurrentConnections
> 0) {
756 tcpCS
->d_tcpConcurrentConnectionsLimit
= tcpMaxConcurrentConnections
;
759 g_frontends
.push_back(std::move(tcpCS
));
761 catch (const std::exception
& e
) {
762 g_outputBuffer
= "Error: " + string(e
.what()) + "\n";
766 luaCtx
.writeFunction("addLocal", [client
](const std::string
& addr
, boost::optional
<localbind_t
> vars
) {
771 if (!checkConfigurationTime("addLocal")) {
774 bool reusePort
= false;
775 int tcpFastOpenQueueSize
= 0;
776 int tcpListenQueueSize
= 0;
777 uint64_t maxInFlightQueriesPerConn
= 0;
778 uint64_t tcpMaxConcurrentConnections
= 0;
779 std::string interface
;
781 bool enableProxyProtocol
= true;
783 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConnections
, enableProxyProtocol
);
784 checkAllParametersConsumed("addLocal", vars
);
787 ComboAddress
loc(addr
, 53);
788 // only works pre-startup, so no sync necessary
789 g_frontends
.push_back(std::make_unique
<ClientState
>(loc
, false, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, enableProxyProtocol
));
790 auto tcpCS
= std::make_unique
<ClientState
>(loc
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, enableProxyProtocol
);
791 if (tcpListenQueueSize
> 0) {
792 tcpCS
->tcpListenQueueSize
= tcpListenQueueSize
;
794 if (maxInFlightQueriesPerConn
> 0) {
795 tcpCS
->d_maxInFlightQueriesPerConn
= maxInFlightQueriesPerConn
;
797 if (tcpMaxConcurrentConnections
> 0) {
798 tcpCS
->d_tcpConcurrentConnectionsLimit
= tcpMaxConcurrentConnections
;
800 g_frontends
.push_back(std::move(tcpCS
));
802 catch (std::exception
& e
) {
803 g_outputBuffer
= "Error: " + string(e
.what()) + "\n";
804 errlog("Error while trying to listen on %s: %s\n", addr
, string(e
.what()));
808 luaCtx
.writeFunction("setACL", [](LuaTypeOrArrayOf
<std::string
> inp
) {
811 if (auto str
= boost::get
<string
>(&inp
)) {
815 for (const auto& p
: boost::get
<LuaArray
<std::string
>>(inp
)) {
816 nmg
.addMask(p
.second
);
821 luaCtx
.writeFunction("setACLFromFile", [](const std::string
& file
) {
827 throw std::runtime_error("Could not open '" + file
+ "': " + stringerror());
830 string::size_type pos
;
832 while (getline(ifs
, line
)) {
833 pos
= line
.find('#');
834 if (pos
!= string::npos
)
846 luaCtx
.writeFunction("showACL", []() {
847 setLuaNoSideEffect();
848 auto aclEntries
= g_ACL
.getLocal()->toStringVector();
850 for (const auto& entry
: aclEntries
) {
851 g_outputBuffer
+= entry
+ "\n";
855 luaCtx
.writeFunction("shutdown", []() {
857 sd_notify(0, "STOPPING=1");
858 #endif /* HAVE_SYSTEMD */
860 // Useful for debugging leaks, but might lead to race under load
861 // since other threads are still running.
862 for (auto& frontend
: g_tlslocals
) {
868 pdns::coverage::dumpCoverageData();
872 typedef LuaAssociativeTable
<boost::variant
<bool, std::string
>> showserversopts_t
;
874 luaCtx
.writeFunction("showServers", [](boost::optional
<showserversopts_t
> vars
) {
875 setLuaNoSideEffect();
876 bool showUUIDs
= false;
877 getOptionalValue
<bool>(vars
, "showUUIDs", showUUIDs
);
878 checkAllParametersConsumed("showServers", vars
);
884 auto latFmt
= boost::format("%5.1f");
886 fmt
= boost::format("%1$-3d %15$-36s %2$-20.20s %|62t|%3% %|107t|%4$5s %|88t|%5$7.1f %|103t|%6$7d %|106t|%7$10d %|115t|%8$10d %|117t|%9$10d %|123t|%10$7d %|128t|%11$5.1f %|146t|%12$5s %|152t|%16$5s %|158t|%13$11d %14%");
887 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 (tcp latency)
888 ret
<< (fmt
% "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools" % "UUID" % "TCP") << endl
;
891 fmt
= boost::format("%1$-3d %2$-20.20s %|25t|%3% %|70t|%4$5s %|51t|%5$7.1f %|66t|%6$7d %|69t|%7$10d %|78t|%8$10d %|80t|%9$10d %|86t|%10$7d %|91t|%11$5.1f %|109t|%12$5s %|115t|%15$5s %|121t|%13$11d %14%");
892 ret
<< (fmt
% "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools" % "TCP") << endl
;
895 uint64_t totQPS
{0}, totQueries
{0}, totDrops
{0};
897 auto states
= g_dstates
.getLocal();
898 for (const auto& s
: *states
) {
899 string status
= s
->getStatus();
901 for (const auto& p
: s
->d_config
.pools
) {
902 if (!pools
.empty()) {
907 const std::string latency
= (s
->latencyUsec
== 0.0 ? "-" : boost::str(latFmt
% (s
->latencyUsec
/ 1000.0)));
908 const std::string latencytcp
= (s
->latencyUsecTCP
== 0.0 ? "-" : boost::str(latFmt
% (s
->latencyUsecTCP
/ 1000.0)));
910 ret
<< (fmt
% counter
% s
->getName() % s
->d_config
.remote
.toStringWithPort() % status
% s
->queryLoad
% s
->qps
.getRate() % s
->d_config
.order
% s
->d_config
.d_weight
% s
->queries
.load() % s
->reuseds
.load() % (s
->dropRate
) % latency
% s
->outstanding
.load() % pools
% *s
->d_config
.id
% latencytcp
) << endl
;
913 ret
<< (fmt
% counter
% s
->getName() % s
->d_config
.remote
.toStringWithPort() % status
% s
->queryLoad
% s
->qps
.getRate() % s
->d_config
.order
% s
->d_config
.d_weight
% s
->queries
.load() % s
->reuseds
.load() % (s
->dropRate
) % latency
% s
->outstanding
.load() % pools
% latencytcp
) << endl
;
915 totQPS
+= s
->queryLoad
;
916 totQueries
+= s
->queries
.load();
917 totDrops
+= s
->reuseds
.load();
921 ret
<< (fmt
% "All" % "" % "" % ""
922 % (double)totQPS
% "" % "" % "" % totQueries
% totDrops
% "" % "" % "" % "" % "" % "")
926 ret
<< (fmt
% "All" % "" % "" % ""
927 % (double)totQPS
% "" % "" % "" % totQueries
% totDrops
% "" % "" % "" % "" % "")
931 g_outputBuffer
= ret
.str();
933 catch (std::exception
& e
) {
934 g_outputBuffer
= e
.what();
939 luaCtx
.writeFunction("getServers", []() {
940 setLuaNoSideEffect();
941 LuaArray
<std::shared_ptr
<DownstreamState
>> ret
;
943 for (const auto& s
: g_dstates
.getCopy()) {
944 ret
.emplace_back(count
++, s
);
949 luaCtx
.writeFunction("getPoolServers", [](const string
& pool
) {
950 const auto poolServers
= getDownstreamCandidates(g_pools
.getCopy(), pool
);
954 luaCtx
.writeFunction("getServer", [client
](boost::variant
<int, std::string
> i
) {
956 return std::make_shared
<DownstreamState
>(ComboAddress());
958 auto states
= g_dstates
.getCopy();
959 if (auto str
= boost::get
<std::string
>(&i
)) {
960 const auto uuid
= getUniqueID(*str
);
961 for (auto& state
: states
) {
962 if (*state
->d_config
.id
== uuid
) {
967 else if (auto pos
= boost::get
<int>(&i
)) {
968 return states
.at(*pos
);
971 g_outputBuffer
= "Error: no rule matched\n";
972 return std::shared_ptr
<DownstreamState
>(nullptr);
975 #ifndef DISABLE_CARBON
976 luaCtx
.writeFunction("carbonServer", [](const std::string
& address
, boost::optional
<string
> ourName
, boost::optional
<uint64_t> interval
, boost::optional
<string
> namespace_name
, boost::optional
<string
> instance_name
) {
978 dnsdist::Carbon::Endpoint endpoint
{ComboAddress(address
, 2003),
979 (namespace_name
&& !namespace_name
->empty()) ? *namespace_name
: "dnsdist",
980 ourName
? *ourName
: "",
981 (instance_name
&& !instance_name
->empty()) ? *instance_name
: "main",
982 (interval
&& *interval
< std::numeric_limits
<unsigned int>::max()) ? static_cast<unsigned int>(*interval
) : 30};
983 dnsdist::Carbon::addEndpoint(std::move(endpoint
));
985 #endif /* DISABLE_CARBON */
987 luaCtx
.writeFunction("webserver", [client
, configCheck
](const std::string
& address
) {
991 local
= ComboAddress(address
);
993 catch (const PDNSException
& e
) {
994 throw std::runtime_error(std::string("Error parsing the bind address for the webserver: ") + e
.reason
);
997 if (client
|| configCheck
) {
1002 int sock
= SSocket(local
.sin4
.sin_family
, SOCK_STREAM
, 0);
1003 SSetsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, 1);
1006 auto launch
= [sock
, local
]() {
1007 thread
t(dnsdistWebserverThread
, sock
, local
);
1011 g_launchWork
->push_back(launch
);
1017 catch (const std::exception
& e
) {
1018 g_outputBuffer
= "Unable to bind to webserver socket on " + local
.toStringWithPort() + ": " + e
.what();
1019 errlog("Unable to bind to webserver socket on %s: %s", local
.toStringWithPort(), e
.what());
1023 typedef LuaAssociativeTable
<boost::variant
<bool, std::string
, LuaAssociativeTable
<std::string
>>> webserveropts_t
;
1025 luaCtx
.writeFunction("setWebserverConfig", [](boost::optional
<webserveropts_t
> vars
) {
1032 bool hashPlaintextCredentials
= false;
1033 getOptionalValue
<bool>(vars
, "hashPlaintextCredentials", hashPlaintextCredentials
);
1035 std::string password
;
1038 LuaAssociativeTable
<std::string
> headers
;
1039 bool statsRequireAuthentication
{true};
1040 bool apiRequiresAuthentication
{true};
1041 bool dashboardRequiresAuthentication
{true};
1042 int maxConcurrentConnections
= 0;
1044 if (getOptionalValue
<std::string
>(vars
, "password", password
) > 0) {
1045 auto holder
= make_unique
<CredentialsHolder
>(std::move(password
), hashPlaintextCredentials
);
1046 if (!holder
->wasHashed() && holder
->isHashingAvailable()) {
1047 infolog("Passing a plain-text password via the 'password' parameter to 'setWebserverConfig()' is not advised, please consider generating a hashed one using 'hashPassword()' instead.");
1050 setWebserverPassword(std::move(holder
));
1053 if (getOptionalValue
<std::string
>(vars
, "apiKey", apiKey
) > 0) {
1054 auto holder
= make_unique
<CredentialsHolder
>(std::move(apiKey
), hashPlaintextCredentials
);
1055 if (!holder
->wasHashed() && holder
->isHashingAvailable()) {
1056 infolog("Passing a plain-text API key via the 'apiKey' parameter to 'setWebserverConfig()' is not advised, please consider generating a hashed one using 'hashPassword()' instead.");
1059 setWebserverAPIKey(std::move(holder
));
1062 if (getOptionalValue
<std::string
>(vars
, "acl", acl
) > 0) {
1063 setWebserverACL(acl
);
1066 if (getOptionalValue
<decltype(headers
)>(vars
, "customHeaders", headers
) > 0) {
1067 setWebserverCustomHeaders(headers
);
1070 if (getOptionalValue
<bool>(vars
, "statsRequireAuthentication", statsRequireAuthentication
) > 0) {
1071 setWebserverStatsRequireAuthentication(statsRequireAuthentication
);
1074 if (getOptionalValue
<bool>(vars
, "apiRequiresAuthentication", apiRequiresAuthentication
) > 0) {
1075 setWebserverAPIRequiresAuthentication(apiRequiresAuthentication
);
1078 if (getOptionalValue
<bool>(vars
, "dashboardRequiresAuthentication", dashboardRequiresAuthentication
) > 0) {
1079 setWebserverDashboardRequiresAuthentication(dashboardRequiresAuthentication
);
1082 if (getOptionalIntegerValue("setWebserverConfig", vars
, "maxConcurrentConnections", maxConcurrentConnections
) > 0) {
1083 setWebserverMaxConcurrentConnections(maxConcurrentConnections
);
1087 luaCtx
.writeFunction("showWebserverConfig", []() {
1088 setLuaNoSideEffect();
1089 return getWebserverConfig();
1092 luaCtx
.writeFunction("hashPassword", [](const std::string
& password
, boost::optional
<uint64_t> workFactor
) {
1094 return hashPassword(password
, *workFactor
, CredentialsHolder::s_defaultParallelFactor
, CredentialsHolder::s_defaultBlockSize
);
1096 return hashPassword(password
);
1099 luaCtx
.writeFunction("controlSocket", [client
, configCheck
](const std::string
& str
) {
1101 ComboAddress
local(str
, 5199);
1103 if (client
|| configCheck
) {
1104 g_serverControl
= local
;
1108 g_consoleEnabled
= true;
1109 #if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO)
1110 if (g_configurationDone
&& g_consoleKey
.empty()) {
1111 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");
1116 auto sock
= std::make_shared
<Socket
>(local
.sin4
.sin_family
, SOCK_STREAM
, 0);
1117 sock
->bind(local
, true);
1119 auto launch
= [sock
, local
]() {
1120 std::thread
consoleControlThread(controlThread
, sock
, local
);
1121 consoleControlThread
.detach();
1124 g_launchWork
->emplace_back(std::move(launch
));
1130 catch (std::exception
& e
) {
1131 g_outputBuffer
= "Unable to bind to control socket on " + local
.toStringWithPort() + ": " + e
.what();
1132 errlog("Unable to bind to control socket on %s: %s", local
.toStringWithPort(), e
.what());
1136 luaCtx
.writeFunction("addConsoleACL", [](const std::string
& netmask
) {
1138 #if !defined(HAVE_LIBSODIUM) && !defined(HAVE_LIBCRYPTO)
1139 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");
1142 g_consoleACL
.modify([netmask
](NetmaskGroup
& nmg
) { nmg
.addMask(netmask
); });
1145 luaCtx
.writeFunction("setConsoleACL", [](LuaTypeOrArrayOf
<std::string
> inp
) {
1148 #if !defined(HAVE_LIBSODIUM) && !defined(HAVE_LIBCRYPTO)
1149 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");
1153 if (auto str
= boost::get
<string
>(&inp
)) {
1157 for (const auto& p
: boost::get
<LuaArray
<std::string
>>(inp
)) {
1158 nmg
.addMask(p
.second
);
1160 g_consoleACL
.setState(nmg
);
1163 luaCtx
.writeFunction("showConsoleACL", []() {
1164 setLuaNoSideEffect();
1166 #if !defined(HAVE_LIBSODIUM) && !defined(HAVE_LIBCRYPTO)
1167 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");
1170 auto aclEntries
= g_consoleACL
.getLocal()->toStringVector();
1172 for (const auto& entry
: aclEntries
) {
1173 g_outputBuffer
+= entry
+ "\n";
1177 luaCtx
.writeFunction("setConsoleMaximumConcurrentConnections", [](uint64_t max
) {
1179 setConsoleMaximumConcurrentConnections(max
);
1182 luaCtx
.writeFunction("clearQueryCounters", []() {
1183 unsigned int size
{0};
1185 auto records
= g_qcount
.records
.write_lock();
1186 size
= records
->size();
1190 boost::format
fmt("%d records cleared from query counter buffer\n");
1191 g_outputBuffer
= (fmt
% size
).str();
1194 luaCtx
.writeFunction("getQueryCounters", [](boost::optional
<uint64_t> optMax
) {
1195 setLuaNoSideEffect();
1196 auto records
= g_qcount
.records
.read_lock();
1197 g_outputBuffer
= "query counting is currently: ";
1198 g_outputBuffer
+= g_qcount
.enabled
? "enabled" : "disabled";
1199 g_outputBuffer
+= (boost::format(" (%d records in buffer)\n") % records
->size()).str();
1201 boost::format
fmt("%-3d %s: %d request(s)\n");
1202 uint64_t max
= optMax
? *optMax
: 10U;
1204 for (auto it
= records
->begin(); it
!= records
->end() && index
<= max
; ++it
, ++index
) {
1205 g_outputBuffer
+= (fmt
% index
% it
->first
% it
->second
).str();
1209 luaCtx
.writeFunction("setQueryCount", [](bool enabled
) { g_qcount
.enabled
= enabled
; });
1211 luaCtx
.writeFunction("setQueryCountFilter", [](QueryCountFilter func
) {
1212 g_qcount
.filter
= std::move(func
);
1215 luaCtx
.writeFunction("makeKey", []() {
1216 setLuaNoSideEffect();
1217 g_outputBuffer
= "setKey(" + dnsdist::crypto::authenticated::newKey() + ")\n";
1220 luaCtx
.writeFunction("setKey", [](const std::string
& key
) {
1221 if (!g_configurationDone
&& !g_consoleKey
.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
1222 return; // but later setKeys() trump the -k value again
1224 #if !defined(HAVE_LIBSODIUM) && !defined(HAVE_LIBCRYPTO)
1225 warnlog("Calling setKey() while neither libsodium nor libcrypto support has been enabled is not secure, and will result in cleartext communications");
1230 if (B64Decode(key
, newkey
) < 0) {
1231 g_outputBuffer
= string("Unable to decode ") + key
+ " as Base64";
1232 errlog("%s", g_outputBuffer
);
1235 g_consoleKey
= std::move(newkey
);
1238 luaCtx
.writeFunction("clearConsoleHistory", []() {
1239 clearConsoleHistory();
1242 luaCtx
.writeFunction("testCrypto", [](boost::optional
<string
> optTestMsg
) {
1243 setLuaNoSideEffect();
1244 #if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO)
1249 testmsg
= *optTestMsg
;
1252 testmsg
= "testStringForCryptoTests";
1255 dnsdist::crypto::authenticated::Nonce nonce1
;
1256 dnsdist::crypto::authenticated::Nonce nonce2
;
1259 string encrypted
= dnsdist::crypto::authenticated::encryptSym(testmsg
, g_consoleKey
, nonce1
);
1260 string decrypted
= dnsdist::crypto::authenticated::decryptSym(encrypted
, g_consoleKey
, nonce2
);
1265 encrypted
= dnsdist::crypto::authenticated::encryptSym(testmsg
, g_consoleKey
, nonce1
);
1266 decrypted
= dnsdist::crypto::authenticated::decryptSym(encrypted
, g_consoleKey
, nonce2
);
1268 if (testmsg
== decrypted
) {
1269 g_outputBuffer
= "Everything is ok!\n";
1272 g_outputBuffer
= "Crypto failed.. (the decoded value does not match the cleartext one)\n";
1275 catch (const std::exception
& e
) {
1276 g_outputBuffer
= "Crypto failed: " + std::string(e
.what()) + "\n";
1279 g_outputBuffer
= "Crypto failed..\n";
1282 g_outputBuffer
= "Crypto not available.\n";
1286 luaCtx
.writeFunction("setTCPRecvTimeout", [](int timeout
) { g_tcpRecvTimeout
= timeout
; });
1288 luaCtx
.writeFunction("setTCPSendTimeout", [](int timeout
) { g_tcpSendTimeout
= timeout
; });
1290 luaCtx
.writeFunction("setUDPTimeout", [](int timeout
) { DownstreamState::s_udpTimeout
= timeout
; });
1292 luaCtx
.writeFunction("setMaxUDPOutstanding", [](uint64_t max
) {
1293 if (!checkConfigurationTime("setMaxUDPOutstanding")) {
1297 checkParameterBound("setMaxUDPOutstanding", max
);
1298 g_maxOutstanding
= max
;
1301 luaCtx
.writeFunction("setMaxTCPClientThreads", [](uint64_t max
) {
1302 if (!checkConfigurationTime("setMaxTCPClientThreads")) {
1305 g_maxTCPClientThreads
= max
;
1308 luaCtx
.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max
) {
1309 if (!checkConfigurationTime("setMaxTCPQueuedConnections")) {
1312 g_maxTCPQueuedConnections
= max
;
1315 luaCtx
.writeFunction("setMaxTCPQueriesPerConnection", [](uint64_t max
) {
1316 if (!checkConfigurationTime("setMaxTCPQueriesPerConnection")) {
1319 g_maxTCPQueriesPerConn
= max
;
1322 luaCtx
.writeFunction("setMaxTCPConnectionsPerClient", [](uint64_t max
) {
1323 if (!checkConfigurationTime("setMaxTCPConnectionsPerClient")) {
1326 dnsdist::IncomingConcurrentTCPConnectionsManager::setMaxTCPConnectionsPerClient(max
);
1329 luaCtx
.writeFunction("setMaxTCPConnectionDuration", [](uint64_t max
) {
1330 if (!checkConfigurationTime("setMaxTCPConnectionDuration")) {
1333 g_maxTCPConnectionDuration
= max
;
1336 luaCtx
.writeFunction("setMaxCachedTCPConnectionsPerDownstream", [](uint64_t max
) {
1337 setTCPDownstreamMaxIdleConnectionsPerBackend(max
);
1340 #if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
1341 luaCtx
.writeFunction("setMaxIdleDoHConnectionsPerDownstream", [](uint64_t max
) {
1342 setDoHDownstreamMaxIdleConnectionsPerBackend(max
);
1345 luaCtx
.writeFunction("setOutgoingDoHWorkerThreads", [](uint64_t workers
) {
1346 if (!checkConfigurationTime("setOutgoingDoHWorkerThreads")) {
1349 g_outgoingDoHWorkerThreads
= workers
;
1351 #endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
1353 luaCtx
.writeFunction("setOutgoingTLSSessionsCacheMaxTicketsPerBackend", [](uint64_t max
) {
1354 if (!checkConfigurationTime("setOutgoingTLSSessionsCacheMaxTicketsPerBackend")) {
1357 TLSSessionCache::setMaxTicketsPerBackend(max
);
1360 luaCtx
.writeFunction("setOutgoingTLSSessionsCacheCleanupDelay", [](time_t delay
) {
1361 if (!checkConfigurationTime("setOutgoingTLSSessionsCacheCleanupDelay")) {
1364 TLSSessionCache::setCleanupDelay(delay
);
1367 luaCtx
.writeFunction("setOutgoingTLSSessionsCacheMaxTicketValidity", [](time_t validity
) {
1368 if (!checkConfigurationTime("setOutgoingTLSSessionsCacheMaxTicketValidity")) {
1371 TLSSessionCache::setSessionValidity(validity
);
1374 luaCtx
.writeFunction("getOutgoingTLSSessionCacheSize", []() {
1375 setLuaNoSideEffect();
1376 return g_sessionCache
.getSize();
1379 luaCtx
.writeFunction("setCacheCleaningDelay", [](uint64_t delay
) {
1380 checkParameterBound("setCacheCleaningDelay", delay
, std::numeric_limits
<uint32_t>::max());
1381 g_cacheCleaningDelay
= delay
;
1384 luaCtx
.writeFunction("setCacheCleaningPercentage", [](uint64_t percentage
) { if (percentage
< 100) g_cacheCleaningPercentage
= percentage
; else g_cacheCleaningPercentage
= 100; });
1386 luaCtx
.writeFunction("setECSSourcePrefixV4", [](uint64_t prefix
) {
1387 checkParameterBound("setECSSourcePrefixV4", prefix
, std::numeric_limits
<uint16_t>::max());
1388 g_ECSSourcePrefixV4
= prefix
;
1391 luaCtx
.writeFunction("setECSSourcePrefixV6", [](uint64_t prefix
) {
1392 checkParameterBound("setECSSourcePrefixV6", prefix
, std::numeric_limits
<uint16_t>::max());
1393 g_ECSSourcePrefixV6
= prefix
;
1396 luaCtx
.writeFunction("setECSOverride", [](bool override
) { g_ECSOverride
= override
; });
1398 #ifndef DISABLE_DYNBLOCKS
1399 luaCtx
.writeFunction("showDynBlocks", []() {
1400 setLuaNoSideEffect();
1401 auto slow
= g_dynblockNMG
.getCopy();
1402 struct timespec now
;
1404 boost::format
fmt("%-24s %8d %8d %-10s %-20s %-10s %s\n");
1405 g_outputBuffer
= (fmt
% "What" % "Seconds" % "Blocks" % "Warning" % "Action" % "eBPF" % "Reason").str();
1406 for (const auto& e
: slow
) {
1407 if (now
< e
.second
.until
) {
1408 uint64_t counter
= e
.second
.blocks
;
1409 if (g_defaultBPFFilter
&& e
.second
.bpf
) {
1410 counter
+= g_defaultBPFFilter
->getHits(e
.first
.getNetwork());
1412 g_outputBuffer
+= (fmt
% e
.first
.toString() % (e
.second
.until
.tv_sec
- now
.tv_sec
) % counter
% (e
.second
.warning
? "true" : "false") % DNSAction::typeToString(e
.second
.action
!= DNSAction::Action::None
? e
.second
.action
: g_dynBlockAction
) % (g_defaultBPFFilter
&& e
.second
.bpf
? "*" : "") % e
.second
.reason
).str();
1415 auto slow2
= g_dynblockSMT
.getCopy();
1416 slow2
.visit([&now
, &fmt
](const SuffixMatchTree
<DynBlock
>& node
) {
1417 if (now
< node
.d_value
.until
) {
1418 string
dom("empty");
1419 if (!node
.d_value
.domain
.empty())
1420 dom
= node
.d_value
.domain
.toString();
1421 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();
1426 luaCtx
.writeFunction("getDynamicBlocks", []() {
1427 setLuaNoSideEffect();
1433 LuaAssociativeTable
<DynBlock
> entries
;
1434 auto fullCopy
= g_dynblockNMG
.getCopy();
1435 for (const auto& blockPair
: fullCopy
) {
1436 const auto& requestor
= blockPair
.first
;
1437 if (!(now
< blockPair
.second
.until
)) {
1440 auto entry
= blockPair
.second
;
1441 if (g_defaultBPFFilter
&& entry
.bpf
) {
1442 entry
.blocks
+= g_defaultBPFFilter
->getHits(requestor
.getNetwork());
1444 if (entry
.action
== DNSAction::Action::None
) {
1445 entry
.action
= g_dynBlockAction
;
1447 entries
.emplace(requestor
.toString(), std::move(entry
));
1452 luaCtx
.writeFunction("getDynamicBlocksSMT", []() {
1453 setLuaNoSideEffect();
1459 LuaAssociativeTable
<DynBlock
> entries
;
1460 auto fullCopy
= g_dynblockSMT
.getCopy();
1461 fullCopy
.visit([&now
, &entries
](const SuffixMatchTree
<DynBlock
>& node
) {
1462 if (!(now
< node
.d_value
.until
)) {
1465 auto entry
= node
.d_value
;
1466 string
key("empty");
1467 if (!entry
.domain
.empty()) {
1468 key
= entry
.domain
.toString();
1470 if (entry
.action
== DNSAction::Action::None
) {
1471 entry
.action
= g_dynBlockAction
;
1473 entries
.emplace(std::move(key
), std::move(entry
));
1478 luaCtx
.writeFunction("clearDynBlocks", []() {
1481 g_dynblockNMG
.setState(nmg
);
1482 SuffixMatchTree
<DynBlock
> smt
;
1483 g_dynblockSMT
.setState(smt
);
1486 #ifndef DISABLE_DEPRECATED_DYNBLOCK
1487 luaCtx
.writeFunction("addDynBlocks",
1488 [](const std::unordered_map
<ComboAddress
, unsigned int, ComboAddress::addressOnlyHash
, ComboAddress::addressOnlyEqual
>& m
, const std::string
& msg
, boost::optional
<int> seconds
, boost::optional
<DNSAction::Action
> action
) {
1493 auto slow
= g_dynblockNMG
.getCopy();
1494 struct timespec until
, now
;
1497 int actualSeconds
= seconds
? *seconds
: 10;
1498 until
.tv_sec
+= actualSeconds
;
1499 for (const auto& capair
: m
) {
1500 unsigned int count
= 0;
1501 /* this legacy interface does not support ranges or ports, use DynBlockRulesGroup instead */
1502 AddressAndPortRange
requestor(capair
.first
, capair
.first
.isIPv4() ? 32 : 128, 0);
1503 auto got
= slow
.lookup(requestor
);
1504 bool expired
= false;
1506 if (until
< got
->second
.until
) {
1507 // had a longer policy
1510 if (now
< got
->second
.until
) {
1511 // only inherit count on fresh query we are extending
1512 count
= got
->second
.blocks
;
1518 DynBlock db
{msg
, until
, DNSName(), (action
? *action
: DNSAction::Action::None
)};
1520 if (!got
|| expired
) {
1521 warnlog("Inserting dynamic block for %s for %d seconds: %s", capair
.first
.toString(), actualSeconds
, msg
);
1523 slow
.insert(requestor
).second
= std::move(db
);
1525 g_dynblockNMG
.setState(slow
);
1528 luaCtx
.writeFunction("setDynBlocksAction", [](DNSAction::Action action
) {
1529 if (!checkConfigurationTime("setDynBlocksAction")) {
1532 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
) {
1533 g_dynBlockAction
= action
;
1536 errlog("Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!");
1537 g_outputBuffer
= "Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!\n";
1540 #endif /* DISABLE_DEPRECATED_DYNBLOCK */
1542 luaCtx
.writeFunction("addDynBlockSMT",
1543 [](const LuaArray
<std::string
>& names
, const std::string
& msg
, boost::optional
<int> seconds
, boost::optional
<DNSAction::Action
> action
) {
1544 if (names
.empty()) {
1552 unsigned int actualSeconds
= seconds
? *seconds
: 10;
1554 bool needUpdate
= false;
1555 auto slow
= g_dynblockSMT
.getCopy();
1556 for (const auto& capair
: names
) {
1557 DNSName
domain(capair
.second
);
1558 domain
.makeUsLowerCase();
1560 if (dnsdist::DynamicBlocks::addOrRefreshBlockSMT(slow
, now
, domain
, msg
, actualSeconds
, action
? *action
: DNSAction::Action::None
, false)) {
1566 g_dynblockSMT
.setState(slow
);
1570 luaCtx
.writeFunction("addDynamicBlock",
1571 [](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
) {
1574 ComboAddress clientIPCA
;
1575 if (clientIP
.type() == typeid(ComboAddress
)) {
1576 clientIPCA
= boost::get
<ComboAddress
>(clientIP
);
1579 const auto& clientIPStr
= boost::get
<std::string
>(clientIP
);
1581 clientIPCA
= ComboAddress(clientIPStr
);
1583 catch (const std::exception
& exp
) {
1584 errlog("addDynamicBlock: Unable to parse '%s': %s", clientIPStr
, exp
.what());
1587 catch (const PDNSException
& exp
) {
1588 errlog("addDynamicBlock: Unable to parse '%s': %s", clientIPStr
, exp
.reason
);
1592 AddressAndPortRange
target(clientIPCA
, clientIPMask
? *clientIPMask
: (clientIPCA
.isIPv4() ? 32 : 128), clientIPPortMask
? *clientIPPortMask
: 0);
1593 unsigned int actualSeconds
= seconds
? *seconds
: 10;
1599 auto slow
= g_dynblockNMG
.getCopy();
1600 if (dnsdist::DynamicBlocks::addOrRefreshBlock(slow
, now
, target
, msg
, actualSeconds
, action
? *action
: DNSAction::Action::None
, false, false)) {
1601 g_dynblockNMG
.setState(slow
);
1605 luaCtx
.writeFunction("setDynBlocksPurgeInterval", [](uint64_t interval
) {
1606 DynBlockMaintenance::s_expiredDynBlocksPurgeInterval
= interval
;
1608 #endif /* DISABLE_DYNBLOCKS */
1610 #ifdef HAVE_DNSCRYPT
1611 luaCtx
.writeFunction("addDNSCryptBind", [](const std::string
& addr
, const std::string
& providerName
, LuaTypeOrArrayOf
<std::string
> certFiles
, LuaTypeOrArrayOf
<std::string
> keyFiles
, boost::optional
<localbind_t
> vars
) {
1612 if (!checkConfigurationTime("addDNSCryptBind")) {
1615 bool reusePort
= false;
1616 int tcpFastOpenQueueSize
= 0;
1617 int tcpListenQueueSize
= 0;
1618 uint64_t maxInFlightQueriesPerConn
= 0;
1619 uint64_t tcpMaxConcurrentConnections
= 0;
1620 std::string interface
;
1622 std::vector
<DNSCryptContext::CertKeyPaths
> certKeys
;
1623 bool enableProxyProtocol
= true;
1625 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConnections
, enableProxyProtocol
);
1626 checkAllParametersConsumed("addDNSCryptBind", vars
);
1628 if (certFiles
.type() == typeid(std::string
) && keyFiles
.type() == typeid(std::string
)) {
1629 auto certFile
= boost::get
<std::string
>(certFiles
);
1630 auto keyFile
= boost::get
<std::string
>(keyFiles
);
1631 certKeys
.push_back({std::move(certFile
), std::move(keyFile
)});
1633 else if (certFiles
.type() == typeid(LuaArray
<std::string
>) && keyFiles
.type() == typeid(LuaArray
<std::string
>)) {
1634 auto certFilesVect
= boost::get
<LuaArray
<std::string
>>(certFiles
);
1635 auto keyFilesVect
= boost::get
<LuaArray
<std::string
>>(keyFiles
);
1636 if (certFilesVect
.size() == keyFilesVect
.size()) {
1637 for (size_t idx
= 0; idx
< certFilesVect
.size(); idx
++) {
1638 certKeys
.push_back({certFilesVect
.at(idx
).second
, keyFilesVect
.at(idx
).second
});
1642 errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind!");
1643 g_outputBuffer
= "Error, mismatching number of certificates and keys in call to addDNSCryptBind()!";
1648 errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind()!");
1649 g_outputBuffer
= "Error, mismatching number of certificates and keys in call to addDNSCryptBind()!";
1654 auto ctx
= std::make_shared
<DNSCryptContext
>(providerName
, certKeys
);
1657 auto clientState
= std::make_unique
<ClientState
>(ComboAddress(addr
, 443), false, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, enableProxyProtocol
);
1658 clientState
->dnscryptCtx
= ctx
;
1659 g_dnsCryptLocals
.push_back(ctx
);
1660 g_frontends
.push_back(std::move(clientState
));
1663 clientState
= std::make_unique
<ClientState
>(ComboAddress(addr
, 443), true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, enableProxyProtocol
);
1664 clientState
->dnscryptCtx
= std::move(ctx
);
1665 if (tcpListenQueueSize
> 0) {
1666 clientState
->tcpListenQueueSize
= tcpListenQueueSize
;
1668 if (maxInFlightQueriesPerConn
> 0) {
1669 clientState
->d_maxInFlightQueriesPerConn
= maxInFlightQueriesPerConn
;
1671 if (tcpMaxConcurrentConnections
> 0) {
1672 clientState
->d_tcpConcurrentConnectionsLimit
= tcpMaxConcurrentConnections
;
1675 g_frontends
.push_back(std::move(clientState
));
1677 catch (const std::exception
& e
) {
1678 errlog("Error during addDNSCryptBind() processing: %s", e
.what());
1679 g_outputBuffer
= "Error during addDNSCryptBind() processing: " + string(e
.what()) + "\n";
1683 luaCtx
.writeFunction("showDNSCryptBinds", []() {
1684 setLuaNoSideEffect();
1686 boost::format
fmt("%1$-3d %2% %|25t|%3$-20.20s");
1687 ret
<< (fmt
% "#" % "Address" % "Provider Name") << endl
;
1690 std::unordered_set
<std::shared_ptr
<DNSCryptContext
>> contexts
;
1691 for (const auto& frontend
: g_frontends
) {
1692 const std::shared_ptr
<DNSCryptContext
> ctx
= frontend
->dnscryptCtx
;
1693 if (!ctx
|| contexts
.count(ctx
) != 0) {
1696 contexts
.insert(ctx
);
1697 ret
<< (fmt
% idx
% frontend
->local
.toStringWithPort() % ctx
->getProviderName()) << endl
;
1701 g_outputBuffer
= ret
.str();
1704 luaCtx
.writeFunction("getDNSCryptBind", [](uint64_t idx
) {
1705 setLuaNoSideEffect();
1706 std::shared_ptr
<DNSCryptContext
> ret
= nullptr;
1707 if (idx
< g_dnsCryptLocals
.size()) {
1708 ret
= g_dnsCryptLocals
.at(idx
);
1713 luaCtx
.writeFunction("getDNSCryptBindCount", []() {
1714 setLuaNoSideEffect();
1715 return g_dnsCryptLocals
.size();
1717 #endif /* HAVE_DNSCRYPT */
1719 luaCtx
.writeFunction("showPools", []() {
1720 setLuaNoSideEffect();
1723 boost::format
fmt("%1$-20.20s %|25t|%2$20s %|25t|%3$20s %|50t|%4%");
1725 ret
<< (fmt
% "Name" % "Cache" % "ServerPolicy" % "Servers") << endl
;
1727 const auto localPools
= g_pools
.getCopy();
1728 for (const auto& entry
: localPools
) {
1729 const string
& name
= entry
.first
;
1730 const std::shared_ptr
<ServerPool
> pool
= entry
.second
;
1731 string cache
= pool
->packetCache
!= nullptr ? pool
->packetCache
->toString() : "";
1732 string policy
= g_policy
.getLocal()->getName();
1733 if (pool
->policy
!= nullptr) {
1734 policy
= pool
->policy
->getName();
1738 const auto poolServers
= pool
->getServers();
1739 for (const auto& server
: *poolServers
) {
1740 if (!servers
.empty()) {
1743 if (!server
.second
->getName().empty()) {
1744 servers
+= server
.second
->getName();
1747 servers
+= server
.second
->d_config
.remote
.toStringWithPort();
1750 ret
<< (fmt
% name
% cache
% policy
% servers
) << endl
;
1752 g_outputBuffer
= ret
.str();
1754 catch (std::exception
& e
) {
1755 g_outputBuffer
= e
.what();
1760 luaCtx
.writeFunction("getPoolNames", []() {
1761 setLuaNoSideEffect();
1762 LuaArray
<std::string
> ret
;
1764 const auto localPools
= g_pools
.getCopy();
1765 for (const auto& entry
: localPools
) {
1766 const string
& name
= entry
.first
;
1767 ret
.emplace_back(count
++, name
);
1772 luaCtx
.writeFunction("getPool", [client
](const string
& poolName
) {
1774 return std::make_shared
<ServerPool
>();
1776 auto localPools
= g_pools
.getCopy();
1777 std::shared_ptr
<ServerPool
> pool
= createPoolIfNotExists(localPools
, poolName
);
1778 g_pools
.setState(localPools
);
1782 luaCtx
.writeFunction("setVerbose", [](bool verbose
) { g_verbose
= verbose
; });
1783 luaCtx
.writeFunction("getVerbose", []() { return g_verbose
; });
1784 luaCtx
.writeFunction("setVerboseHealthChecks", [](bool verbose
) { g_verboseHealthChecks
= verbose
; });
1785 luaCtx
.writeFunction("setVerboseLogDestination", [](const std::string
& dest
) {
1786 if (!checkConfigurationTime("setVerboseLogDestination")) {
1790 auto stream
= std::ofstream(dest
.c_str());
1791 dnsdist::logging::LoggingConfiguration::setVerboseStream(std::move(stream
));
1793 catch (const std::exception
& e
) {
1794 errlog("Error while opening the verbose logging destination file %s: %s", dest
, e
.what());
1797 luaCtx
.writeFunction("setStructuredLogging", [](bool enable
, boost::optional
<LuaAssociativeTable
<std::string
>> options
) {
1798 std::string levelPrefix
;
1799 std::string timeFormat
;
1801 getOptionalValue
<std::string
>(options
, "levelPrefix", levelPrefix
);
1802 if (getOptionalValue
<std::string
>(options
, "timeFormat", timeFormat
) == 1) {
1803 if (timeFormat
== "numeric") {
1804 dnsdist::logging::LoggingConfiguration::setStructuredTimeFormat(dnsdist::logging::LoggingConfiguration::TimeFormat::Numeric
);
1806 else if (timeFormat
== "ISO8601") {
1807 dnsdist::logging::LoggingConfiguration::setStructuredTimeFormat(dnsdist::logging::LoggingConfiguration::TimeFormat::ISO8601
);
1810 warnlog("Unknown value '%s' to setStructuredLogging's 'timeFormat' parameter", timeFormat
);
1813 checkAllParametersConsumed("setStructuredLogging", options
);
1816 dnsdist::logging::LoggingConfiguration::setStructuredLogging(enable
, levelPrefix
);
1819 luaCtx
.writeFunction("setStaleCacheEntriesTTL", [](uint64_t ttl
) {
1820 checkParameterBound("setStaleCacheEntriesTTL", ttl
, std::numeric_limits
<uint32_t>::max());
1821 g_staleCacheEntriesTTL
= ttl
;
1824 luaCtx
.writeFunction("showBinds", []() {
1825 setLuaNoSideEffect();
1828 boost::format
fmt("%1$-3d %2$-20.20s %|35t|%3$-20.20s %|57t|%4%");
1830 ret
<< (fmt
% "#" % "Address" % "Protocol" % "Queries") << endl
;
1833 for (const auto& front
: g_frontends
) {
1834 ret
<< (fmt
% counter
% front
->local
.toStringWithPort() % front
->getType() % front
->queries
) << endl
;
1837 g_outputBuffer
= ret
.str();
1839 catch (std::exception
& e
) {
1840 g_outputBuffer
= e
.what();
1845 luaCtx
.writeFunction("getBind", [](uint64_t num
) {
1846 setLuaNoSideEffect();
1847 ClientState
* ret
= nullptr;
1848 if (num
< g_frontends
.size()) {
1849 ret
= g_frontends
[num
].get();
1854 luaCtx
.writeFunction("getBindCount", []() {
1855 setLuaNoSideEffect();
1856 return g_frontends
.size();
1859 luaCtx
.writeFunction("help", [](boost::optional
<std::string
> command
) {
1860 setLuaNoSideEffect();
1861 g_outputBuffer
= "";
1862 #ifndef DISABLE_COMPLETION
1863 for (const auto& keyword
: g_consoleKeywords
) {
1865 g_outputBuffer
+= keyword
.toString() + "\n";
1867 else if (keyword
.name
== command
) {
1868 g_outputBuffer
= keyword
.toString() + "\n";
1872 #endif /* DISABLE_COMPLETION */
1874 g_outputBuffer
= "Nothing found for " + *command
+ "\n";
1878 luaCtx
.writeFunction("showVersion", []() {
1879 setLuaNoSideEffect();
1880 g_outputBuffer
= "dnsdist " + std::string(VERSION
) + "\n";
1884 luaCtx
.writeFunction("setDefaultBPFFilter", [](std::shared_ptr
<BPFFilter
> bpf
) {
1885 if (!checkConfigurationTime("setDefaultBPFFilter")) {
1888 g_defaultBPFFilter
= std::move(bpf
);
1891 luaCtx
.writeFunction("registerDynBPFFilter", [](std::shared_ptr
<DynBPFFilter
> dbpf
) {
1893 g_dynBPFFilters
.push_back(std::move(dbpf
));
1897 luaCtx
.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr
<DynBPFFilter
> dbpf
) {
1899 for (auto it
= g_dynBPFFilters
.begin(); it
!= g_dynBPFFilters
.end(); it
++) {
1901 g_dynBPFFilters
.erase(it
);
1908 #ifndef DISABLE_DYNBLOCKS
1909 #ifndef DISABLE_DEPRECATED_DYNBLOCK
1910 luaCtx
.writeFunction("addBPFFilterDynBlocks", [](const std::unordered_map
<ComboAddress
, unsigned int, ComboAddress::addressOnlyHash
, ComboAddress::addressOnlyEqual
>& m
, std::shared_ptr
<DynBPFFilter
> dynbpf
, boost::optional
<int> seconds
, boost::optional
<std::string
> msg
) {
1915 struct timespec until
, now
;
1916 clock_gettime(CLOCK_MONOTONIC
, &now
);
1918 int actualSeconds
= seconds
? *seconds
: 10;
1919 until
.tv_sec
+= actualSeconds
;
1920 for (const auto& capair
: m
) {
1921 if (dynbpf
->block(capair
.first
, until
)) {
1922 warnlog("Inserting eBPF dynamic block for %s for %d seconds: %s", capair
.first
.toString(), actualSeconds
, msg
? *msg
: "");
1926 #endif /* DISABLE_DEPRECATED_DYNBLOCK */
1927 #endif /* DISABLE_DYNBLOCKS */
1929 #endif /* HAVE_EBPF */
1931 luaCtx
.writeFunction
<LuaAssociativeTable
<uint64_t>()>("getStatisticsCounters", []() {
1932 setLuaNoSideEffect();
1933 std::unordered_map
<string
, uint64_t> res
;
1935 auto entries
= dnsdist::metrics::g_stats
.entries
.read_lock();
1936 res
.reserve(entries
->size());
1937 for (const auto& entry
: *entries
) {
1938 if (const auto& val
= std::get_if
<pdns::stat_t
*>(&entry
.d_value
)) {
1939 res
[entry
.d_name
] = (*val
)->load();
1946 luaCtx
.writeFunction("includeDirectory", [&luaCtx
](const std::string
& dirname
) {
1947 if (!checkConfigurationTime("includeDirectory")) {
1951 errlog("includeDirectory() cannot be used recursively!");
1952 g_outputBuffer
= "includeDirectory() cannot be used recursively!\n";
1957 if (stat(dirname
.c_str(), &st
)) {
1958 errlog("The included directory %s does not exist!", dirname
.c_str());
1959 g_outputBuffer
= "The included directory " + dirname
+ " does not exist!";
1963 if (!S_ISDIR(st
.st_mode
)) {
1964 errlog("The included directory %s is not a directory!", dirname
.c_str());
1965 g_outputBuffer
= "The included directory " + dirname
+ " is not a directory!";
1969 std::vector
<std::string
> files
;
1970 auto directoryError
= pdns::visit_directory(dirname
, [&dirname
, &files
]([[maybe_unused
]] ino_t inodeNumber
, const std::string_view
& name
) {
1971 if (boost::starts_with(name
, ".")) {
1974 if (boost::ends_with(name
, ".conf")) {
1975 std::ostringstream namebuf
;
1976 namebuf
<< dirname
<< "/" << name
;
1977 struct stat fileStat
1980 if (stat(namebuf
.str().c_str(), &fileStat
) == 0 && S_ISREG(fileStat
.st_mode
)) {
1981 files
.push_back(namebuf
.str());
1987 if (directoryError
) {
1988 errlog("Error opening included directory: %s!", *directoryError
);
1989 g_outputBuffer
= "Error opening included directory: " + *directoryError
+ "!";
1993 std::sort(files
.begin(), files
.end());
1997 for (const auto& file
: files
) {
1998 std::ifstream
ifs(file
);
2000 warnlog("Unable to read configuration from '%s'", file
);
2003 vinfolog("Read configuration from '%s'", file
);
2007 luaCtx
.executeCode(ifs
);
2014 luaCtx
.executeCode(ifs
);
2020 luaCtx
.writeFunction("setAPIWritable", [](bool writable
, boost::optional
<std::string
> apiConfigDir
) {
2022 g_apiReadWrite
= writable
;
2024 if (!(*apiConfigDir
).empty()) {
2025 g_apiConfigDirectory
= *apiConfigDir
;
2028 errlog("The API configuration directory value cannot be empty!");
2029 g_outputBuffer
= "The API configuration directory value cannot be empty!";
2034 luaCtx
.writeFunction("setServFailWhenNoServer", [](bool servfail
) {
2036 g_servFailOnNoPolicy
= servfail
;
2039 luaCtx
.writeFunction("setRoundRobinFailOnNoServer", [](bool fail
) {
2041 g_roundrobinFailOnNoServer
= fail
;
2044 luaCtx
.writeFunction("setConsistentHashingBalancingFactor", [](double factor
) {
2046 if (factor
>= 1.0) {
2047 g_consistentHashBalancingFactor
= factor
;
2050 errlog("Invalid value passed to setConsistentHashingBalancingFactor()!");
2051 g_outputBuffer
= "Invalid value passed to setConsistentHashingBalancingFactor()!\n";
2056 luaCtx
.writeFunction("setWeightedBalancingFactor", [](double factor
) {
2058 if (factor
>= 1.0) {
2059 g_weightedBalancingFactor
= factor
;
2062 errlog("Invalid value passed to setWeightedBalancingFactor()!");
2063 g_outputBuffer
= "Invalid value passed to setWeightedBalancingFactor()!\n";
2068 luaCtx
.writeFunction("setRingBuffersSize", [client
](uint64_t capacity
, boost::optional
<uint64_t> numberOfShards
) {
2070 if (!checkConfigurationTime("setRingBuffersSize")) {
2074 g_rings
.setCapacity(capacity
, numberOfShards
? *numberOfShards
: 10);
2077 g_rings
.setCapacity(0, 1);
2081 luaCtx
.writeFunction("setRingBuffersLockRetries", [](uint64_t retries
) {
2083 g_rings
.setNumberOfLockRetries(retries
);
2086 luaCtx
.writeFunction("setRingBuffersOptions", [](const LuaAssociativeTable
<boost::variant
<bool, uint64_t>>& options
) {
2088 if (!checkConfigurationTime("setRingBuffersOptions")) {
2091 if (options
.count("lockRetries") > 0) {
2092 auto retries
= boost::get
<uint64_t>(options
.at("lockRetries"));
2093 g_rings
.setNumberOfLockRetries(retries
);
2095 if (options
.count("recordQueries") > 0) {
2096 auto record
= boost::get
<bool>(options
.at("recordQueries"));
2097 g_rings
.setRecordQueries(record
);
2099 if (options
.count("recordResponses") > 0) {
2100 auto record
= boost::get
<bool>(options
.at("recordResponses"));
2101 g_rings
.setRecordResponses(record
);
2105 luaCtx
.writeFunction("setWHashedPertubation", [](uint64_t perturb
) {
2107 checkParameterBound("setWHashedPertubation", perturb
, std::numeric_limits
<uint32_t>::max());
2108 g_hashperturb
= perturb
;
2111 luaCtx
.writeFunction("setTCPInternalPipeBufferSize", [](uint64_t size
) { g_tcpInternalPipeBufferSize
= size
; });
2112 luaCtx
.writeFunction("setTCPFastOpenKey", [](const std::string
& keyString
) {
2114 uint32_t key
[4] = {};
2115 auto ret
= sscanf(keyString
.c_str(), "%" SCNx32
"-%" SCNx32
"-%" SCNx32
"-%" SCNx32
, &key
[0], &key
[1], &key
[2], &key
[3]);
2117 g_outputBuffer
= "Invalid value passed to setTCPFastOpenKey()!\n";
2120 extern vector
<uint32_t> g_TCPFastOpenKey
;
2121 for (const auto i
: key
) {
2122 g_TCPFastOpenKey
.push_back(i
);
2126 #ifdef HAVE_NET_SNMP
2127 luaCtx
.writeFunction("snmpAgent", [client
, configCheck
](bool enableTraps
, boost::optional
<std::string
> daemonSocket
) {
2128 if (client
|| configCheck
) {
2131 if (!checkConfigurationTime("snmpAgent")) {
2134 if (g_snmpEnabled
) {
2135 errlog("snmpAgent() cannot be used twice!");
2136 g_outputBuffer
= "snmpAgent() cannot be used twice!\n";
2140 g_snmpEnabled
= true;
2141 g_snmpTrapsEnabled
= enableTraps
;
2142 g_snmpAgent
= new DNSDistSNMPAgent("dnsdist", daemonSocket
? *daemonSocket
: std::string());
2145 luaCtx
.writeFunction("sendCustomTrap", [](const std::string
& str
) {
2146 if (g_snmpAgent
&& g_snmpTrapsEnabled
) {
2147 g_snmpAgent
->sendCustomTrap(str
);
2150 #endif /* HAVE_NET_SNMP */
2152 #ifndef DISABLE_POLICIES_BINDINGS
2153 luaCtx
.writeFunction("setServerPolicy", [](const ServerPolicy
& policy
) {
2155 g_policy
.setState(policy
);
2158 luaCtx
.writeFunction("setServerPolicyLua", [](const string
& name
, ServerPolicy::policyfunc_t policy
) {
2160 g_policy
.setState(ServerPolicy
{name
, policy
, true});
2163 luaCtx
.writeFunction("setServerPolicyLuaFFI", [](const string
& name
, ServerPolicy::ffipolicyfunc_t policy
) {
2165 auto pol
= ServerPolicy(name
, policy
);
2166 g_policy
.setState(std::move(pol
));
2169 luaCtx
.writeFunction("setServerPolicyLuaFFIPerThread", [](const string
& name
, const std::string
& policyCode
) {
2171 auto pol
= ServerPolicy(name
, policyCode
);
2172 g_policy
.setState(std::move(pol
));
2175 luaCtx
.writeFunction("showServerPolicy", []() {
2177 g_outputBuffer
= g_policy
.getLocal()->getName() + "\n";
2180 luaCtx
.writeFunction("setPoolServerPolicy", [](ServerPolicy policy
, const string
& pool
) {
2182 auto localPools
= g_pools
.getCopy();
2183 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(std::move(policy
)));
2184 g_pools
.setState(localPools
);
2187 luaCtx
.writeFunction("setPoolServerPolicyLua", [](const string
& name
, ServerPolicy::policyfunc_t policy
, const string
& pool
) {
2189 auto localPools
= g_pools
.getCopy();
2190 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(ServerPolicy
{name
, std::move(policy
), true}));
2191 g_pools
.setState(localPools
);
2194 luaCtx
.writeFunction("setPoolServerPolicyLuaFFI", [](const string
& name
, ServerPolicy::ffipolicyfunc_t policy
, const string
& pool
) {
2196 auto localPools
= g_pools
.getCopy();
2197 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(ServerPolicy
{name
, std::move(policy
)}));
2198 g_pools
.setState(localPools
);
2201 luaCtx
.writeFunction("setPoolServerPolicyLuaFFIPerThread", [](const string
& name
, const std::string
& policyCode
, const std::string
& pool
) {
2203 auto localPools
= g_pools
.getCopy();
2204 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(ServerPolicy
{name
, policyCode
}));
2205 g_pools
.setState(localPools
);
2208 luaCtx
.writeFunction("showPoolServerPolicy", [](const std::string
& pool
) {
2210 auto localPools
= g_pools
.getCopy();
2211 auto poolObj
= getPool(localPools
, pool
);
2212 if (poolObj
->policy
== nullptr) {
2213 g_outputBuffer
= g_policy
.getLocal()->getName() + "\n";
2216 g_outputBuffer
= poolObj
->policy
->getName() + "\n";
2219 #endif /* DISABLE_POLICIES_BINDINGS */
2221 luaCtx
.writeFunction("setTCPDownstreamCleanupInterval", [](uint64_t interval
) {
2223 checkParameterBound("setTCPDownstreamCleanupInterval", interval
);
2224 setTCPDownstreamCleanupInterval(interval
);
2227 #if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
2228 luaCtx
.writeFunction("setDoHDownstreamCleanupInterval", [](uint64_t interval
) {
2230 checkParameterBound("setDoHDownstreamCleanupInterval", interval
);
2231 setDoHDownstreamCleanupInterval(interval
);
2233 #endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
2235 luaCtx
.writeFunction("setTCPDownstreamMaxIdleTime", [](uint64_t max
) {
2237 checkParameterBound("setTCPDownstreamMaxIdleTime", max
);
2238 setTCPDownstreamMaxIdleTime(max
);
2241 #if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
2242 luaCtx
.writeFunction("setDoHDownstreamMaxIdleTime", [](uint64_t max
) {
2244 checkParameterBound("setDoHDownstreamMaxIdleTime", max
);
2245 setDoHDownstreamMaxIdleTime(max
);
2247 #endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
2249 luaCtx
.writeFunction("setConsoleConnectionsLogging", [](bool enabled
) {
2250 g_logConsoleConnections
= enabled
;
2253 luaCtx
.writeFunction("setConsoleOutputMaxMsgSize", [](uint64_t size
) {
2254 checkParameterBound("setConsoleOutputMaxMsgSize", size
, std::numeric_limits
<uint32_t>::max());
2255 g_consoleOutputMsgMaxSize
= size
;
2258 luaCtx
.writeFunction("setProxyProtocolACL", [](LuaTypeOrArrayOf
<std::string
> inp
) {
2259 if (!checkConfigurationTime("setProxyProtocolACL")) {
2264 if (auto str
= boost::get
<string
>(&inp
)) {
2268 for (const auto& p
: boost::get
<LuaArray
<std::string
>>(inp
)) {
2269 nmg
.addMask(p
.second
);
2272 g_proxyProtocolACL
= std::move(nmg
);
2275 luaCtx
.writeFunction("setProxyProtocolApplyACLToProxiedClients", [](bool apply
) {
2276 if (!checkConfigurationTime("setProxyProtocolApplyACLToProxiedClients")) {
2280 g_applyACLToProxiedClients
= apply
;
2283 luaCtx
.writeFunction("setProxyProtocolMaximumPayloadSize", [](uint64_t size
) {
2284 if (!checkConfigurationTime("setProxyProtocolMaximumPayloadSize")) {
2288 g_proxyProtocolMaximumSize
= std::max(static_cast<uint64_t>(16), size
);
2291 #ifndef DISABLE_RECVMMSG
2292 luaCtx
.writeFunction("setUDPMultipleMessagesVectorSize", [](uint64_t vSize
) {
2293 if (!checkConfigurationTime("setUDPMultipleMessagesVectorSize")) {
2296 #if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
2298 g_udpVectorSize
= vSize
;
2300 errlog("recvmmsg() support is not available!");
2301 g_outputBuffer
= "recvmmsg support is not available!\n";
2304 #endif /* DISABLE_RECVMMSG */
2306 luaCtx
.writeFunction("setAddEDNSToSelfGeneratedResponses", [](bool add
) {
2307 g_addEDNSToSelfGeneratedResponses
= add
;
2310 luaCtx
.writeFunction("setPayloadSizeOnSelfGeneratedAnswers", [](uint64_t payloadSize
) {
2311 if (payloadSize
< 512) {
2312 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!");
2313 g_outputBuffer
= "setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!";
2316 if (payloadSize
> s_udpIncomingBufferSize
) {
2317 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to %d instead!", s_udpIncomingBufferSize
);
2318 g_outputBuffer
= "setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to " + std::to_string(s_udpIncomingBufferSize
) + " instead";
2319 payloadSize
= s_udpIncomingBufferSize
;
2321 g_PayloadSizeSelfGenAnswers
= payloadSize
;
2324 #ifndef DISABLE_SECPOLL
2325 luaCtx
.writeFunction("showSecurityStatus", []() {
2326 setLuaNoSideEffect();
2327 g_outputBuffer
= std::to_string(dnsdist::metrics::g_stats
.securityStatus
) + "\n";
2330 luaCtx
.writeFunction("setSecurityPollSuffix", [](const std::string
& suffix
) {
2331 if (!checkConfigurationTime("setSecurityPollSuffix")) {
2334 g_secPollSuffix
= suffix
;
2337 luaCtx
.writeFunction("setSecurityPollInterval", [](time_t newInterval
) {
2338 if (newInterval
<= 0) {
2339 warnlog("setSecurityPollInterval() should be > 0, skipping");
2340 g_outputBuffer
= "setSecurityPollInterval() should be > 0, skipping";
2343 g_secPollInterval
= newInterval
;
2345 #endif /* DISABLE_SECPOLL */
2347 luaCtx
.writeFunction("setSyslogFacility", [](boost::variant
<int, std::string
> facility
) {
2348 if (!checkConfigurationTime("setSyslogFacility")) {
2352 if (facility
.type() == typeid(std::string
)) {
2353 static std::map
<std::string
, int> const facilities
= {
2354 {"local0", LOG_LOCAL0
},
2355 {"log_local0", LOG_LOCAL0
},
2356 {"local1", LOG_LOCAL1
},
2357 {"log_local1", LOG_LOCAL1
},
2358 {"local2", LOG_LOCAL2
},
2359 {"log_local2", LOG_LOCAL2
},
2360 {"local3", LOG_LOCAL3
},
2361 {"log_local3", LOG_LOCAL3
},
2362 {"local4", LOG_LOCAL4
},
2363 {"log_local4", LOG_LOCAL4
},
2364 {"local5", LOG_LOCAL5
},
2365 {"log_local5", LOG_LOCAL5
},
2366 {"local6", LOG_LOCAL6
},
2367 {"log_local6", LOG_LOCAL6
},
2368 {"local7", LOG_LOCAL7
},
2369 {"log_local7", LOG_LOCAL7
},
2370 /* most of these likely make very little sense
2371 for dnsdist, but why not? */
2373 {"log_kern", LOG_KERN
},
2375 {"log_user", LOG_USER
},
2377 {"log_mail", LOG_MAIL
},
2378 {"daemon", LOG_DAEMON
},
2379 {"log_daemon", LOG_DAEMON
},
2381 {"log_auth", LOG_AUTH
},
2382 {"syslog", LOG_SYSLOG
},
2383 {"log_syslog", LOG_SYSLOG
},
2385 {"log_lpr", LOG_LPR
},
2387 {"log_news", LOG_NEWS
},
2389 {"log_uucp", LOG_UUCP
},
2391 {"log_cron", LOG_CRON
},
2392 {"authpriv", LOG_AUTHPRIV
},
2393 {"log_authpriv", LOG_AUTHPRIV
},
2395 {"log_ftp", LOG_FTP
}};
2396 auto facilityStr
= boost::get
<std::string
>(facility
);
2397 toLowerInPlace(facilityStr
);
2398 auto it
= facilities
.find(facilityStr
);
2399 if (it
== facilities
.end()) {
2400 g_outputBuffer
= "Unknown facility '" + facilityStr
+ "' passed to setSyslogFacility()!\n";
2403 setSyslogFacility(it
->second
);
2406 setSyslogFacility(boost::get
<int>(facility
));
2410 typedef std::unordered_map
<std::string
, std::string
> tlscertificateopts_t
;
2411 luaCtx
.writeFunction("newTLSCertificate", [client
](const std::string
& cert
, boost::optional
<tlscertificateopts_t
> opts
) {
2412 std::shared_ptr
<TLSCertKeyPair
> result
= nullptr;
2416 #if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
2417 std::optional
<std::string
> key
, password
;
2419 if (opts
->count("key")) {
2420 key
= boost::get
<const string
>((*opts
)["key"]);
2422 if (opts
->count("password")) {
2423 password
= boost::get
<const string
>((*opts
)["password"]);
2426 result
= std::make_shared
<TLSCertKeyPair
>(cert
, std::move(key
), std::move(password
));
2431 luaCtx
.writeFunction("addDOHLocal", [client
](const std::string
& addr
, boost::optional
<boost::variant
<std::string
, std::shared_ptr
<TLSCertKeyPair
>, LuaArray
<std::string
>, LuaArray
<std::shared_ptr
<TLSCertKeyPair
>>>> certFiles
, boost::optional
<boost::variant
<std::string
, LuaArray
<std::string
>>> keyFiles
, boost::optional
<LuaTypeOrArrayOf
<std::string
>> urls
, boost::optional
<localbind_t
> vars
) {
2435 #ifdef HAVE_DNS_OVER_HTTPS
2436 if (!checkConfigurationTime("addDOHLocal")) {
2441 auto frontend
= std::make_shared
<DOHFrontend
>();
2442 if (getOptionalValue
<std::string
>(vars
, "library", frontend
->d_library
) == 0) {
2444 frontend
->d_library
= "nghttp2";
2445 #else /* HAVE_NGHTTP2 */
2446 frontend
->d_library
= "h2o";
2447 #endif /* HAVE_NGHTTP2 */
2449 if (frontend
->d_library
== "h2o") {
2450 #ifdef HAVE_LIBH2OEVLOOP
2451 frontend
= std::make_shared
<H2ODOHFrontend
>();
2452 // we _really_ need to set it again, as we just replaced the generic frontend by a new one
2453 frontend
->d_library
= "h2o";
2454 #else /* HAVE_LIBH2OEVLOOP */
2455 errlog("DOH bind %s is configured to use libh2o but the library is not available", addr
);
2457 #endif /* HAVE_LIBH2OEVLOOP */
2459 else if (frontend
->d_library
== "nghttp2") {
2460 #ifndef HAVE_NGHTTP2
2461 errlog("DOH bind %s is configured to use nghttp2 but the library is not available", addr
);
2463 #endif /* HAVE_NGHTTP2 */
2466 errlog("DOH bind %s is configured to use an unknown library ('%s')", addr
, frontend
->d_library
);
2471 if (certFiles
&& !certFiles
->empty()) {
2472 if (!loadTLSCertificateAndKeys("addDOHLocal", frontend
->d_tlsContext
.d_tlsConfig
.d_certKeyPairs
, *certFiles
, *keyFiles
)) {
2476 frontend
->d_tlsContext
.d_addr
= ComboAddress(addr
, 443);
2479 frontend
->d_tlsContext
.d_addr
= ComboAddress(addr
, 80);
2480 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());
2485 if (urls
->type() == typeid(std::string
)) {
2486 frontend
->d_urls
.insert(boost::get
<std::string
>(*urls
));
2488 else if (urls
->type() == typeid(LuaArray
<std::string
>)) {
2489 auto urlsVect
= boost::get
<LuaArray
<std::string
>>(*urls
);
2490 for (const auto& p
: urlsVect
) {
2491 frontend
->d_urls
.insert(p
.second
);
2496 frontend
->d_urls
.insert("/dns-query");
2499 bool reusePort
= false;
2500 int tcpFastOpenQueueSize
= 0;
2501 int tcpListenQueueSize
= 0;
2502 uint64_t maxInFlightQueriesPerConn
= 0;
2503 uint64_t tcpMaxConcurrentConnections
= 0;
2504 std::string interface
;
2506 std::vector
<std::pair
<ComboAddress
, int>> additionalAddresses
;
2507 bool enableProxyProtocol
= true;
2510 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConnections
, enableProxyProtocol
);
2511 getOptionalValue
<int>(vars
, "idleTimeout", frontend
->d_idleTimeout
);
2512 getOptionalValue
<std::string
>(vars
, "serverTokens", frontend
->d_serverTokens
);
2513 getOptionalValue
<std::string
>(vars
, "provider", frontend
->d_tlsContext
.d_provider
);
2514 boost::algorithm::to_lower(frontend
->d_tlsContext
.d_provider
);
2515 getOptionalValue
<bool>(vars
, "proxyProtocolOutsideTLS", frontend
->d_tlsContext
.d_proxyProtocolOutsideTLS
);
2517 LuaAssociativeTable
<std::string
> customResponseHeaders
;
2518 if (getOptionalValue
<decltype(customResponseHeaders
)>(vars
, "customResponseHeaders", customResponseHeaders
) > 0) {
2519 for (auto const& headerMap
: customResponseHeaders
) {
2520 auto headerResponse
= std::pair(boost::to_lower_copy(headerMap
.first
), headerMap
.second
);
2521 frontend
->d_customResponseHeaders
.insert(headerResponse
);
2525 getOptionalValue
<bool>(vars
, "sendCacheControlHeaders", frontend
->d_sendCacheControlHeaders
);
2526 getOptionalValue
<bool>(vars
, "keepIncomingHeaders", frontend
->d_keepIncomingHeaders
);
2527 getOptionalValue
<bool>(vars
, "trustForwardedForHeader", frontend
->d_trustForwardedForHeader
);
2528 getOptionalValue
<bool>(vars
, "earlyACLDrop", frontend
->d_earlyACLDrop
);
2529 getOptionalValue
<int>(vars
, "internalPipeBufferSize", frontend
->d_internalPipeBufferSize
);
2530 getOptionalValue
<bool>(vars
, "exactPathMatching", frontend
->d_exactPathMatching
);
2532 LuaArray
<std::string
> addresses
;
2533 if (getOptionalValue
<decltype(addresses
)>(vars
, "additionalAddresses", addresses
) > 0) {
2534 for (const auto& [_
, add
] : addresses
) {
2536 ComboAddress
address(add
);
2537 additionalAddresses
.emplace_back(address
, -1);
2539 catch (const PDNSException
& e
) {
2540 errlog("Unable to parse additional address %s for DOH bind: %s", add
, e
.reason
);
2546 parseTLSConfig(frontend
->d_tlsContext
.d_tlsConfig
, "addDOHLocal", vars
);
2548 bool ignoreTLSConfigurationErrors
= false;
2549 if (getOptionalValue
<bool>(vars
, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors
) > 0 && ignoreTLSConfigurationErrors
) {
2550 // we are asked to try to load the certificates so we can return a potential error
2551 // and properly ignore the frontend before actually launching it
2553 std::map
<int, std::string
> ocspResponses
= {};
2554 auto ctx
= libssl_init_server_context(frontend
->d_tlsContext
.d_tlsConfig
, ocspResponses
);
2556 catch (const std::runtime_error
& e
) {
2557 errlog("Ignoring DoH frontend: '%s'", e
.what());
2562 checkAllParametersConsumed("addDOHLocal", vars
);
2565 if (useTLS
&& frontend
->d_library
== "nghttp2") {
2566 if (!frontend
->d_tlsContext
.d_provider
.empty()) {
2567 vinfolog("Loading TLS provider '%s'", frontend
->d_tlsContext
.d_provider
);
2571 const std::string
provider("openssl");
2573 const std::string
provider("gnutls");
2575 vinfolog("Loading default TLS provider '%s'", provider
);
2579 g_dohlocals
.push_back(frontend
);
2580 auto clientState
= std::make_unique
<ClientState
>(frontend
->d_tlsContext
.d_addr
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, enableProxyProtocol
);
2581 clientState
->dohFrontend
= std::move(frontend
);
2582 clientState
->d_additionalAddresses
= std::move(additionalAddresses
);
2584 if (tcpListenQueueSize
> 0) {
2585 clientState
->tcpListenQueueSize
= tcpListenQueueSize
;
2587 if (tcpMaxConcurrentConnections
> 0) {
2588 clientState
->d_tcpConcurrentConnectionsLimit
= tcpMaxConcurrentConnections
;
2590 g_frontends
.push_back(std::move(clientState
));
2591 #else /* HAVE_DNS_OVER_HTTPS */
2592 throw std::runtime_error("addDOHLocal() called but DNS over HTTPS support is not present!");
2593 #endif /* HAVE_DNS_OVER_HTTPS */
2596 // NOLINTNEXTLINE(performance-unnecessary-value-param): somehow clang-tidy gets confused about the fact vars could be const while it cannot
2597 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
) {
2601 #ifdef HAVE_DNS_OVER_HTTP3
2602 if (!checkConfigurationTime("addDOH3Local")) {
2607 auto frontend
= std::make_shared
<DOH3Frontend
>();
2608 if (!loadTLSCertificateAndKeys("addDOH3Local", frontend
->d_quicheParams
.d_tlsConfig
.d_certKeyPairs
, certFiles
, keyFiles
)) {
2611 frontend
->d_local
= ComboAddress(addr
, 443);
2613 bool reusePort
= false;
2614 int tcpFastOpenQueueSize
= 0;
2615 int tcpListenQueueSize
= 0;
2616 uint64_t maxInFlightQueriesPerConn
= 0;
2617 uint64_t tcpMaxConcurrentConnections
= 0;
2618 std::string interface
;
2620 std::vector
<std::pair
<ComboAddress
, int>> additionalAddresses
;
2621 bool enableProxyProtocol
= true;
2624 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConnections
, enableProxyProtocol
);
2625 if (maxInFlightQueriesPerConn
> 0) {
2626 frontend
->d_quicheParams
.d_maxInFlight
= maxInFlightQueriesPerConn
;
2628 getOptionalValue
<int>(vars
, "internalPipeBufferSize", frontend
->d_internalPipeBufferSize
);
2629 getOptionalValue
<int>(vars
, "idleTimeout", frontend
->d_quicheParams
.d_idleTimeout
);
2630 getOptionalValue
<std::string
>(vars
, "keyLogFile", frontend
->d_quicheParams
.d_keyLogFile
);
2632 std::string valueStr
;
2633 if (getOptionalValue
<std::string
>(vars
, "congestionControlAlgo", valueStr
) > 0) {
2634 if (dnsdist::doq::s_available_cc_algorithms
.count(valueStr
) > 0) {
2635 frontend
->d_quicheParams
.d_ccAlgo
= valueStr
;
2638 warnlog("Ignoring unknown value '%s' for 'congestionControlAlgo' on 'addDOH3Local'", valueStr
);
2642 parseTLSConfig(frontend
->d_quicheParams
.d_tlsConfig
, "addDOH3Local", vars
);
2644 bool ignoreTLSConfigurationErrors
= false;
2645 if (getOptionalValue
<bool>(vars
, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors
) > 0 && ignoreTLSConfigurationErrors
) {
2646 // we are asked to try to load the certificates so we can return a potential error
2647 // and properly ignore the frontend before actually launching it
2649 std::map
<int, std::string
> ocspResponses
= {};
2650 auto ctx
= libssl_init_server_context(frontend
->d_quicheParams
.d_tlsConfig
, ocspResponses
);
2652 catch (const std::runtime_error
& e
) {
2653 errlog("Ignoring DoH3 frontend: '%s'", e
.what());
2658 checkAllParametersConsumed("addDOH3Local", vars
);
2660 g_doh3locals
.push_back(frontend
);
2661 auto clientState
= std::make_unique
<ClientState
>(frontend
->d_local
, false, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, enableProxyProtocol
);
2662 clientState
->doh3Frontend
= frontend
;
2663 clientState
->d_additionalAddresses
= std::move(additionalAddresses
);
2665 g_frontends
.push_back(std::move(clientState
));
2667 throw std::runtime_error("addDOH3Local() called but DNS over HTTP/3 support is not present!");
2671 // NOLINTNEXTLINE(performance-unnecessary-value-param): somehow clang-tidy gets confused about the fact vars could be const while it cannot
2672 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
) {
2676 #ifdef HAVE_DNS_OVER_QUIC
2677 if (!checkConfigurationTime("addDOQLocal")) {
2682 auto frontend
= std::make_shared
<DOQFrontend
>();
2683 if (!loadTLSCertificateAndKeys("addDOQLocal", frontend
->d_quicheParams
.d_tlsConfig
.d_certKeyPairs
, certFiles
, keyFiles
)) {
2686 frontend
->d_local
= ComboAddress(addr
, 853);
2688 bool reusePort
= false;
2689 int tcpFastOpenQueueSize
= 0;
2690 int tcpListenQueueSize
= 0;
2691 uint64_t maxInFlightQueriesPerConn
= 0;
2692 uint64_t tcpMaxConcurrentConnections
= 0;
2693 std::string interface
;
2695 std::vector
<std::pair
<ComboAddress
, int>> additionalAddresses
;
2696 bool enableProxyProtocol
= true;
2699 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConnections
, enableProxyProtocol
);
2700 if (maxInFlightQueriesPerConn
> 0) {
2701 frontend
->d_quicheParams
.d_maxInFlight
= maxInFlightQueriesPerConn
;
2703 getOptionalValue
<int>(vars
, "internalPipeBufferSize", frontend
->d_internalPipeBufferSize
);
2704 getOptionalValue
<int>(vars
, "idleTimeout", frontend
->d_quicheParams
.d_idleTimeout
);
2705 getOptionalValue
<std::string
>(vars
, "keyLogFile", frontend
->d_quicheParams
.d_keyLogFile
);
2707 std::string valueStr
;
2708 if (getOptionalValue
<std::string
>(vars
, "congestionControlAlgo", valueStr
) > 0) {
2709 if (dnsdist::doq::s_available_cc_algorithms
.count(valueStr
) > 0) {
2710 frontend
->d_quicheParams
.d_ccAlgo
= std::move(valueStr
);
2713 warnlog("Ignoring unknown value '%s' for 'congestionControlAlgo' on 'addDOQLocal'", valueStr
);
2717 parseTLSConfig(frontend
->d_quicheParams
.d_tlsConfig
, "addDOQLocal", vars
);
2719 bool ignoreTLSConfigurationErrors
= false;
2720 if (getOptionalValue
<bool>(vars
, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors
) > 0 && ignoreTLSConfigurationErrors
) {
2721 // we are asked to try to load the certificates so we can return a potential error
2722 // and properly ignore the frontend before actually launching it
2724 std::map
<int, std::string
> ocspResponses
= {};
2725 auto ctx
= libssl_init_server_context(frontend
->d_quicheParams
.d_tlsConfig
, ocspResponses
);
2727 catch (const std::runtime_error
& e
) {
2728 errlog("Ignoring DoQ frontend: '%s'", e
.what());
2733 checkAllParametersConsumed("addDOQLocal", vars
);
2735 g_doqlocals
.push_back(frontend
);
2736 auto clientState
= std::make_unique
<ClientState
>(frontend
->d_local
, false, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, enableProxyProtocol
);
2737 clientState
->doqFrontend
= std::move(frontend
);
2738 clientState
->d_additionalAddresses
= std::move(additionalAddresses
);
2740 g_frontends
.push_back(std::move(clientState
));
2742 throw std::runtime_error("addDOQLocal() called but DNS over QUIC support is not present!");
2746 luaCtx
.writeFunction("showDOQFrontends", []() {
2747 #ifdef HAVE_DNS_OVER_QUIC
2748 setLuaNoSideEffect();
2751 boost::format
fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d");
2752 ret
<< (fmt
% "#" % "Address" % "Bad Version" % "Invalid Token" % "Errors" % "Valid") << endl
;
2754 for (const auto& ctx
: g_doqlocals
) {
2755 ret
<< (fmt
% counter
% ctx
->d_local
.toStringWithPort() % ctx
->d_doqUnsupportedVersionErrors
% ctx
->d_doqInvalidTokensReceived
% ctx
->d_errorResponses
% ctx
->d_validResponses
) << endl
;
2758 g_outputBuffer
= ret
.str();
2760 catch (const std::exception
& e
) {
2761 g_outputBuffer
= e
.what();
2765 g_outputBuffer
= "DNS over QUIC support is not present!\n";
2769 luaCtx
.writeFunction("showDOHFrontends", []() {
2770 #ifdef HAVE_DNS_OVER_HTTPS
2771 setLuaNoSideEffect();
2774 boost::format
fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d");
2775 ret
<< (fmt
% "#" % "Address" % "HTTP" % "HTTP/1" % "HTTP/2" % "GET" % "POST" % "Bad" % "Errors" % "Redirects" % "Valid" % "# ticket keys" % "Rotation delay" % "Next rotation") << endl
;
2777 for (const auto& ctx
: g_dohlocals
) {
2778 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
;
2781 g_outputBuffer
= ret
.str();
2783 catch (const std::exception
& e
) {
2784 g_outputBuffer
= e
.what();
2788 g_outputBuffer
= "DNS over HTTPS support is not present!\n";
2792 luaCtx
.writeFunction("showDOH3Frontends", []() {
2793 #ifdef HAVE_DNS_OVER_HTTP3
2794 setLuaNoSideEffect();
2797 boost::format
fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d");
2798 ret
<< (fmt
% "#" % "Address" % "Bad Version" % "Invalid Token" % "Errors" % "Valid") << endl
;
2800 for (const auto& ctx
: g_doh3locals
) {
2801 ret
<< (fmt
% counter
% ctx
->d_local
.toStringWithPort() % ctx
->d_doh3UnsupportedVersionErrors
% ctx
->d_doh3InvalidTokensReceived
% ctx
->d_errorResponses
% ctx
->d_validResponses
) << endl
;
2804 g_outputBuffer
= ret
.str();
2806 catch (const std::exception
& e
) {
2807 g_outputBuffer
= e
.what();
2811 g_outputBuffer
= "DNS over HTTP3 support is not present!\n";
2815 luaCtx
.writeFunction("showDOHResponseCodes", []() {
2816 #ifdef HAVE_DNS_OVER_HTTPS
2817 setLuaNoSideEffect();
2820 boost::format
fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d %-15d %-15d");
2821 g_outputBuffer
= "\n- HTTP/1:\n\n";
2822 ret
<< (fmt
% "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others") << endl
;
2824 for (const auto& ctx
: g_dohlocals
) {
2825 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
;
2828 g_outputBuffer
+= ret
.str();
2831 g_outputBuffer
+= "\n- HTTP/2:\n\n";
2832 ret
<< (fmt
% "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others") << endl
;
2834 for (const auto& ctx
: g_dohlocals
) {
2835 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
;
2838 g_outputBuffer
+= ret
.str();
2840 catch (const std::exception
& e
) {
2841 g_outputBuffer
= e
.what();
2845 g_outputBuffer
= "DNS over HTTPS support is not present!\n";
2849 luaCtx
.writeFunction("getDOHFrontend", [client
](uint64_t index
) {
2850 std::shared_ptr
<DOHFrontend
> result
= nullptr;
2854 #ifdef HAVE_DNS_OVER_HTTPS
2855 setLuaNoSideEffect();
2857 if (index
< g_dohlocals
.size()) {
2858 result
= g_dohlocals
.at(index
);
2861 errlog("Error: trying to get DOH frontend with index %d but we only have %d frontend(s)\n", index
, g_dohlocals
.size());
2862 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";
2865 catch (const std::exception
& e
) {
2866 g_outputBuffer
= "Error while trying to get DOH frontend with index " + std::to_string(index
) + ": " + string(e
.what()) + "\n";
2867 errlog("Error while trying to get DOH frontend with index %d: %s\n", index
, string(e
.what()));
2870 g_outputBuffer
="DNS over HTTPS support is not present!\n";
2875 luaCtx
.writeFunction("getDOHFrontendCount", []() {
2876 setLuaNoSideEffect();
2877 return g_dohlocals
.size();
2880 luaCtx
.registerFunction
<void (std::shared_ptr
<DOHFrontend
>::*)()>("reloadCertificates", [](std::shared_ptr
<DOHFrontend
> frontend
) {
2881 if (frontend
!= nullptr) {
2882 frontend
->reloadCertificates();
2886 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
) {
2887 #ifdef HAVE_DNS_OVER_HTTPS
2888 if (frontend
!= nullptr) {
2889 if (loadTLSCertificateAndKeys("DOHFrontend::loadNewCertificatesAndKeys", frontend
->d_tlsContext
.d_tlsConfig
.d_certKeyPairs
, certFiles
, keyFiles
)) {
2890 frontend
->reloadCertificates();
2896 luaCtx
.registerFunction
<void (std::shared_ptr
<DOHFrontend
>::*)()>("rotateTicketsKey", [](std::shared_ptr
<DOHFrontend
> frontend
) {
2897 if (frontend
!= nullptr) {
2898 frontend
->rotateTicketsKey(time(nullptr));
2902 luaCtx
.registerFunction
<void (std::shared_ptr
<DOHFrontend
>::*)(const std::string
&)>("loadTicketsKeys", [](std::shared_ptr
<DOHFrontend
> frontend
, const std::string
& file
) {
2903 if (frontend
!= nullptr) {
2904 frontend
->loadTicketsKeys(file
);
2908 luaCtx
.registerFunction
<void (std::shared_ptr
<DOHFrontend
>::*)(const LuaArray
<std::shared_ptr
<DOHResponseMapEntry
>>&)>("setResponsesMap", [](std::shared_ptr
<DOHFrontend
> frontend
, const LuaArray
<std::shared_ptr
<DOHResponseMapEntry
>>& map
) {
2909 if (frontend
!= nullptr) {
2910 auto newMap
= std::make_shared
<std::vector
<std::shared_ptr
<DOHResponseMapEntry
>>>();
2911 newMap
->reserve(map
.size());
2913 for (const auto& entry
: map
) {
2914 newMap
->push_back(entry
.second
);
2917 frontend
->d_responsesMap
= std::move(newMap
);
2921 luaCtx
.writeFunction("addTLSLocal", [client
](const std::string
& addr
, boost::variant
<std::string
, std::shared_ptr
<TLSCertKeyPair
>, LuaArray
<std::string
>, LuaArray
<std::shared_ptr
<TLSCertKeyPair
>>> certFiles
, LuaTypeOrArrayOf
<std::string
> keyFiles
, boost::optional
<localbind_t
> vars
) {
2925 #ifdef HAVE_DNS_OVER_TLS
2926 if (!checkConfigurationTime("addTLSLocal")) {
2931 auto frontend
= std::make_shared
<TLSFrontend
>(TLSFrontend::ALPN::DoT
);
2932 if (!loadTLSCertificateAndKeys("addTLSLocal", frontend
->d_tlsConfig
.d_certKeyPairs
, certFiles
, keyFiles
)) {
2936 bool reusePort
= false;
2937 int tcpFastOpenQueueSize
= 0;
2938 int tcpListenQueueSize
= 0;
2939 uint64_t maxInFlightQueriesPerConn
= 0;
2940 uint64_t tcpMaxConcurrentConns
= 0;
2941 std::string interface
;
2943 std::vector
<std::pair
<ComboAddress
, int>> additionalAddresses
;
2944 bool enableProxyProtocol
= true;
2947 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConns
, enableProxyProtocol
);
2949 getOptionalValue
<std::string
>(vars
, "provider", frontend
->d_provider
);
2950 boost::algorithm::to_lower(frontend
->d_provider
);
2951 getOptionalValue
<bool>(vars
, "proxyProtocolOutsideTLS", frontend
->d_proxyProtocolOutsideTLS
);
2953 LuaArray
<std::string
> addresses
;
2954 if (getOptionalValue
<decltype(addresses
)>(vars
, "additionalAddresses", addresses
) > 0) {
2955 for (const auto& [_
, add
] : addresses
) {
2957 ComboAddress
address(add
);
2958 additionalAddresses
.emplace_back(address
, -1);
2960 catch (const PDNSException
& e
) {
2961 errlog("Unable to parse additional address %s for DoT bind: %s", add
, e
.reason
);
2967 parseTLSConfig(frontend
->d_tlsConfig
, "addTLSLocal", vars
);
2969 bool ignoreTLSConfigurationErrors
= false;
2970 if (getOptionalValue
<bool>(vars
, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors
) > 0 && ignoreTLSConfigurationErrors
) {
2971 // we are asked to try to load the certificates so we can return a potential error
2972 // and properly ignore the frontend before actually launching it
2974 std::map
<int, std::string
> ocspResponses
= {};
2975 auto ctx
= libssl_init_server_context(frontend
->d_tlsConfig
, ocspResponses
);
2977 catch (const std::runtime_error
& e
) {
2978 errlog("Ignoring TLS frontend: '%s'", e
.what());
2983 checkAllParametersConsumed("addTLSLocal", vars
);
2987 frontend
->d_addr
= ComboAddress(addr
, 853);
2988 if (!frontend
->d_provider
.empty()) {
2989 vinfolog("Loading TLS provider '%s'", frontend
->d_provider
);
2993 const std::string
provider("openssl");
2995 const std::string
provider("gnutls");
2997 vinfolog("Loading default TLS provider '%s'", provider
);
2999 // only works pre-startup, so no sync necessary
3000 auto clientState
= std::make_unique
<ClientState
>(frontend
->d_addr
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, enableProxyProtocol
);
3001 clientState
->tlsFrontend
= frontend
;
3002 clientState
->d_additionalAddresses
= std::move(additionalAddresses
);
3003 if (tcpListenQueueSize
> 0) {
3004 clientState
->tcpListenQueueSize
= tcpListenQueueSize
;
3006 if (maxInFlightQueriesPerConn
> 0) {
3007 clientState
->d_maxInFlightQueriesPerConn
= maxInFlightQueriesPerConn
;
3009 if (tcpMaxConcurrentConns
> 0) {
3010 clientState
->d_tcpConcurrentConnectionsLimit
= tcpMaxConcurrentConns
;
3013 g_tlslocals
.push_back(clientState
->tlsFrontend
);
3014 g_frontends
.push_back(std::move(clientState
));
3016 catch (const std::exception
& e
) {
3017 g_outputBuffer
= "Error: " + string(e
.what()) + "\n";
3020 throw std::runtime_error("addTLSLocal() called but DNS over TLS support is not present!");
3024 luaCtx
.writeFunction("showTLSContexts", []() {
3025 #ifdef HAVE_DNS_OVER_TLS
3026 setLuaNoSideEffect();
3029 boost::format
fmt("%1$-3d %2$-20.20s %|25t|%3$-14d %|40t|%4$-14d %|54t|%5$-21.21s");
3031 ret
<< (fmt
% "#" % "Address" % "# ticket keys" % "Rotation delay" % "Next rotation") << endl
;
3033 for (const auto& ctx
: g_tlslocals
) {
3034 ret
<< (fmt
% counter
% ctx
->d_addr
.toStringWithPort() % ctx
->getTicketsKeysCount() % ctx
->getTicketsKeyRotationDelay() % ctx
->getNextTicketsKeyRotation()) << endl
;
3037 g_outputBuffer
= ret
.str();
3039 catch (const std::exception
& e
) {
3040 g_outputBuffer
= e
.what();
3044 g_outputBuffer
= "DNS over TLS support is not present!\n";
3048 luaCtx
.writeFunction("getTLSContext", [](uint64_t index
) {
3049 std::shared_ptr
<TLSCtx
> result
= nullptr;
3050 #ifdef HAVE_DNS_OVER_TLS
3051 setLuaNoSideEffect();
3053 if (index
< g_tlslocals
.size()) {
3054 result
= g_tlslocals
.at(index
)->getContext();
3057 errlog("Error: trying to get TLS context with index %d but we only have %d context(s)\n", index
, g_tlslocals
.size());
3058 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";
3061 catch (const std::exception
& e
) {
3062 g_outputBuffer
= "Error while trying to get TLS context with index " + std::to_string(index
) + ": " + string(e
.what()) + "\n";
3063 errlog("Error while trying to get TLS context with index %d: %s\n", index
, string(e
.what()));
3066 g_outputBuffer
="DNS over TLS support is not present!\n";
3071 luaCtx
.writeFunction("getTLSFrontend", [](uint64_t index
) {
3072 std::shared_ptr
<TLSFrontend
> result
= nullptr;
3073 #ifdef HAVE_DNS_OVER_TLS
3074 setLuaNoSideEffect();
3076 if (index
< g_tlslocals
.size()) {
3077 result
= g_tlslocals
.at(index
);
3080 errlog("Error: trying to get TLS frontend with index %d but we only have %d frontends\n", index
, g_tlslocals
.size());
3081 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";
3084 catch (const std::exception
& e
) {
3085 g_outputBuffer
= "Error while trying to get TLS frontend with index " + std::to_string(index
) + ": " + string(e
.what()) + "\n";
3086 errlog("Error while trying to get TLS frontend with index %d: %s\n", index
, string(e
.what()));
3089 g_outputBuffer
="DNS over TLS support is not present!\n";
3094 luaCtx
.writeFunction("getTLSFrontendCount", []() {
3095 setLuaNoSideEffect();
3096 return g_tlslocals
.size();
3099 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSCtx
>::*)()>("rotateTicketsKey", [](std::shared_ptr
<TLSCtx
>& ctx
) {
3100 if (ctx
!= nullptr) {
3101 ctx
->rotateTicketsKey(time(nullptr));
3105 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSCtx
>::*)(const std::string
&)>("loadTicketsKeys", [](std::shared_ptr
<TLSCtx
>& ctx
, const std::string
& file
) {
3106 if (ctx
!= nullptr) {
3107 ctx
->loadTicketsKeys(file
);
3111 luaCtx
.registerFunction
<std::string (std::shared_ptr
<TLSFrontend
>::*)() const>("getAddressAndPort", [](const std::shared_ptr
<TLSFrontend
>& frontend
) {
3112 if (frontend
== nullptr) {
3113 return std::string();
3115 return frontend
->d_addr
.toStringWithPort();
3118 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSFrontend
>::*)()>("rotateTicketsKey", [](std::shared_ptr
<TLSFrontend
>& frontend
) {
3119 if (frontend
== nullptr) {
3122 auto ctx
= frontend
->getContext();
3124 ctx
->rotateTicketsKey(time(nullptr));
3128 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSFrontend
>::*)(const std::string
&)>("loadTicketsKeys", [](std::shared_ptr
<TLSFrontend
>& frontend
, const std::string
& file
) {
3129 if (frontend
== nullptr) {
3132 auto ctx
= frontend
->getContext();
3134 ctx
->loadTicketsKeys(file
);
3138 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSFrontend
>::*)()>("reloadCertificates", [](std::shared_ptr
<TLSFrontend
>& frontend
) {
3139 if (frontend
== nullptr) {
3142 frontend
->setupTLS();
3145 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSFrontend
>::*)(boost::variant
<std::string
, std::shared_ptr
<TLSCertKeyPair
>, LuaArray
<std::string
>, LuaArray
<std::shared_ptr
<TLSCertKeyPair
>>> certFiles
, LuaTypeOrArrayOf
<std::string
> keyFiles
)>("loadNewCertificatesAndKeys", [](std::shared_ptr
<TLSFrontend
>& frontend
, boost::variant
<std::string
, std::shared_ptr
<TLSCertKeyPair
>, LuaArray
<std::string
>, LuaArray
<std::shared_ptr
<TLSCertKeyPair
>>> certFiles
, LuaTypeOrArrayOf
<std::string
> keyFiles
) {
3146 #ifdef HAVE_DNS_OVER_TLS
3147 if (loadTLSCertificateAndKeys("TLSFrontend::loadNewCertificatesAndKeys", frontend
->d_tlsConfig
.d_certKeyPairs
, certFiles
, keyFiles
)) {
3148 frontend
->setupTLS();
3153 luaCtx
.writeFunction("reloadAllCertificates", []() {
3154 for (auto& frontend
: g_frontends
) {
3159 #ifdef HAVE_DNSCRYPT
3160 if (frontend
->dnscryptCtx
) {
3161 frontend
->dnscryptCtx
->reloadCertificates();
3163 #endif /* HAVE_DNSCRYPT */
3164 #ifdef HAVE_DNS_OVER_TLS
3165 if (frontend
->tlsFrontend
) {
3166 frontend
->tlsFrontend
->setupTLS();
3168 #endif /* HAVE_DNS_OVER_TLS */
3169 #ifdef HAVE_DNS_OVER_HTTPS
3170 if (frontend
->dohFrontend
) {
3171 frontend
->dohFrontend
->reloadCertificates();
3173 #endif /* HAVE_DNS_OVER_HTTPS */
3175 catch (const std::exception
& e
) {
3176 errlog("Error reloading certificates for frontend %s: %s", frontend
->local
.toStringWithPort(), e
.what());
3181 luaCtx
.writeFunction("setAllowEmptyResponse", [](bool allow
) { g_allowEmptyResponse
= allow
; });
3182 luaCtx
.writeFunction("setDropEmptyQueries", [](bool drop
) { extern bool g_dropEmptyQueries
; g_dropEmptyQueries
= drop
; });
3184 #if defined(HAVE_LIBSSL) && defined(HAVE_OCSP_BASIC_SIGN) && !defined(DISABLE_OCSP_STAPLING)
3185 luaCtx
.writeFunction("generateOCSPResponse", [client
](const std::string
& certFile
, const std::string
& caCert
, const std::string
& caKey
, const std::string
& outFile
, int ndays
, int nmin
) {
3190 libssl_generate_ocsp_response(certFile
, caCert
, caKey
, outFile
, ndays
, nmin
);
3192 #endif /* HAVE_LIBSSL && HAVE_OCSP_BASIC_SIGN && !DISABLE_OCSP_STAPLING */
3194 luaCtx
.writeFunction("addCapabilitiesToRetain", [](LuaTypeOrArrayOf
<std::string
> caps
) {
3195 if (!checkConfigurationTime("addCapabilitiesToRetain")) {
3199 if (caps
.type() == typeid(std::string
)) {
3200 g_capabilitiesToRetain
.insert(boost::get
<std::string
>(caps
));
3202 else if (caps
.type() == typeid(LuaArray
<std::string
>)) {
3203 for (const auto& cap
: boost::get
<LuaArray
<std::string
>>(caps
)) {
3204 g_capabilitiesToRetain
.insert(cap
.second
);
3209 luaCtx
.writeFunction("setUDPSocketBufferSizes", [client
](uint64_t recv
, uint64_t snd
) {
3213 if (!checkConfigurationTime("setUDPSocketBufferSizes")) {
3216 checkParameterBound("setUDPSocketBufferSizes", recv
, std::numeric_limits
<uint32_t>::max());
3217 checkParameterBound("setUDPSocketBufferSizes", snd
, std::numeric_limits
<uint32_t>::max());
3220 g_socketUDPSendBuffer
= snd
;
3221 g_socketUDPRecvBuffer
= recv
;
3224 luaCtx
.writeFunction("setRandomizedOutgoingSockets", [](bool randomized
) {
3225 DownstreamState::s_randomizeSockets
= randomized
;
3228 luaCtx
.writeFunction("setRandomizedIdsOverUDP", [](bool randomized
) {
3229 DownstreamState::s_randomizeIDs
= randomized
;
3232 #if defined(HAVE_LIBSSL) && !defined(HAVE_TLS_PROVIDERS)
3233 luaCtx
.writeFunction("loadTLSEngine", [client
](const std::string
& engineName
, boost::optional
<std::string
> defaultString
) {
3238 auto [success
, error
] = libssl_load_engine(engineName
, defaultString
? std::optional
<std::string
>(*defaultString
) : std::nullopt
);
3240 g_outputBuffer
= "Error while trying to load TLS engine '" + engineName
+ "': " + error
+ "\n";
3241 errlog("Error while trying to load TLS engine '%s': %s", engineName
, error
);
3244 #endif /* HAVE_LIBSSL && !HAVE_TLS_PROVIDERS */
3246 #if defined(HAVE_LIBSSL) && OPENSSL_VERSION_MAJOR >= 3 && defined(HAVE_TLS_PROVIDERS)
3247 luaCtx
.writeFunction("loadTLSProvider", [client
](const std::string
& providerName
) {
3252 auto [success
, error
] = libssl_load_provider(providerName
);
3254 g_outputBuffer
= "Error while trying to load TLS provider '" + providerName
+ "': " + error
+ "\n";
3255 errlog("Error while trying to load TLS provider '%s': %s", providerName
, error
);
3258 #endif /* HAVE_LIBSSL && OPENSSL_VERSION_MAJOR >= 3 && HAVE_TLS_PROVIDERS */
3260 luaCtx
.writeFunction("newThread", [client
, configCheck
](const std::string
& code
) {
3261 if (client
|| configCheck
) {
3264 std::thread
newThread(LuaThread
, code
);
3269 luaCtx
.writeFunction("declareMetric", [](const std::string
& name
, const std::string
& type
, const std::string
& description
, boost::optional
<std::string
> customName
) {
3270 auto result
= dnsdist::metrics::declareCustomMetric(name
, type
, description
, customName
? std::optional
<std::string
>(*customName
) : std::nullopt
);
3272 g_outputBuffer
+= *result
+ "\n";
3273 errlog("Error in declareMetric: %s", *result
);
3278 luaCtx
.writeFunction("incMetric", [](const std::string
& name
, boost::optional
<uint64_t> step
) {
3279 auto result
= dnsdist::metrics::incrementCustomCounter(name
, step
? *step
: 1);
3280 if (const auto* errorStr
= std::get_if
<dnsdist::metrics::Error
>(&result
)) {
3281 g_outputBuffer
= *errorStr
+ "'\n";
3282 errlog("Error in incMetric: %s", *errorStr
);
3283 return static_cast<uint64_t>(0);
3285 return std::get
<uint64_t>(result
);
3287 luaCtx
.writeFunction("decMetric", [](const std::string
& name
, boost::optional
<uint64_t> step
) {
3288 auto result
= dnsdist::metrics::decrementCustomCounter(name
, step
? *step
: 1);
3289 if (const auto* errorStr
= std::get_if
<dnsdist::metrics::Error
>(&result
)) {
3290 g_outputBuffer
= *errorStr
+ "'\n";
3291 errlog("Error in decMetric: %s", *errorStr
);
3292 return static_cast<uint64_t>(0);
3294 return std::get
<uint64_t>(result
);
3296 luaCtx
.writeFunction("setMetric", [](const std::string
& name
, const double value
) -> double {
3297 auto result
= dnsdist::metrics::setCustomGauge(name
, value
);
3298 if (const auto* errorStr
= std::get_if
<dnsdist::metrics::Error
>(&result
)) {
3299 g_outputBuffer
= *errorStr
+ "'\n";
3300 errlog("Error in setMetric: %s", *errorStr
);
3303 return std::get
<double>(result
);
3305 luaCtx
.writeFunction("getMetric", [](const std::string
& name
) {
3306 auto result
= dnsdist::metrics::getCustomMetric(name
);
3307 if (const auto* errorStr
= std::get_if
<dnsdist::metrics::Error
>(&result
)) {
3308 g_outputBuffer
= *errorStr
+ "'\n";
3309 errlog("Error in getMetric: %s", *errorStr
);
3312 return std::get
<double>(result
);
3316 vector
<std::function
<void(void)>> setupLua(LuaContext
& luaCtx
, bool client
, bool configCheck
, const std::string
& config
)
3318 // this needs to exist only during the parsing of the configuration
3319 // and cannot be captured by lambdas
3320 g_launchWork
= std::vector
<std::function
<void(void)>>();
3322 setupLuaActions(luaCtx
);
3323 setupLuaConfig(luaCtx
, client
, configCheck
);
3324 setupLuaBindings(luaCtx
, client
, configCheck
);
3325 setupLuaBindingsDNSCrypt(luaCtx
, client
);
3326 setupLuaBindingsDNSParser(luaCtx
);
3327 setupLuaBindingsDNSQuestion(luaCtx
);
3328 setupLuaBindingsKVS(luaCtx
, client
);
3329 setupLuaBindingsNetwork(luaCtx
, client
);
3330 setupLuaBindingsPacketCache(luaCtx
, client
);
3331 setupLuaBindingsProtoBuf(luaCtx
, client
, configCheck
);
3332 setupLuaBindingsRings(luaCtx
, client
);
3333 setupLuaInspection(luaCtx
);
3334 setupLuaRules(luaCtx
);
3335 setupLuaVars(luaCtx
);
3336 setupLuaWeb(luaCtx
);
3338 #ifdef LUAJIT_VERSION
3339 luaCtx
.executeCode(getLuaFFIWrappers());
3342 std::ifstream
ifs(config
);
3345 throw std::runtime_error("Unable to read configuration file from " + config
);
3348 warnlog("Unable to read configuration from '%s'", config
);
3352 vinfolog("Read configuration from '%s'", config
);
3355 luaCtx
.executeCode(ifs
);
3357 auto ret
= *g_launchWork
;
3358 g_launchWork
= boost::none
;