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-dynblocks.hh"
43 #include "dnsdist-discovery.hh"
44 #include "dnsdist-ecs.hh"
45 #include "dnsdist-healthchecks.hh"
46 #include "dnsdist-lua.hh"
48 #include "dnsdist-lua-ffi.hh"
49 #endif /* LUAJIT_VERSION */
50 #include "dnsdist-metrics.hh"
51 #include "dnsdist-nghttp2.hh"
52 #include "dnsdist-proxy-protocol.hh"
53 #include "dnsdist-rings.hh"
54 #include "dnsdist-secpoll.hh"
55 #include "dnsdist-session-cache.hh"
56 #include "dnsdist-tcp-downstream.hh"
57 #include "dnsdist-web.hh"
60 #include "coverage.hh"
63 #include "sodcrypto.hh"
64 #include "threadname.hh"
70 #include <boost/logic/tribool.hpp>
71 #include <boost/uuid/string_generator.hpp>
74 #include <systemd/sd-daemon.h>
79 static boost::optional
<std::vector
<std::function
<void(void)>>> g_launchWork
= boost::none
;
81 boost::tribool g_noLuaSideEffect
;
82 static bool g_included
{false};
84 /* this is a best effort way to prevent logging calls with no side-effects in the output of delta()
85 Functions can declare setLuaNoSideEffect() and if nothing else does declare a side effect, or nothing
86 has done so before on this invocation, this call won't be part of delta() output */
87 void setLuaNoSideEffect()
89 if (g_noLuaSideEffect
== false) // there has been a side effect already
91 g_noLuaSideEffect
= true;
94 void setLuaSideEffect()
96 g_noLuaSideEffect
= false;
99 bool getLuaNoSideEffect()
101 if (g_noLuaSideEffect
) {
107 void resetLuaSideEffect()
109 g_noLuaSideEffect
= boost::logic::indeterminate
;
112 using localbind_t
= LuaAssociativeTable
<boost::variant
<bool, int, std::string
, LuaArray
<int>, LuaArray
<std::string
>, LuaAssociativeTable
<std::string
>>>;
114 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
)
117 LuaArray
<int> setCpus
;
119 getOptionalValue
<bool>(vars
, "reusePort", reusePort
);
120 getOptionalValue
<int>(vars
, "tcpFastOpenQueueSize", tcpFastOpenQueueSize
);
121 getOptionalValue
<int>(vars
, "tcpListenQueueSize", tcpListenQueueSize
);
122 getOptionalValue
<int>(vars
, "maxConcurrentTCPConnections", tcpMaxConcurrentConnections
);
123 getOptionalValue
<int>(vars
, "maxInFlight", maxInFlightQueriesPerConnection
);
124 getOptionalValue
<std::string
>(vars
, "interface", interface
);
125 if (getOptionalValue
<decltype(setCpus
)>(vars
, "cpus", setCpus
) > 0) {
126 for (const auto& cpu
: setCpus
) {
127 cpus
.insert(cpu
.second
);
133 #if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
134 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
)
136 if (certFiles
.type() == typeid(std::string
) && keyFiles
.type() == typeid(std::string
)) {
137 auto certFile
= boost::get
<std::string
>(certFiles
);
138 auto keyFile
= boost::get
<std::string
>(keyFiles
);
140 pairs
.emplace_back(certFile
, keyFile
);
142 else if (certFiles
.type() == typeid(std::shared_ptr
<TLSCertKeyPair
>)) {
143 auto cert
= boost::get
<std::shared_ptr
<TLSCertKeyPair
>>(certFiles
);
145 pairs
.emplace_back(*cert
);
147 else if (certFiles
.type() == typeid(LuaArray
<std::shared_ptr
<TLSCertKeyPair
>>)) {
148 auto certs
= boost::get
<LuaArray
<std::shared_ptr
<TLSCertKeyPair
>>>(certFiles
);
150 for (const auto& cert
: certs
) {
151 pairs
.emplace_back(*(cert
.second
));
154 else if (certFiles
.type() == typeid(LuaArray
<std::string
>) && keyFiles
.type() == typeid(LuaArray
<std::string
>)) {
155 auto certFilesVect
= boost::get
<LuaArray
<std::string
>>(certFiles
);
156 auto keyFilesVect
= boost::get
<LuaArray
<std::string
>>(keyFiles
);
157 if (certFilesVect
.size() == keyFilesVect
.size()) {
159 for (size_t idx
= 0; idx
< certFilesVect
.size(); idx
++) {
160 pairs
.emplace_back(certFilesVect
.at(idx
).second
, keyFilesVect
.at(idx
).second
);
164 errlog("Error, mismatching number of certificates and keys in call to %s()!", context
);
165 g_outputBuffer
= "Error, mismatching number of certificates and keys in call to " + context
+ "()!";
170 errlog("Error, mismatching number of certificates and keys in call to %s()!", context
);
171 g_outputBuffer
= "Error, mismatching number of certificates and keys in call to " + context
+ "()!";
178 static void parseTLSConfig(TLSConfig
& config
, const std::string
& context
, boost::optional
<localbind_t
>& vars
)
180 getOptionalValue
<std::string
>(vars
, "ciphers", config
.d_ciphers
);
181 getOptionalValue
<std::string
>(vars
, "ciphersTLS13", config
.d_ciphers13
);
184 std::string minVersion
;
185 if (getOptionalValue
<std::string
>(vars
, "minTLSVersion", minVersion
) > 0) {
186 config
.d_minTLSVersion
= libssl_tls_version_from_string(minVersion
);
188 #else /* HAVE_LIBSSL */
189 if (vars
->erase("minTLSVersion") > 0)
190 warnlog("minTLSVersion has no effect with chosen TLS library");
191 #endif /* HAVE_LIBSSL */
193 getOptionalValue
<std::string
>(vars
, "ticketKeyFile", config
.d_ticketKeyFile
);
194 getOptionalValue
<int>(vars
, "ticketsKeysRotationDelay", config
.d_ticketsKeyRotationDelay
);
195 getOptionalValue
<int>(vars
, "numberOfTicketsKeys", config
.d_numberOfTicketsKeys
);
196 getOptionalValue
<bool>(vars
, "preferServerCiphers", config
.d_preferServerCiphers
);
197 getOptionalValue
<int>(vars
, "sessionTimeout", config
.d_sessionTimeout
);
198 getOptionalValue
<bool>(vars
, "sessionTickets", config
.d_enableTickets
);
199 int numberOfStoredSessions
{0};
200 if (getOptionalValue
<int>(vars
, "numberOfStoredSessions", numberOfStoredSessions
) > 0) {
201 if (numberOfStoredSessions
< 0) {
202 errlog("Invalid value '%d' for %s() parameter 'numberOfStoredSessions', should be >= 0, dismissing", numberOfStoredSessions
, context
);
203 g_outputBuffer
= "Invalid value '" + std::to_string(numberOfStoredSessions
) + "' for " + context
+ "() parameter 'numberOfStoredSessions', should be >= 0, dimissing";
206 config
.d_maxStoredSessions
= numberOfStoredSessions
;
210 LuaArray
<std::string
> files
;
211 if (getOptionalValue
<decltype(files
)>(vars
, "ocspResponses", files
) > 0) {
212 for (const auto& file
: files
) {
213 config
.d_ocspFiles
.push_back(file
.second
);
217 if (vars
->count("keyLogFile") > 0) {
218 #ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
219 getOptionalValue
<std::string
>(vars
, "keyLogFile", config
.d_keyLogFile
);
221 errlog("TLS Key logging has been enabled using the 'keyLogFile' parameter to %s(), but this version of OpenSSL does not support it", context
);
222 g_outputBuffer
= "TLS Key logging has been enabled using the 'keyLogFile' parameter to " + context
+ "(), but this version of OpenSSL does not support it";
226 getOptionalValue
<bool>(vars
, "releaseBuffers", config
.d_releaseBuffers
);
227 getOptionalValue
<bool>(vars
, "enableRenegotiation", config
.d_enableRenegotiation
);
228 getOptionalValue
<bool>(vars
, "tlsAsyncMode", config
.d_asyncMode
);
229 getOptionalValue
<bool>(vars
, "ktls", config
.d_ktls
);
230 getOptionalValue
<bool>(vars
, "readAhead", config
.d_readAhead
);
233 #endif // defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
235 void checkParameterBound(const std::string
& parameter
, uint64_t value
, size_t max
)
238 throw std::runtime_error("The value (" + std::to_string(value
) + ") passed to " + parameter
+ " is too large, the maximum is " + std::to_string(max
));
242 static void LuaThread(const std::string
& code
)
244 setThreadName("dnsdist/lua-bg");
247 // mask SIGTERM on threads so the signal always comes to dnsdist itself
248 sigset_t blockSignals
;
250 sigemptyset(&blockSignals
);
251 sigaddset(&blockSignals
, SIGTERM
);
253 pthread_sigmask(SIG_BLOCK
, &blockSignals
, nullptr);
255 // submitToMainThread is camelcased, threadmessage is not.
256 // This follows our tradition of hooks we call being lowercased but functions the user can call being camelcased.
257 l
.writeFunction("submitToMainThread", [](std::string cmd
, LuaAssociativeTable
<std::string
> data
) {
258 auto lua
= g_lua
.lock();
259 // maybe offer more than `void`
260 auto func
= lua
->readVariable
<boost::optional
<std::function
<void(std::string cmd
, LuaAssociativeTable
<std::string
> data
)>>>("threadmessage");
262 func
.get()(std::move(cmd
), std::move(data
));
265 errlog("Lua thread called submitToMainThread but no threadmessage receiver is defined");
269 // function threadmessage(cmd, data) print("got thread data:", cmd) for k,v in pairs(data) do print(k,v) end end
274 errlog("Lua thread exited, restarting in 5 seconds");
276 catch (const std::exception
& e
) {
277 errlog("Lua thread crashed, restarting in 5 seconds: %s", e
.what());
280 errlog("Lua thread crashed, restarting in 5 seconds");
286 static bool checkConfigurationTime(const std::string
& name
)
288 if (!g_configurationDone
) {
291 g_outputBuffer
= name
+ " cannot be used at runtime!\n";
292 errlog("%s cannot be used at runtime!", name
);
296 // NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold
297 static void setupLuaConfig(LuaContext
& luaCtx
, bool client
, bool configCheck
)
299 typedef LuaAssociativeTable
<boost::variant
<bool, std::string
, LuaArray
<std::string
>, DownstreamState::checkfunc_t
>> newserver_t
;
300 luaCtx
.writeFunction("inClientStartup", [client
]() {
301 return client
&& !g_configurationDone
;
304 luaCtx
.writeFunction("inConfigCheck", [configCheck
]() {
308 luaCtx
.writeFunction("newServer",
309 [client
, configCheck
](boost::variant
<string
, newserver_t
> pvars
, boost::optional
<int> qps
) {
312 boost::optional
<newserver_t
> vars
= newserver_t();
313 DownstreamState::Config config
;
315 std::string serverAddressStr
;
316 if (auto addrStr
= boost::get
<string
>(&pvars
)) {
317 serverAddressStr
= *addrStr
;
319 (*vars
)["qps"] = std::to_string(*qps
);
323 vars
= boost::get
<newserver_t
>(pvars
);
324 getOptionalValue
<std::string
>(vars
, "address", serverAddressStr
);
328 if (getOptionalValue
<std::string
>(vars
, "source", source
) > 0) {
329 /* handle source in the following forms:
330 - v4 address ("192.0.2.1")
331 - v6 address ("2001:DB8::1")
332 - interface name ("eth0")
333 - v4 address and interface name ("192.0.2.1@eth0")
334 - v6 address and interface name ("2001:DB8::1@eth0")
337 std::string::size_type pos
= source
.find("@");
338 if (pos
== std::string::npos
) {
339 /* no '@', try to parse that as a valid v4/v6 address */
341 config
.sourceAddr
= ComboAddress(source
);
348 if (parsed
== false) {
349 /* try to parse as interface name, or v4/v6@itf */
350 config
.sourceItfName
= source
.substr(pos
== std::string::npos
? 0 : pos
+ 1);
351 unsigned int itfIdx
= if_nametoindex(config
.sourceItfName
.c_str());
353 if (pos
== 0 || pos
== std::string::npos
) {
354 /* "eth0" or "@eth0" */
355 config
.sourceItf
= itfIdx
;
358 /* "192.0.2.1@eth0" */
359 config
.sourceAddr
= ComboAddress(source
.substr(0, pos
));
360 config
.sourceItf
= itfIdx
;
362 #ifdef SO_BINDTODEVICE
363 /* we need to retain CAP_NET_RAW to be able to set SO_BINDTODEVICE in the health checks */
364 g_capabilitiesToRetain
.insert("CAP_NET_RAW");
368 warnlog("Dismissing source %s because '%s' is not a valid interface name", source
, config
.sourceItfName
);
373 std::string valueStr
;
374 if (getOptionalValue
<std::string
>(vars
, "sockets", valueStr
) > 0) {
375 config
.d_numberOfSockets
= std::stoul(valueStr
);
376 if (config
.d_numberOfSockets
== 0) {
377 warnlog("Dismissing invalid number of sockets '%s', using 1 instead", valueStr
);
378 config
.d_numberOfSockets
= 1;
382 getOptionalIntegerValue("newServer", vars
, "qps", config
.d_qpsLimit
);
383 getOptionalIntegerValue("newServer", vars
, "order", config
.order
);
384 getOptionalIntegerValue("newServer", vars
, "weight", config
.d_weight
);
385 if (config
.d_weight
< 1) {
386 errlog("Error creating new server: downstream weight value must be greater than 0.");
387 return std::shared_ptr
<DownstreamState
>();
390 getOptionalIntegerValue("newServer", vars
, "retries", config
.d_retries
);
391 getOptionalIntegerValue("newServer", vars
, "tcpConnectTimeout", config
.tcpConnectTimeout
);
392 getOptionalIntegerValue("newServer", vars
, "tcpSendTimeout", config
.tcpSendTimeout
);
393 getOptionalIntegerValue("newServer", vars
, "tcpRecvTimeout", config
.tcpRecvTimeout
);
395 if (getOptionalValue
<std::string
>(vars
, "checkInterval", valueStr
) > 0) {
396 config
.checkInterval
= static_cast<unsigned int>(std::stoul(valueStr
));
399 bool fastOpen
{false};
400 if (getOptionalValue
<bool>(vars
, "tcpFastOpen", fastOpen
) > 0) {
403 config
.tcpFastOpen
= true;
405 warnlog("TCP Fast Open has been configured on downstream server %s but is not supported", serverAddressStr
);
410 getOptionalIntegerValue("newServer", vars
, "maxInFlight", config
.d_maxInFlightQueriesPerConn
);
411 getOptionalIntegerValue("newServer", vars
, "maxConcurrentTCPConnections", config
.d_tcpConcurrentConnectionsLimit
);
413 getOptionalValue
<std::string
>(vars
, "name", config
.name
);
415 if (getOptionalValue
<std::string
>(vars
, "id", valueStr
) > 0) {
416 config
.id
= boost::uuids::string_generator()(valueStr
);
419 if (getOptionalValue
<std::string
>(vars
, "healthCheckMode", valueStr
) > 0) {
420 const auto& mode
= valueStr
;
421 if (pdns_iequals(mode
, "auto")) {
422 config
.availability
= DownstreamState::Availability::Auto
;
424 else if (pdns_iequals(mode
, "lazy")) {
425 config
.availability
= DownstreamState::Availability::Lazy
;
427 else if (pdns_iequals(mode
, "up")) {
428 config
.availability
= DownstreamState::Availability::Up
;
430 else if (pdns_iequals(mode
, "down")) {
431 config
.availability
= DownstreamState::Availability::Down
;
434 warnlog("Ignoring unknown value '%s' for 'healthCheckMode' on 'newServer'", mode
);
438 if (getOptionalValue
<std::string
>(vars
, "checkName", valueStr
) > 0) {
439 config
.checkName
= DNSName(valueStr
);
442 getOptionalValue
<std::string
>(vars
, "checkType", config
.checkType
);
443 getOptionalIntegerValue("newServer", vars
, "checkClass", config
.checkClass
);
444 getOptionalValue
<DownstreamState::checkfunc_t
>(vars
, "checkFunction", config
.checkFunction
);
445 getOptionalIntegerValue("newServer", vars
, "checkTimeout", config
.checkTimeout
);
446 getOptionalValue
<bool>(vars
, "checkTCP", config
.d_tcpCheck
);
447 getOptionalValue
<bool>(vars
, "setCD", config
.setCD
);
448 getOptionalValue
<bool>(vars
, "mustResolve", config
.mustResolve
);
450 if (getOptionalValue
<std::string
>(vars
, "lazyHealthCheckSampleSize", valueStr
) > 0) {
451 const auto& value
= std::stoi(valueStr
);
452 checkParameterBound("lazyHealthCheckSampleSize", value
);
453 config
.d_lazyHealthCheckSampleSize
= value
;
456 if (getOptionalValue
<std::string
>(vars
, "lazyHealthCheckMinSampleCount", valueStr
) > 0) {
457 const auto& value
= std::stoi(valueStr
);
458 checkParameterBound("lazyHealthCheckMinSampleCount", value
);
459 config
.d_lazyHealthCheckMinSampleCount
= value
;
462 if (getOptionalValue
<std::string
>(vars
, "lazyHealthCheckThreshold", valueStr
) > 0) {
463 const auto& value
= std::stoi(valueStr
);
464 checkParameterBound("lazyHealthCheckThreshold", value
, std::numeric_limits
<uint8_t>::max());
465 config
.d_lazyHealthCheckThreshold
= value
;
468 if (getOptionalValue
<std::string
>(vars
, "lazyHealthCheckFailedInterval", valueStr
) > 0) {
469 const auto& value
= std::stoi(valueStr
);
470 checkParameterBound("lazyHealthCheckFailedInterval", value
);
471 config
.d_lazyHealthCheckFailedInterval
= value
;
474 getOptionalValue
<bool>(vars
, "lazyHealthCheckUseExponentialBackOff", config
.d_lazyHealthCheckUseExponentialBackOff
);
476 if (getOptionalValue
<std::string
>(vars
, "lazyHealthCheckMaxBackOff", valueStr
) > 0) {
477 const auto& value
= std::stoi(valueStr
);
478 checkParameterBound("lazyHealthCheckMaxBackOff", value
);
479 config
.d_lazyHealthCheckMaxBackOff
= value
;
482 if (getOptionalValue
<std::string
>(vars
, "lazyHealthCheckMode", valueStr
) > 0) {
483 const auto& mode
= valueStr
;
484 if (pdns_iequals(mode
, "TimeoutOnly")) {
485 config
.d_lazyHealthCheckMode
= DownstreamState::LazyHealthCheckMode::TimeoutOnly
;
487 else if (pdns_iequals(mode
, "TimeoutOrServFail")) {
488 config
.d_lazyHealthCheckMode
= DownstreamState::LazyHealthCheckMode::TimeoutOrServFail
;
491 warnlog("Ignoring unknown value '%s' for 'lazyHealthCheckMode' on 'newServer'", mode
);
495 getOptionalValue
<bool>(vars
, "lazyHealthCheckWhenUpgraded", config
.d_upgradeToLazyHealthChecks
);
497 getOptionalValue
<bool>(vars
, "useClientSubnet", config
.useECS
);
498 getOptionalValue
<bool>(vars
, "useProxyProtocol", config
.useProxyProtocol
);
499 getOptionalValue
<bool>(vars
, "disableZeroScope", config
.disableZeroScope
);
500 getOptionalValue
<bool>(vars
, "ipBindAddrNoPort", config
.ipBindAddrNoPort
);
502 getOptionalIntegerValue("newServer", vars
, "addXPF", config
.xpfRRCode
);
503 getOptionalIntegerValue("newServer", vars
, "maxCheckFailures", config
.maxCheckFailures
);
504 getOptionalIntegerValue("newServer", vars
, "rise", config
.minRiseSuccesses
);
506 getOptionalValue
<bool>(vars
, "reconnectOnUp", config
.reconnectOnUp
);
508 LuaArray
<string
> cpuMap
;
509 if (getOptionalValue
<decltype(cpuMap
)>(vars
, "cpus", cpuMap
) > 0) {
510 for (const auto& cpu
: cpuMap
) {
511 config
.d_cpus
.insert(std::stoi(cpu
.second
));
515 getOptionalValue
<bool>(vars
, "tcpOnly", config
.d_tcpOnly
);
517 std::shared_ptr
<TLSCtx
> tlsCtx
;
518 getOptionalValue
<std::string
>(vars
, "ciphers", config
.d_tlsParams
.d_ciphers
);
519 getOptionalValue
<std::string
>(vars
, "ciphers13", config
.d_tlsParams
.d_ciphers13
);
520 getOptionalValue
<std::string
>(vars
, "caStore", config
.d_tlsParams
.d_caStore
);
521 getOptionalValue
<bool>(vars
, "validateCertificates", config
.d_tlsParams
.d_validateCertificates
);
522 getOptionalValue
<bool>(vars
, "releaseBuffers", config
.d_tlsParams
.d_releaseBuffers
);
523 getOptionalValue
<bool>(vars
, "enableRenegotiation", config
.d_tlsParams
.d_enableRenegotiation
);
524 getOptionalValue
<bool>(vars
, "ktls", config
.d_tlsParams
.d_ktls
);
525 getOptionalValue
<std::string
>(vars
, "subjectName", config
.d_tlsSubjectName
);
527 if (getOptionalValue
<std::string
>(vars
, "subjectAddr", valueStr
) > 0) {
529 ComboAddress
ca(valueStr
);
530 config
.d_tlsSubjectName
= ca
.toString();
531 config
.d_tlsSubjectIsAddr
= true;
533 catch (const std::exception
& e
) {
534 errlog("Error creating new server: downstream subjectAddr value must be a valid IP address");
535 return std::shared_ptr
<DownstreamState
>();
539 uint16_t serverPort
= 53;
541 if (getOptionalValue
<std::string
>(vars
, "tls", valueStr
) > 0) {
543 config
.d_tlsParams
.d_provider
= valueStr
;
544 tlsCtx
= getTLSContext(config
.d_tlsParams
);
546 if (getOptionalValue
<std::string
>(vars
, "dohPath", valueStr
) > 0) {
547 #if !defined(HAVE_DNS_OVER_HTTPS) || !defined(HAVE_NGHTTP2)
548 throw std::runtime_error("Outgoing DNS over HTTPS support requested (via 'dohPath' on newServer()) but nghttp2 support is not available");
552 config
.d_dohPath
= valueStr
;
554 getOptionalValue
<bool>(vars
, "addXForwardedHeaders", config
.d_addXForwardedHeaders
);
559 config
.remote
= ComboAddress(serverAddressStr
, serverPort
);
561 catch (const PDNSException
& e
) {
562 g_outputBuffer
= "Error creating new server: " + string(e
.reason
);
563 errlog("Error creating new server with address %s: %s", serverAddressStr
, e
.reason
);
564 return std::shared_ptr
<DownstreamState
>();
566 catch (const std::exception
& e
) {
567 g_outputBuffer
= "Error creating new server: " + string(e
.what());
568 errlog("Error creating new server with address %s: %s", serverAddressStr
, e
.what());
569 return std::shared_ptr
<DownstreamState
>();
572 if (IsAnyAddress(config
.remote
)) {
573 g_outputBuffer
= "Error creating new server: invalid address for a downstream server.";
574 errlog("Error creating new server: %s is not a valid address for a downstream server", serverAddressStr
);
575 return std::shared_ptr
<DownstreamState
>();
578 LuaArray
<std::string
> pools
;
579 if (getOptionalValue
<std::string
>(vars
, "pool", valueStr
, false) > 0) {
580 config
.pools
.insert(valueStr
);
582 else if (getOptionalValue
<decltype(pools
)>(vars
, "pool", pools
) > 0) {
583 for (auto& p
: pools
) {
584 config
.pools
.insert(p
.second
);
588 bool autoUpgrade
= false;
589 bool keepAfterUpgrade
= false;
590 uint32_t upgradeInterval
= 3600;
591 uint16_t upgradeDoHKey
= dnsdist::ServiceDiscovery::s_defaultDoHSVCKey
;
592 std::string upgradePool
;
594 getOptionalValue
<bool>(vars
, "autoUpgrade", autoUpgrade
);
596 if (getOptionalValue
<std::string
>(vars
, "autoUpgradeInterval", valueStr
) > 0) {
598 upgradeInterval
= static_cast<uint32_t>(std::stoul(valueStr
));
600 catch (const std::exception
& e
) {
601 warnlog("Error parsing 'autoUpgradeInterval' value: %s", e
.what());
604 getOptionalValue
<bool>(vars
, "autoUpgradeKeep", keepAfterUpgrade
);
605 getOptionalValue
<std::string
>(vars
, "autoUpgradePool", upgradePool
);
606 if (getOptionalValue
<std::string
>(vars
, "autoUpgradeDoHKey", valueStr
) > 0) {
608 upgradeDoHKey
= static_cast<uint16_t>(std::stoul(valueStr
));
610 catch (const std::exception
& e
) {
611 warnlog("Error parsing 'autoUpgradeDoHKey' value: %s", e
.what());
616 // create but don't connect the socket in client or check-config modes
617 auto ret
= std::make_shared
<DownstreamState
>(std::move(config
), std::move(tlsCtx
), !(client
|| configCheck
));
618 if (!(client
|| configCheck
)) {
619 infolog("Added downstream server %s", ret
->d_config
.remote
.toStringWithPort());
622 if (autoUpgrade
&& ret
->getProtocol() != dnsdist::Protocol::DoT
&& ret
->getProtocol() != dnsdist::Protocol::DoH
) {
623 dnsdist::ServiceDiscovery::addUpgradeableServer(ret
, upgradeInterval
, upgradePool
, upgradeDoHKey
, keepAfterUpgrade
);
626 /* this needs to be done _AFTER_ the order has been set,
627 since the server are kept ordered inside the pool */
628 auto localPools
= g_pools
.getCopy();
629 if (!ret
->d_config
.pools
.empty()) {
630 for (const auto& poolName
: ret
->d_config
.pools
) {
631 addServerToPool(localPools
, poolName
, ret
);
635 addServerToPool(localPools
, "", ret
);
637 g_pools
.setState(localPools
);
639 if (ret
->connected
) {
641 g_launchWork
->push_back([ret
]() {
650 auto states
= g_dstates
.getCopy();
651 states
.push_back(ret
);
652 std::stable_sort(states
.begin(), states
.end(), [](const decltype(ret
)& a
, const decltype(ret
)& b
) {
653 return a
->d_config
.order
< b
->d_config
.order
;
655 g_dstates
.setState(states
);
656 checkAllParametersConsumed("newServer", vars
);
660 luaCtx
.writeFunction("rmServer",
661 [](boost::variant
<std::shared_ptr
<DownstreamState
>, int, std::string
> var
) {
663 shared_ptr
<DownstreamState
> server
= nullptr;
664 auto states
= g_dstates
.getCopy();
665 if (auto* rem
= boost::get
<shared_ptr
<DownstreamState
>>(&var
)) {
668 else if (auto str
= boost::get
<std::string
>(&var
)) {
669 const auto uuid
= getUniqueID(*str
);
670 for (auto& state
: states
) {
671 if (*state
->d_config
.id
== uuid
) {
677 int idx
= boost::get
<int>(var
);
678 server
= states
.at(idx
);
681 throw std::runtime_error("unable to locate the requested server");
683 auto localPools
= g_pools
.getCopy();
684 for (const string
& poolName
: server
->d_config
.pools
) {
685 removeServerFromPool(localPools
, poolName
, server
);
687 /* the server might also be in the default pool */
688 removeServerFromPool(localPools
, "", server
);
689 g_pools
.setState(localPools
);
690 states
.erase(remove(states
.begin(), states
.end(), server
), states
.end());
691 g_dstates
.setState(states
);
695 luaCtx
.writeFunction("truncateTC", [](bool tc
) { setLuaSideEffect(); g_truncateTC
=tc
; });
696 luaCtx
.writeFunction("fixupCase", [](bool fu
) { setLuaSideEffect(); g_fixupCase
=fu
; });
698 luaCtx
.writeFunction("addACL", [](const std::string
& domain
) {
700 g_ACL
.modify([domain
](NetmaskGroup
& nmg
) { nmg
.addMask(domain
); });
703 luaCtx
.writeFunction("rmACL", [](const std::string
& netmask
) {
705 g_ACL
.modify([netmask
](NetmaskGroup
& nmg
) { nmg
.deleteMask(netmask
); });
708 luaCtx
.writeFunction("setLocal", [client
](const std::string
& addr
, boost::optional
<localbind_t
> vars
) {
714 if (!checkConfigurationTime("setLocal")) {
718 bool reusePort
= false;
719 int tcpFastOpenQueueSize
= 0;
720 int tcpListenQueueSize
= 0;
721 uint64_t maxInFlightQueriesPerConn
= 0;
722 uint64_t tcpMaxConcurrentConnections
= 0;
723 std::string interface
;
726 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConnections
);
728 checkAllParametersConsumed("setLocal", vars
);
731 ComboAddress
loc(addr
, 53);
732 for (auto it
= g_frontends
.begin(); it
!= g_frontends
.end();) {
733 /* DoH, DoT and DNSCrypt frontends are separate */
734 if ((*it
)->tlsFrontend
== nullptr && (*it
)->dnscryptCtx
== nullptr && (*it
)->dohFrontend
== nullptr) {
735 it
= g_frontends
.erase(it
);
742 // only works pre-startup, so no sync necessary
743 g_frontends
.push_back(std::make_unique
<ClientState
>(loc
, false, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
));
744 auto tcpCS
= std::make_unique
<ClientState
>(loc
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
745 if (tcpListenQueueSize
> 0) {
746 tcpCS
->tcpListenQueueSize
= tcpListenQueueSize
;
748 if (maxInFlightQueriesPerConn
> 0) {
749 tcpCS
->d_maxInFlightQueriesPerConn
= maxInFlightQueriesPerConn
;
751 if (tcpMaxConcurrentConnections
> 0) {
752 tcpCS
->d_tcpConcurrentConnectionsLimit
= tcpMaxConcurrentConnections
;
755 g_frontends
.push_back(std::move(tcpCS
));
757 catch (const std::exception
& e
) {
758 g_outputBuffer
= "Error: " + string(e
.what()) + "\n";
762 luaCtx
.writeFunction("addLocal", [client
](const std::string
& addr
, boost::optional
<localbind_t
> vars
) {
767 if (!checkConfigurationTime("addLocal")) {
770 bool reusePort
= false;
771 int tcpFastOpenQueueSize
= 0;
772 int tcpListenQueueSize
= 0;
773 uint64_t maxInFlightQueriesPerConn
= 0;
774 uint64_t tcpMaxConcurrentConnections
= 0;
775 std::string interface
;
778 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConnections
);
779 checkAllParametersConsumed("addLocal", vars
);
782 ComboAddress
loc(addr
, 53);
783 // only works pre-startup, so no sync necessary
784 g_frontends
.push_back(std::make_unique
<ClientState
>(loc
, false, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
));
785 auto tcpCS
= std::make_unique
<ClientState
>(loc
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
786 if (tcpListenQueueSize
> 0) {
787 tcpCS
->tcpListenQueueSize
= tcpListenQueueSize
;
789 if (maxInFlightQueriesPerConn
> 0) {
790 tcpCS
->d_maxInFlightQueriesPerConn
= maxInFlightQueriesPerConn
;
792 if (tcpMaxConcurrentConnections
> 0) {
793 tcpCS
->d_tcpConcurrentConnectionsLimit
= tcpMaxConcurrentConnections
;
795 g_frontends
.push_back(std::move(tcpCS
));
797 catch (std::exception
& e
) {
798 g_outputBuffer
= "Error: " + string(e
.what()) + "\n";
799 errlog("Error while trying to listen on %s: %s\n", addr
, string(e
.what()));
803 luaCtx
.writeFunction("setACL", [](LuaTypeOrArrayOf
<std::string
> inp
) {
806 if (auto str
= boost::get
<string
>(&inp
)) {
810 for (const auto& p
: boost::get
<LuaArray
<std::string
>>(inp
)) {
811 nmg
.addMask(p
.second
);
816 luaCtx
.writeFunction("setACLFromFile", [](const std::string
& file
) {
822 throw std::runtime_error("Could not open '" + file
+ "': " + stringerror());
825 string::size_type pos
;
827 while (getline(ifs
, line
)) {
828 pos
= line
.find('#');
829 if (pos
!= string::npos
)
841 luaCtx
.writeFunction("showACL", []() {
842 setLuaNoSideEffect();
845 g_ACL
.getLocal()->toStringVector(&vec
);
847 for (const auto& s
: vec
)
848 g_outputBuffer
+= s
+ "\n";
851 luaCtx
.writeFunction("shutdown", []() {
853 sd_notify(0, "STOPPING=1");
854 #endif /* HAVE_SYSTEMD */
856 // Useful for debugging leaks, but might lead to race under load
857 // since other threads are still running.
858 for (auto& frontend
: g_tlslocals
) {
864 pdns::coverage::dumpCoverageData();
868 typedef LuaAssociativeTable
<boost::variant
<bool, std::string
>> showserversopts_t
;
870 luaCtx
.writeFunction("showServers", [](boost::optional
<showserversopts_t
> vars
) {
871 setLuaNoSideEffect();
872 bool showUUIDs
= false;
873 getOptionalValue
<bool>(vars
, "showUUIDs", showUUIDs
);
874 checkAllParametersConsumed("showServers", vars
);
880 auto latFmt
= boost::format("%5.1f");
882 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%");
883 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 (tcp latency)
884 ret
<< (fmt
% "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools" % "UUID" % "TCP") << endl
;
887 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%");
888 ret
<< (fmt
% "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools" % "TCP") << endl
;
891 uint64_t totQPS
{0}, totQueries
{0}, totDrops
{0};
893 auto states
= g_dstates
.getLocal();
894 for (const auto& s
: *states
) {
895 string status
= s
->getStatus();
897 for (const auto& p
: s
->d_config
.pools
) {
898 if (!pools
.empty()) {
903 const std::string latency
= (s
->latencyUsec
== 0.0 ? "-" : boost::str(latFmt
% (s
->latencyUsec
/ 1000.0)));
904 const std::string latencytcp
= (s
->latencyUsecTCP
== 0.0 ? "-" : boost::str(latFmt
% (s
->latencyUsecTCP
/ 1000.0)));
906 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
;
909 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
;
911 totQPS
+= s
->queryLoad
;
912 totQueries
+= s
->queries
.load();
913 totDrops
+= s
->reuseds
.load();
917 ret
<< (fmt
% "All" % "" % "" % ""
918 % (double)totQPS
% "" % "" % "" % totQueries
% totDrops
% "" % "" % "" % "" % "" % "")
922 ret
<< (fmt
% "All" % "" % "" % ""
923 % (double)totQPS
% "" % "" % "" % totQueries
% totDrops
% "" % "" % "" % "" % "")
927 g_outputBuffer
= ret
.str();
929 catch (std::exception
& e
) {
930 g_outputBuffer
= e
.what();
935 luaCtx
.writeFunction("getServers", []() {
936 setLuaNoSideEffect();
937 LuaArray
<std::shared_ptr
<DownstreamState
>> ret
;
939 for (const auto& s
: g_dstates
.getCopy()) {
940 ret
.emplace_back(count
++, s
);
945 luaCtx
.writeFunction("getPoolServers", [](const string
& pool
) {
946 const auto poolServers
= getDownstreamCandidates(g_pools
.getCopy(), pool
);
950 luaCtx
.writeFunction("getServer", [client
](boost::variant
<int, std::string
> i
) {
952 return std::make_shared
<DownstreamState
>(ComboAddress());
954 auto states
= g_dstates
.getCopy();
955 if (auto str
= boost::get
<std::string
>(&i
)) {
956 const auto uuid
= getUniqueID(*str
);
957 for (auto& state
: states
) {
958 if (*state
->d_config
.id
== uuid
) {
963 else if (auto pos
= boost::get
<int>(&i
)) {
964 return states
.at(*pos
);
967 g_outputBuffer
= "Error: no rule matched\n";
968 return std::shared_ptr
<DownstreamState
>(nullptr);
971 #ifndef DISABLE_CARBON
972 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
) {
974 dnsdist::Carbon::Endpoint endpoint
{ComboAddress(address
, 2003),
975 (namespace_name
&& !namespace_name
->empty()) ? *namespace_name
: "dnsdist",
976 ourName
? *ourName
: "",
977 (instance_name
&& !instance_name
->empty()) ? *instance_name
: "main",
978 (interval
&& *interval
< std::numeric_limits
<unsigned int>::max()) ? static_cast<unsigned int>(*interval
) : 30};
979 dnsdist::Carbon::addEndpoint(std::move(endpoint
));
981 #endif /* DISABLE_CARBON */
983 luaCtx
.writeFunction("webserver", [client
, configCheck
](const std::string
& address
) {
987 local
= ComboAddress(address
);
989 catch (const PDNSException
& e
) {
990 throw std::runtime_error(std::string("Error parsing the bind address for the webserver: ") + e
.reason
);
993 if (client
|| configCheck
) {
998 int sock
= SSocket(local
.sin4
.sin_family
, SOCK_STREAM
, 0);
999 SSetsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, 1);
1002 auto launch
= [sock
, local
]() {
1003 thread
t(dnsdistWebserverThread
, sock
, local
);
1007 g_launchWork
->push_back(launch
);
1013 catch (const std::exception
& e
) {
1014 g_outputBuffer
= "Unable to bind to webserver socket on " + local
.toStringWithPort() + ": " + e
.what();
1015 errlog("Unable to bind to webserver socket on %s: %s", local
.toStringWithPort(), e
.what());
1019 typedef LuaAssociativeTable
<boost::variant
<bool, std::string
, LuaAssociativeTable
<std::string
>>> webserveropts_t
;
1021 luaCtx
.writeFunction("setWebserverConfig", [](boost::optional
<webserveropts_t
> vars
) {
1028 bool hashPlaintextCredentials
= false;
1029 getOptionalValue
<bool>(vars
, "hashPlaintextCredentials", hashPlaintextCredentials
);
1031 std::string password
;
1034 LuaAssociativeTable
<std::string
> headers
;
1035 bool statsRequireAuthentication
{true};
1036 bool apiRequiresAuthentication
{true};
1037 bool dashboardRequiresAuthentication
{true};
1038 int maxConcurrentConnections
= 0;
1040 if (getOptionalValue
<std::string
>(vars
, "password", password
) > 0) {
1041 auto holder
= make_unique
<CredentialsHolder
>(std::move(password
), hashPlaintextCredentials
);
1042 if (!holder
->wasHashed() && holder
->isHashingAvailable()) {
1043 infolog("Passing a plain-text password via the 'password' parameter to 'setWebserverConfig()' is not advised, please consider generating a hashed one using 'hashPassword()' instead.");
1046 setWebserverPassword(std::move(holder
));
1049 if (getOptionalValue
<std::string
>(vars
, "apiKey", apiKey
) > 0) {
1050 auto holder
= make_unique
<CredentialsHolder
>(std::move(apiKey
), hashPlaintextCredentials
);
1051 if (!holder
->wasHashed() && holder
->isHashingAvailable()) {
1052 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.");
1055 setWebserverAPIKey(std::move(holder
));
1058 if (getOptionalValue
<std::string
>(vars
, "acl", acl
) > 0) {
1059 setWebserverACL(acl
);
1062 if (getOptionalValue
<decltype(headers
)>(vars
, "customHeaders", headers
) > 0) {
1063 setWebserverCustomHeaders(headers
);
1066 if (getOptionalValue
<bool>(vars
, "statsRequireAuthentication", statsRequireAuthentication
) > 0) {
1067 setWebserverStatsRequireAuthentication(statsRequireAuthentication
);
1070 if (getOptionalValue
<bool>(vars
, "apiRequiresAuthentication", apiRequiresAuthentication
) > 0) {
1071 setWebserverAPIRequiresAuthentication(apiRequiresAuthentication
);
1074 if (getOptionalValue
<bool>(vars
, "dashboardRequiresAuthentication", dashboardRequiresAuthentication
) > 0) {
1075 setWebserverDashboardRequiresAuthentication(dashboardRequiresAuthentication
);
1078 if (getOptionalIntegerValue("setWebserverConfig", vars
, "maxConcurrentConnections", maxConcurrentConnections
) > 0) {
1079 setWebserverMaxConcurrentConnections(maxConcurrentConnections
);
1083 luaCtx
.writeFunction("showWebserverConfig", []() {
1084 setLuaNoSideEffect();
1085 return getWebserverConfig();
1088 luaCtx
.writeFunction("hashPassword", [](const std::string
& password
, boost::optional
<uint64_t> workFactor
) {
1090 return hashPassword(password
, *workFactor
, CredentialsHolder::s_defaultParallelFactor
, CredentialsHolder::s_defaultBlockSize
);
1092 return hashPassword(password
);
1095 luaCtx
.writeFunction("controlSocket", [client
, configCheck
](const std::string
& str
) {
1097 ComboAddress
local(str
, 5199);
1099 if (client
|| configCheck
) {
1100 g_serverControl
= local
;
1104 g_consoleEnabled
= true;
1105 #ifdef HAVE_LIBSODIUM
1106 if (g_configurationDone
&& g_consoleKey
.empty()) {
1107 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");
1112 int sock
= SSocket(local
.sin4
.sin_family
, SOCK_STREAM
, 0);
1113 SSetsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, 1);
1116 auto launch
= [sock
, local
]() {
1117 thread
t(controlThread
, sock
, local
);
1121 g_launchWork
->push_back(launch
);
1127 catch (std::exception
& e
) {
1128 g_outputBuffer
= "Unable to bind to control socket on " + local
.toStringWithPort() + ": " + e
.what();
1129 errlog("Unable to bind to control socket on %s: %s", local
.toStringWithPort(), e
.what());
1133 luaCtx
.writeFunction("addConsoleACL", [](const std::string
& netmask
) {
1135 #ifndef HAVE_LIBSODIUM
1136 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
1139 g_consoleACL
.modify([netmask
](NetmaskGroup
& nmg
) { nmg
.addMask(netmask
); });
1142 luaCtx
.writeFunction("setConsoleACL", [](LuaTypeOrArrayOf
<std::string
> inp
) {
1145 #ifndef HAVE_LIBSODIUM
1146 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
1150 if (auto str
= boost::get
<string
>(&inp
)) {
1154 for (const auto& p
: boost::get
<LuaArray
<std::string
>>(inp
)) {
1155 nmg
.addMask(p
.second
);
1157 g_consoleACL
.setState(nmg
);
1160 luaCtx
.writeFunction("showConsoleACL", []() {
1161 setLuaNoSideEffect();
1163 #ifndef HAVE_LIBSODIUM
1164 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
1168 g_consoleACL
.getLocal()->toStringVector(&vec
);
1170 for (const auto& s
: vec
) {
1171 g_outputBuffer
+= s
+ "\n";
1175 luaCtx
.writeFunction("setConsoleMaximumConcurrentConnections", [](uint64_t max
) {
1177 setConsoleMaximumConcurrentConnections(max
);
1180 luaCtx
.writeFunction("clearQueryCounters", []() {
1181 unsigned int size
{0};
1183 auto records
= g_qcount
.records
.write_lock();
1184 size
= records
->size();
1188 boost::format
fmt("%d records cleared from query counter buffer\n");
1189 g_outputBuffer
= (fmt
% size
).str();
1192 luaCtx
.writeFunction("getQueryCounters", [](boost::optional
<uint64_t> optMax
) {
1193 setLuaNoSideEffect();
1194 auto records
= g_qcount
.records
.read_lock();
1195 g_outputBuffer
= "query counting is currently: ";
1196 g_outputBuffer
+= g_qcount
.enabled
? "enabled" : "disabled";
1197 g_outputBuffer
+= (boost::format(" (%d records in buffer)\n") % records
->size()).str();
1199 boost::format
fmt("%-3d %s: %d request(s)\n");
1200 uint64_t max
= optMax
? *optMax
: 10U;
1202 for (auto it
= records
->begin(); it
!= records
->end() && index
<= max
; ++it
, ++index
) {
1203 g_outputBuffer
+= (fmt
% index
% it
->first
% it
->second
).str();
1207 luaCtx
.writeFunction("setQueryCount", [](bool enabled
) { g_qcount
.enabled
= enabled
; });
1209 luaCtx
.writeFunction("setQueryCountFilter", [](QueryCountFilter func
) {
1210 g_qcount
.filter
= std::move(func
);
1213 luaCtx
.writeFunction("makeKey", []() {
1214 setLuaNoSideEffect();
1215 g_outputBuffer
= "setKey(" + newKey() + ")\n";
1218 luaCtx
.writeFunction("setKey", [](const std::string
& key
) {
1219 if (!g_configurationDone
&& !g_consoleKey
.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
1220 return; // but later setKeys() trump the -k value again
1222 #ifndef HAVE_LIBSODIUM
1223 warnlog("Calling setKey() while libsodium support has not been enabled is not secure, and will result in cleartext communications");
1228 if (B64Decode(key
, newkey
) < 0) {
1229 g_outputBuffer
= string("Unable to decode ") + key
+ " as Base64";
1230 errlog("%s", g_outputBuffer
);
1233 g_consoleKey
= newkey
;
1236 luaCtx
.writeFunction("clearConsoleHistory", []() {
1237 clearConsoleHistory();
1240 luaCtx
.writeFunction("testCrypto", [](boost::optional
<string
> optTestMsg
) {
1241 setLuaNoSideEffect();
1242 #ifdef HAVE_LIBSODIUM
1247 testmsg
= *optTestMsg
;
1250 testmsg
= "testStringForCryptoTests";
1253 SodiumNonce sn
, sn2
;
1256 string encrypted
= sodEncryptSym(testmsg
, g_consoleKey
, sn
);
1257 string decrypted
= sodDecryptSym(encrypted
, g_consoleKey
, sn2
);
1262 encrypted
= sodEncryptSym(testmsg
, g_consoleKey
, sn
);
1263 decrypted
= sodDecryptSym(encrypted
, g_consoleKey
, sn2
);
1265 if (testmsg
== decrypted
)
1266 g_outputBuffer
= "Everything is ok!\n";
1268 g_outputBuffer
= "Crypto failed.. (the decoded value does not match the cleartext one)\n";
1270 catch (const std::exception
& e
) {
1271 g_outputBuffer
= "Crypto failed: " + std::string(e
.what()) + "\n";
1274 g_outputBuffer
= "Crypto failed..\n";
1277 g_outputBuffer
= "Crypto not available.\n";
1281 luaCtx
.writeFunction("setTCPRecvTimeout", [](int timeout
) { g_tcpRecvTimeout
= timeout
; });
1283 luaCtx
.writeFunction("setTCPSendTimeout", [](int timeout
) { g_tcpSendTimeout
= timeout
; });
1285 luaCtx
.writeFunction("setUDPTimeout", [](int timeout
) { DownstreamState::s_udpTimeout
= timeout
; });
1287 luaCtx
.writeFunction("setMaxUDPOutstanding", [](uint64_t max
) {
1288 if (!checkConfigurationTime("setMaxUDPOutstanding")) {
1292 checkParameterBound("setMaxUDPOutstanding", max
);
1293 g_maxOutstanding
= max
;
1296 luaCtx
.writeFunction("setMaxTCPClientThreads", [](uint64_t max
) {
1297 if (!checkConfigurationTime("setMaxTCPClientThreads")) {
1300 g_maxTCPClientThreads
= max
;
1303 luaCtx
.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max
) {
1304 if (!checkConfigurationTime("setMaxTCPQueuedConnections")) {
1307 g_maxTCPQueuedConnections
= max
;
1310 luaCtx
.writeFunction("setMaxTCPQueriesPerConnection", [](uint64_t max
) {
1311 if (!checkConfigurationTime("setMaxTCPQueriesPerConnection")) {
1314 g_maxTCPQueriesPerConn
= max
;
1317 luaCtx
.writeFunction("setMaxTCPConnectionsPerClient", [](uint64_t max
) {
1318 if (!checkConfigurationTime("setMaxTCPConnectionsPerClient")) {
1321 dnsdist::IncomingConcurrentTCPConnectionsManager::setMaxTCPConnectionsPerClient(max
);
1324 luaCtx
.writeFunction("setMaxTCPConnectionDuration", [](uint64_t max
) {
1325 if (!checkConfigurationTime("setMaxTCPConnectionDuration")) {
1328 g_maxTCPConnectionDuration
= max
;
1331 luaCtx
.writeFunction("setMaxCachedTCPConnectionsPerDownstream", [](uint64_t max
) {
1332 setTCPDownstreamMaxIdleConnectionsPerBackend(max
);
1335 #if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
1336 luaCtx
.writeFunction("setMaxIdleDoHConnectionsPerDownstream", [](uint64_t max
) {
1337 setDoHDownstreamMaxIdleConnectionsPerBackend(max
);
1340 luaCtx
.writeFunction("setOutgoingDoHWorkerThreads", [](uint64_t workers
) {
1341 if (!checkConfigurationTime("setOutgoingDoHWorkerThreads")) {
1344 g_outgoingDoHWorkerThreads
= workers
;
1346 #endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
1348 luaCtx
.writeFunction("setOutgoingTLSSessionsCacheMaxTicketsPerBackend", [](uint64_t max
) {
1349 if (!checkConfigurationTime("setOutgoingTLSSessionsCacheMaxTicketsPerBackend")) {
1352 TLSSessionCache::setMaxTicketsPerBackend(max
);
1355 luaCtx
.writeFunction("setOutgoingTLSSessionsCacheCleanupDelay", [](time_t delay
) {
1356 if (!checkConfigurationTime("setOutgoingTLSSessionsCacheCleanupDelay")) {
1359 TLSSessionCache::setCleanupDelay(delay
);
1362 luaCtx
.writeFunction("setOutgoingTLSSessionsCacheMaxTicketValidity", [](time_t validity
) {
1363 if (!checkConfigurationTime("setOutgoingTLSSessionsCacheMaxTicketValidity")) {
1366 TLSSessionCache::setSessionValidity(validity
);
1369 luaCtx
.writeFunction("getOutgoingTLSSessionCacheSize", []() {
1370 setLuaNoSideEffect();
1371 return g_sessionCache
.getSize();
1374 luaCtx
.writeFunction("setCacheCleaningDelay", [](uint64_t delay
) {
1375 checkParameterBound("setCacheCleaningDelay", delay
, std::numeric_limits
<uint32_t>::max());
1376 g_cacheCleaningDelay
= delay
;
1379 luaCtx
.writeFunction("setCacheCleaningPercentage", [](uint64_t percentage
) { if (percentage
< 100) g_cacheCleaningPercentage
= percentage
; else g_cacheCleaningPercentage
= 100; });
1381 luaCtx
.writeFunction("setECSSourcePrefixV4", [](uint64_t prefix
) {
1382 checkParameterBound("setECSSourcePrefixV4", prefix
, std::numeric_limits
<uint16_t>::max());
1383 g_ECSSourcePrefixV4
= prefix
;
1386 luaCtx
.writeFunction("setECSSourcePrefixV6", [](uint64_t prefix
) {
1387 checkParameterBound("setECSSourcePrefixV6", prefix
, std::numeric_limits
<uint16_t>::max());
1388 g_ECSSourcePrefixV6
= prefix
;
1391 luaCtx
.writeFunction("setECSOverride", [](bool override
) { g_ECSOverride
= override
; });
1393 #ifndef DISABLE_DYNBLOCKS
1394 luaCtx
.writeFunction("showDynBlocks", []() {
1395 setLuaNoSideEffect();
1396 auto slow
= g_dynblockNMG
.getCopy();
1397 struct timespec now
;
1399 boost::format
fmt("%-24s %8d %8d %-10s %-20s %-10s %s\n");
1400 g_outputBuffer
= (fmt
% "What" % "Seconds" % "Blocks" % "Warning" % "Action" % "eBPF" % "Reason").str();
1401 for (const auto& e
: slow
) {
1402 if (now
< e
.second
.until
) {
1403 uint64_t counter
= e
.second
.blocks
;
1404 if (g_defaultBPFFilter
&& e
.second
.bpf
) {
1405 counter
+= g_defaultBPFFilter
->getHits(e
.first
.getNetwork());
1407 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();
1410 auto slow2
= g_dynblockSMT
.getCopy();
1411 slow2
.visit([&now
, &fmt
](const SuffixMatchTree
<DynBlock
>& node
) {
1412 if (now
< node
.d_value
.until
) {
1413 string
dom("empty");
1414 if (!node
.d_value
.domain
.empty())
1415 dom
= node
.d_value
.domain
.toString();
1416 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();
1421 luaCtx
.writeFunction("clearDynBlocks", []() {
1424 g_dynblockNMG
.setState(nmg
);
1425 SuffixMatchTree
<DynBlock
> smt
;
1426 g_dynblockSMT
.setState(smt
);
1429 #ifndef DISABLE_DEPRECATED_DYNBLOCK
1430 luaCtx
.writeFunction("addDynBlocks",
1431 [](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
) {
1436 auto slow
= g_dynblockNMG
.getCopy();
1437 struct timespec until
, now
;
1440 int actualSeconds
= seconds
? *seconds
: 10;
1441 until
.tv_sec
+= actualSeconds
;
1442 for (const auto& capair
: m
) {
1443 unsigned int count
= 0;
1444 /* this legacy interface does not support ranges or ports, use DynBlockRulesGroup instead */
1445 AddressAndPortRange
requestor(capair
.first
, capair
.first
.isIPv4() ? 32 : 128, 0);
1446 auto got
= slow
.lookup(requestor
);
1447 bool expired
= false;
1449 if (until
< got
->second
.until
) {
1450 // had a longer policy
1453 if (now
< got
->second
.until
) {
1454 // only inherit count on fresh query we are extending
1455 count
= got
->second
.blocks
;
1461 DynBlock db
{msg
, until
, DNSName(), (action
? *action
: DNSAction::Action::None
)};
1463 if (!got
|| expired
) {
1464 warnlog("Inserting dynamic block for %s for %d seconds: %s", capair
.first
.toString(), actualSeconds
, msg
);
1466 slow
.insert(requestor
).second
= db
;
1468 g_dynblockNMG
.setState(slow
);
1471 luaCtx
.writeFunction("addDynBlockSMT",
1472 [](const LuaArray
<std::string
>& names
, const std::string
& msg
, boost::optional
<int> seconds
, boost::optional
<DNSAction::Action
> action
) {
1473 if (names
.empty()) {
1477 auto slow
= g_dynblockSMT
.getCopy();
1478 struct timespec until
, now
;
1481 int actualSeconds
= seconds
? *seconds
: 10;
1482 until
.tv_sec
+= actualSeconds
;
1484 for (const auto& capair
: names
) {
1485 unsigned int count
= 0;
1486 DNSName
domain(capair
.second
);
1487 domain
.makeUsLowerCase();
1488 auto got
= slow
.lookup(domain
);
1489 bool expired
= false;
1491 if (until
< got
->until
) // had a longer policy
1493 if (now
< got
->until
) // only inherit count on fresh query we are extending
1494 count
= got
->blocks
;
1499 DynBlock db
{msg
, until
, domain
, (action
? *action
: DNSAction::Action::None
)};
1501 if (!got
|| expired
)
1502 warnlog("Inserting dynamic block for %s for %d seconds: %s", domain
, actualSeconds
, msg
);
1503 slow
.add(domain
, std::move(db
));
1505 g_dynblockSMT
.setState(slow
);
1508 luaCtx
.writeFunction("setDynBlocksAction", [](DNSAction::Action action
) {
1509 if (!checkConfigurationTime("setDynBlocksAction")) {
1512 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
) {
1513 g_dynBlockAction
= action
;
1516 errlog("Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!");
1517 g_outputBuffer
= "Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!\n";
1520 #endif /* DISABLE_DEPRECATED_DYNBLOCK */
1522 luaCtx
.writeFunction("setDynBlocksPurgeInterval", [](uint64_t interval
) {
1523 DynBlockMaintenance::s_expiredDynBlocksPurgeInterval
= interval
;
1525 #endif /* DISABLE_DYNBLOCKS */
1527 #ifdef HAVE_DNSCRYPT
1528 luaCtx
.writeFunction("addDNSCryptBind", [](const std::string
& addr
, const std::string
& providerName
, LuaTypeOrArrayOf
<std::string
> certFiles
, LuaTypeOrArrayOf
<std::string
> keyFiles
, boost::optional
<localbind_t
> vars
) {
1529 if (!checkConfigurationTime("addDNSCryptBind")) {
1532 bool reusePort
= false;
1533 int tcpFastOpenQueueSize
= 0;
1534 int tcpListenQueueSize
= 0;
1535 uint64_t maxInFlightQueriesPerConn
= 0;
1536 uint64_t tcpMaxConcurrentConnections
= 0;
1537 std::string interface
;
1539 std::vector
<DNSCryptContext::CertKeyPaths
> certKeys
;
1541 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConnections
);
1542 checkAllParametersConsumed("addDNSCryptBind", vars
);
1544 if (certFiles
.type() == typeid(std::string
) && keyFiles
.type() == typeid(std::string
)) {
1545 auto certFile
= boost::get
<std::string
>(certFiles
);
1546 auto keyFile
= boost::get
<std::string
>(keyFiles
);
1547 certKeys
.push_back({std::move(certFile
), std::move(keyFile
)});
1549 else if (certFiles
.type() == typeid(LuaArray
<std::string
>) && keyFiles
.type() == typeid(LuaArray
<std::string
>)) {
1550 auto certFilesVect
= boost::get
<LuaArray
<std::string
>>(certFiles
);
1551 auto keyFilesVect
= boost::get
<LuaArray
<std::string
>>(keyFiles
);
1552 if (certFilesVect
.size() == keyFilesVect
.size()) {
1553 for (size_t idx
= 0; idx
< certFilesVect
.size(); idx
++) {
1554 certKeys
.push_back({certFilesVect
.at(idx
).second
, keyFilesVect
.at(idx
).second
});
1558 errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind!");
1559 g_outputBuffer
= "Error, mismatching number of certificates and keys in call to addDNSCryptBind()!";
1564 errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind()!");
1565 g_outputBuffer
= "Error, mismatching number of certificates and keys in call to addDNSCryptBind()!";
1570 auto ctx
= std::make_shared
<DNSCryptContext
>(providerName
, certKeys
);
1573 auto cs
= std::make_unique
<ClientState
>(ComboAddress(addr
, 443), false, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
1574 cs
->dnscryptCtx
= ctx
;
1575 g_dnsCryptLocals
.push_back(ctx
);
1576 g_frontends
.push_back(std::move(cs
));
1579 cs
= std::make_unique
<ClientState
>(ComboAddress(addr
, 443), true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
1580 cs
->dnscryptCtx
= ctx
;
1581 if (tcpListenQueueSize
> 0) {
1582 cs
->tcpListenQueueSize
= tcpListenQueueSize
;
1584 if (maxInFlightQueriesPerConn
> 0) {
1585 cs
->d_maxInFlightQueriesPerConn
= maxInFlightQueriesPerConn
;
1587 if (tcpMaxConcurrentConnections
> 0) {
1588 cs
->d_tcpConcurrentConnectionsLimit
= tcpMaxConcurrentConnections
;
1591 g_frontends
.push_back(std::move(cs
));
1593 catch (const std::exception
& e
) {
1594 errlog("Error during addDNSCryptBind() processing: %s", e
.what());
1595 g_outputBuffer
= "Error during addDNSCryptBind() processing: " + string(e
.what()) + "\n";
1599 luaCtx
.writeFunction("showDNSCryptBinds", []() {
1600 setLuaNoSideEffect();
1602 boost::format
fmt("%1$-3d %2% %|25t|%3$-20.20s");
1603 ret
<< (fmt
% "#" % "Address" % "Provider Name") << endl
;
1606 std::unordered_set
<std::shared_ptr
<DNSCryptContext
>> contexts
;
1607 for (const auto& frontend
: g_frontends
) {
1608 const std::shared_ptr
<DNSCryptContext
> ctx
= frontend
->dnscryptCtx
;
1609 if (!ctx
|| contexts
.count(ctx
) != 0) {
1612 contexts
.insert(ctx
);
1613 ret
<< (fmt
% idx
% frontend
->local
.toStringWithPort() % ctx
->getProviderName()) << endl
;
1617 g_outputBuffer
= ret
.str();
1620 luaCtx
.writeFunction("getDNSCryptBind", [](uint64_t idx
) {
1621 setLuaNoSideEffect();
1622 std::shared_ptr
<DNSCryptContext
> ret
= nullptr;
1623 if (idx
< g_dnsCryptLocals
.size()) {
1624 ret
= g_dnsCryptLocals
.at(idx
);
1629 luaCtx
.writeFunction("getDNSCryptBindCount", []() {
1630 setLuaNoSideEffect();
1631 return g_dnsCryptLocals
.size();
1633 #endif /* HAVE_DNSCRYPT */
1635 luaCtx
.writeFunction("showPools", []() {
1636 setLuaNoSideEffect();
1639 boost::format
fmt("%1$-20.20s %|25t|%2$20s %|25t|%3$20s %|50t|%4%");
1641 ret
<< (fmt
% "Name" % "Cache" % "ServerPolicy" % "Servers") << endl
;
1643 const auto localPools
= g_pools
.getCopy();
1644 for (const auto& entry
: localPools
) {
1645 const string
& name
= entry
.first
;
1646 const std::shared_ptr
<ServerPool
> pool
= entry
.second
;
1647 string cache
= pool
->packetCache
!= nullptr ? pool
->packetCache
->toString() : "";
1648 string policy
= g_policy
.getLocal()->getName();
1649 if (pool
->policy
!= nullptr) {
1650 policy
= pool
->policy
->getName();
1654 const auto poolServers
= pool
->getServers();
1655 for (const auto& server
: *poolServers
) {
1656 if (!servers
.empty()) {
1659 if (!server
.second
->getName().empty()) {
1660 servers
+= server
.second
->getName();
1663 servers
+= server
.second
->d_config
.remote
.toStringWithPort();
1666 ret
<< (fmt
% name
% cache
% policy
% servers
) << endl
;
1668 g_outputBuffer
= ret
.str();
1670 catch (std::exception
& e
) {
1671 g_outputBuffer
= e
.what();
1676 luaCtx
.writeFunction("getPoolNames", []() {
1677 setLuaNoSideEffect();
1678 LuaArray
<std::string
> ret
;
1680 const auto localPools
= g_pools
.getCopy();
1681 for (const auto& entry
: localPools
) {
1682 const string
& name
= entry
.first
;
1683 ret
.emplace_back(count
++, name
);
1688 luaCtx
.writeFunction("getPool", [client
](const string
& poolName
) {
1690 return std::make_shared
<ServerPool
>();
1692 auto localPools
= g_pools
.getCopy();
1693 std::shared_ptr
<ServerPool
> pool
= createPoolIfNotExists(localPools
, poolName
);
1694 g_pools
.setState(localPools
);
1698 luaCtx
.writeFunction("setVerbose", [](bool verbose
) { g_verbose
= verbose
; });
1699 luaCtx
.writeFunction("getVerbose", []() { return g_verbose
; });
1700 luaCtx
.writeFunction("setVerboseHealthChecks", [](bool verbose
) { g_verboseHealthChecks
= verbose
; });
1701 luaCtx
.writeFunction("setVerboseLogDestination", [](const std::string
& dest
) {
1702 if (!checkConfigurationTime("setVerboseLogDestination")) {
1706 auto stream
= std::ofstream(dest
.c_str());
1707 g_verboseStream
= std::move(stream
);
1709 catch (const std::exception
& e
) {
1710 errlog("Error while opening the verbose logging destination file %s: %s", dest
, e
.what());
1714 luaCtx
.writeFunction("setStaleCacheEntriesTTL", [](uint64_t ttl
) {
1715 checkParameterBound("setStaleCacheEntriesTTL", ttl
, std::numeric_limits
<uint32_t>::max());
1716 g_staleCacheEntriesTTL
= ttl
;
1719 luaCtx
.writeFunction("showBinds", []() {
1720 setLuaNoSideEffect();
1723 boost::format
fmt("%1$-3d %2$-20.20s %|35t|%3$-20.20s %|57t|%4%");
1725 ret
<< (fmt
% "#" % "Address" % "Protocol" % "Queries") << endl
;
1728 for (const auto& front
: g_frontends
) {
1729 ret
<< (fmt
% counter
% front
->local
.toStringWithPort() % front
->getType() % front
->queries
) << endl
;
1732 g_outputBuffer
= ret
.str();
1734 catch (std::exception
& e
) {
1735 g_outputBuffer
= e
.what();
1740 luaCtx
.writeFunction("getBind", [](uint64_t num
) {
1741 setLuaNoSideEffect();
1742 ClientState
* ret
= nullptr;
1743 if (num
< g_frontends
.size()) {
1744 ret
= g_frontends
[num
].get();
1749 luaCtx
.writeFunction("getBindCount", []() {
1750 setLuaNoSideEffect();
1751 return g_frontends
.size();
1754 luaCtx
.writeFunction("help", [](boost::optional
<std::string
> command
) {
1755 setLuaNoSideEffect();
1756 g_outputBuffer
= "";
1757 #ifndef DISABLE_COMPLETION
1758 for (const auto& keyword
: g_consoleKeywords
) {
1760 g_outputBuffer
+= keyword
.toString() + "\n";
1762 else if (keyword
.name
== command
) {
1763 g_outputBuffer
= keyword
.toString() + "\n";
1767 #endif /* DISABLE_COMPLETION */
1769 g_outputBuffer
= "Nothing found for " + *command
+ "\n";
1773 luaCtx
.writeFunction("showVersion", []() {
1774 setLuaNoSideEffect();
1775 g_outputBuffer
= "dnsdist " + std::string(VERSION
) + "\n";
1779 luaCtx
.writeFunction("setDefaultBPFFilter", [](std::shared_ptr
<BPFFilter
> bpf
) {
1780 if (!checkConfigurationTime("setDefaultBPFFilter")) {
1783 g_defaultBPFFilter
= std::move(bpf
);
1786 luaCtx
.writeFunction("registerDynBPFFilter", [](std::shared_ptr
<DynBPFFilter
> dbpf
) {
1788 g_dynBPFFilters
.push_back(std::move(dbpf
));
1792 luaCtx
.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr
<DynBPFFilter
> dbpf
) {
1794 for (auto it
= g_dynBPFFilters
.begin(); it
!= g_dynBPFFilters
.end(); it
++) {
1796 g_dynBPFFilters
.erase(it
);
1803 #ifndef DISABLE_DYNBLOCKS
1804 #ifndef DISABLE_DEPRECATED_DYNBLOCK
1805 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
) {
1810 struct timespec until
, now
;
1811 clock_gettime(CLOCK_MONOTONIC
, &now
);
1813 int actualSeconds
= seconds
? *seconds
: 10;
1814 until
.tv_sec
+= actualSeconds
;
1815 for (const auto& capair
: m
) {
1816 if (dynbpf
->block(capair
.first
, until
)) {
1817 warnlog("Inserting eBPF dynamic block for %s for %d seconds: %s", capair
.first
.toString(), actualSeconds
, msg
? *msg
: "");
1821 #endif /* DISABLE_DEPRECATED_DYNBLOCK */
1822 #endif /* DISABLE_DYNBLOCKS */
1824 #endif /* HAVE_EBPF */
1826 luaCtx
.writeFunction
<LuaAssociativeTable
<uint64_t>()>("getStatisticsCounters", []() {
1827 setLuaNoSideEffect();
1828 std::unordered_map
<string
, uint64_t> res
;
1830 auto entries
= dnsdist::metrics::g_stats
.entries
.read_lock();
1831 res
.reserve(entries
->size());
1832 for (const auto& entry
: *entries
) {
1833 if (const auto& val
= std::get_if
<pdns::stat_t
*>(&entry
.d_value
)) {
1834 res
[entry
.d_name
] = (*val
)->load();
1841 luaCtx
.writeFunction("includeDirectory", [&luaCtx
](const std::string
& dirname
) {
1842 if (!checkConfigurationTime("includeDirectory")) {
1846 errlog("includeDirectory() cannot be used recursively!");
1847 g_outputBuffer
= "includeDirectory() cannot be used recursively!\n";
1852 if (stat(dirname
.c_str(), &st
)) {
1853 errlog("The included directory %s does not exist!", dirname
.c_str());
1854 g_outputBuffer
= "The included directory " + dirname
+ " does not exist!";
1858 if (!S_ISDIR(st
.st_mode
)) {
1859 errlog("The included directory %s is not a directory!", dirname
.c_str());
1860 g_outputBuffer
= "The included directory " + dirname
+ " is not a directory!";
1866 std::vector
<std::string
> files
;
1867 if (!(dirp
= opendir(dirname
.c_str()))) {
1868 errlog("Error opening the included directory %s!", dirname
.c_str());
1869 g_outputBuffer
= "Error opening the included directory " + dirname
+ "!";
1873 while ((ent
= readdir(dirp
)) != NULL
) {
1874 if (ent
->d_name
[0] == '.') {
1878 if (boost::ends_with(ent
->d_name
, ".conf")) {
1879 std::ostringstream namebuf
;
1880 namebuf
<< dirname
<< "/" << ent
->d_name
;
1882 if (stat(namebuf
.str().c_str(), &st
) || !S_ISREG(st
.st_mode
)) {
1886 files
.push_back(namebuf
.str());
1891 std::sort(files
.begin(), files
.end());
1895 for (const auto& file
: files
) {
1896 std::ifstream
ifs(file
);
1898 warnlog("Unable to read configuration from '%s'", file
);
1901 vinfolog("Read configuration from '%s'", file
);
1905 luaCtx
.executeCode(ifs
);
1912 luaCtx
.executeCode(ifs
);
1918 luaCtx
.writeFunction("setAPIWritable", [](bool writable
, boost::optional
<std::string
> apiConfigDir
) {
1920 g_apiReadWrite
= writable
;
1922 if (!(*apiConfigDir
).empty()) {
1923 g_apiConfigDirectory
= *apiConfigDir
;
1926 errlog("The API configuration directory value cannot be empty!");
1927 g_outputBuffer
= "The API configuration directory value cannot be empty!";
1932 luaCtx
.writeFunction("setServFailWhenNoServer", [](bool servfail
) {
1934 g_servFailOnNoPolicy
= servfail
;
1937 luaCtx
.writeFunction("setRoundRobinFailOnNoServer", [](bool fail
) {
1939 g_roundrobinFailOnNoServer
= fail
;
1942 luaCtx
.writeFunction("setConsistentHashingBalancingFactor", [](double factor
) {
1944 if (factor
>= 1.0) {
1945 g_consistentHashBalancingFactor
= factor
;
1948 errlog("Invalid value passed to setConsistentHashingBalancingFactor()!");
1949 g_outputBuffer
= "Invalid value passed to setConsistentHashingBalancingFactor()!\n";
1954 luaCtx
.writeFunction("setWeightedBalancingFactor", [](double factor
) {
1956 if (factor
>= 1.0) {
1957 g_weightedBalancingFactor
= factor
;
1960 errlog("Invalid value passed to setWeightedBalancingFactor()!");
1961 g_outputBuffer
= "Invalid value passed to setWeightedBalancingFactor()!\n";
1966 luaCtx
.writeFunction("setRingBuffersSize", [client
](uint64_t capacity
, boost::optional
<uint64_t> numberOfShards
) {
1968 if (!checkConfigurationTime("setRingBuffersSize")) {
1972 g_rings
.setCapacity(capacity
, numberOfShards
? *numberOfShards
: 10);
1975 g_rings
.setCapacity(0, 1);
1979 luaCtx
.writeFunction("setRingBuffersLockRetries", [](uint64_t retries
) {
1981 g_rings
.setNumberOfLockRetries(retries
);
1984 luaCtx
.writeFunction("setRingBuffersOptions", [](const LuaAssociativeTable
<boost::variant
<bool, uint64_t>>& options
) {
1986 if (!checkConfigurationTime("setRingBuffersOptions")) {
1989 if (options
.count("lockRetries") > 0) {
1990 auto retries
= boost::get
<uint64_t>(options
.at("lockRetries"));
1991 g_rings
.setNumberOfLockRetries(retries
);
1993 if (options
.count("recordQueries") > 0) {
1994 auto record
= boost::get
<bool>(options
.at("recordQueries"));
1995 g_rings
.setRecordQueries(record
);
1997 if (options
.count("recordResponses") > 0) {
1998 auto record
= boost::get
<bool>(options
.at("recordResponses"));
1999 g_rings
.setRecordResponses(record
);
2003 luaCtx
.writeFunction("setWHashedPertubation", [](uint64_t perturb
) {
2005 checkParameterBound("setWHashedPertubation", perturb
, std::numeric_limits
<uint32_t>::max());
2006 g_hashperturb
= perturb
;
2009 luaCtx
.writeFunction("setTCPInternalPipeBufferSize", [](uint64_t size
) { g_tcpInternalPipeBufferSize
= size
; });
2010 luaCtx
.writeFunction("setTCPFastOpenKey", [](const std::string
& keyString
) {
2012 uint32_t key
[4] = {};
2013 auto ret
= sscanf(keyString
.c_str(), "%" SCNx32
"-%" SCNx32
"-%" SCNx32
"-%" SCNx32
, &key
[0], &key
[1], &key
[2], &key
[3]);
2015 g_outputBuffer
= "Invalid value passed to setTCPFastOpenKey()!\n";
2018 extern vector
<uint32_t> g_TCPFastOpenKey
;
2019 for (const auto i
: key
) {
2020 g_TCPFastOpenKey
.push_back(i
);
2024 #ifdef HAVE_NET_SNMP
2025 luaCtx
.writeFunction("snmpAgent", [client
, configCheck
](bool enableTraps
, boost::optional
<std::string
> daemonSocket
) {
2026 if (client
|| configCheck
) {
2029 if (!checkConfigurationTime("snmpAgent")) {
2032 if (g_snmpEnabled
) {
2033 errlog("snmpAgent() cannot be used twice!");
2034 g_outputBuffer
= "snmpAgent() cannot be used twice!\n";
2038 g_snmpEnabled
= true;
2039 g_snmpTrapsEnabled
= enableTraps
;
2040 g_snmpAgent
= new DNSDistSNMPAgent("dnsdist", daemonSocket
? *daemonSocket
: std::string());
2043 luaCtx
.writeFunction("sendCustomTrap", [](const std::string
& str
) {
2044 if (g_snmpAgent
&& g_snmpTrapsEnabled
) {
2045 g_snmpAgent
->sendCustomTrap(str
);
2048 #endif /* HAVE_NET_SNMP */
2050 #ifndef DISABLE_POLICIES_BINDINGS
2051 luaCtx
.writeFunction("setServerPolicy", [](const ServerPolicy
& policy
) {
2053 g_policy
.setState(policy
);
2056 luaCtx
.writeFunction("setServerPolicyLua", [](const string
& name
, ServerPolicy::policyfunc_t policy
) {
2058 g_policy
.setState(ServerPolicy
{name
, policy
, true});
2061 luaCtx
.writeFunction("setServerPolicyLuaFFI", [](const string
& name
, ServerPolicy::ffipolicyfunc_t policy
) {
2063 auto pol
= ServerPolicy(name
, policy
);
2064 g_policy
.setState(std::move(pol
));
2067 luaCtx
.writeFunction("setServerPolicyLuaFFIPerThread", [](const string
& name
, const std::string
& policyCode
) {
2069 auto pol
= ServerPolicy(name
, policyCode
);
2070 g_policy
.setState(std::move(pol
));
2073 luaCtx
.writeFunction("showServerPolicy", []() {
2075 g_outputBuffer
= g_policy
.getLocal()->getName() + "\n";
2078 luaCtx
.writeFunction("setPoolServerPolicy", [](ServerPolicy policy
, const string
& pool
) {
2080 auto localPools
= g_pools
.getCopy();
2081 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(std::move(policy
)));
2082 g_pools
.setState(localPools
);
2085 luaCtx
.writeFunction("setPoolServerPolicyLua", [](const string
& name
, ServerPolicy::policyfunc_t policy
, const string
& pool
) {
2087 auto localPools
= g_pools
.getCopy();
2088 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(ServerPolicy
{name
, std::move(policy
), true}));
2089 g_pools
.setState(localPools
);
2092 luaCtx
.writeFunction("setPoolServerPolicyLuaFFI", [](const string
& name
, ServerPolicy::ffipolicyfunc_t policy
, const string
& pool
) {
2094 auto localPools
= g_pools
.getCopy();
2095 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(ServerPolicy
{name
, std::move(policy
)}));
2096 g_pools
.setState(localPools
);
2099 luaCtx
.writeFunction("setPoolServerPolicyLuaFFIPerThread", [](const string
& name
, const std::string
& policyCode
, const std::string
& pool
) {
2101 auto localPools
= g_pools
.getCopy();
2102 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(ServerPolicy
{name
, policyCode
}));
2103 g_pools
.setState(localPools
);
2106 luaCtx
.writeFunction("showPoolServerPolicy", [](const std::string
& pool
) {
2108 auto localPools
= g_pools
.getCopy();
2109 auto poolObj
= getPool(localPools
, pool
);
2110 if (poolObj
->policy
== nullptr) {
2111 g_outputBuffer
= g_policy
.getLocal()->getName() + "\n";
2114 g_outputBuffer
= poolObj
->policy
->getName() + "\n";
2117 #endif /* DISABLE_POLICIES_BINDINGS */
2119 luaCtx
.writeFunction("setTCPDownstreamCleanupInterval", [](uint64_t interval
) {
2121 checkParameterBound("setTCPDownstreamCleanupInterval", interval
);
2122 setTCPDownstreamCleanupInterval(interval
);
2125 #if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
2126 luaCtx
.writeFunction("setDoHDownstreamCleanupInterval", [](uint64_t interval
) {
2128 checkParameterBound("setDoHDownstreamCleanupInterval", interval
);
2129 setDoHDownstreamCleanupInterval(interval
);
2131 #endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
2133 luaCtx
.writeFunction("setTCPDownstreamMaxIdleTime", [](uint64_t max
) {
2135 checkParameterBound("setTCPDownstreamMaxIdleTime", max
);
2136 setTCPDownstreamMaxIdleTime(max
);
2139 #if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
2140 luaCtx
.writeFunction("setDoHDownstreamMaxIdleTime", [](uint64_t max
) {
2142 checkParameterBound("setDoHDownstreamMaxIdleTime", max
);
2143 setDoHDownstreamMaxIdleTime(max
);
2145 #endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
2147 luaCtx
.writeFunction("setConsoleConnectionsLogging", [](bool enabled
) {
2148 g_logConsoleConnections
= enabled
;
2151 luaCtx
.writeFunction("setConsoleOutputMaxMsgSize", [](uint64_t size
) {
2152 checkParameterBound("setConsoleOutputMaxMsgSize", size
, std::numeric_limits
<uint32_t>::max());
2153 g_consoleOutputMsgMaxSize
= size
;
2156 luaCtx
.writeFunction("setProxyProtocolACL", [](LuaTypeOrArrayOf
<std::string
> inp
) {
2157 if (!checkConfigurationTime("setProxyProtocolACL")) {
2162 if (auto str
= boost::get
<string
>(&inp
)) {
2166 for (const auto& p
: boost::get
<LuaArray
<std::string
>>(inp
)) {
2167 nmg
.addMask(p
.second
);
2170 g_proxyProtocolACL
= std::move(nmg
);
2173 luaCtx
.writeFunction("setProxyProtocolApplyACLToProxiedClients", [](bool apply
) {
2174 if (!checkConfigurationTime("setProxyProtocolApplyACLToProxiedClients")) {
2178 g_applyACLToProxiedClients
= apply
;
2181 luaCtx
.writeFunction("setProxyProtocolMaximumPayloadSize", [](uint64_t size
) {
2182 if (!checkConfigurationTime("setProxyProtocolMaximumPayloadSize")) {
2186 g_proxyProtocolMaximumSize
= std::max(static_cast<uint64_t>(16), size
);
2189 #ifndef DISABLE_RECVMMSG
2190 luaCtx
.writeFunction("setUDPMultipleMessagesVectorSize", [](uint64_t vSize
) {
2191 if (!checkConfigurationTime("setUDPMultipleMessagesVectorSize")) {
2194 #if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
2196 g_udpVectorSize
= vSize
;
2198 errlog("recvmmsg() support is not available!");
2199 g_outputBuffer
= "recvmmsg support is not available!\n";
2202 #endif /* DISABLE_RECVMMSG */
2204 luaCtx
.writeFunction("setAddEDNSToSelfGeneratedResponses", [](bool add
) {
2205 g_addEDNSToSelfGeneratedResponses
= add
;
2208 luaCtx
.writeFunction("setPayloadSizeOnSelfGeneratedAnswers", [](uint64_t payloadSize
) {
2209 if (payloadSize
< 512) {
2210 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!");
2211 g_outputBuffer
= "setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!";
2214 if (payloadSize
> s_udpIncomingBufferSize
) {
2215 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to %d instead!", s_udpIncomingBufferSize
);
2216 g_outputBuffer
= "setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to " + std::to_string(s_udpIncomingBufferSize
) + " instead";
2217 payloadSize
= s_udpIncomingBufferSize
;
2219 g_PayloadSizeSelfGenAnswers
= payloadSize
;
2222 #ifndef DISABLE_SECPOLL
2223 luaCtx
.writeFunction("showSecurityStatus", []() {
2224 setLuaNoSideEffect();
2225 g_outputBuffer
= std::to_string(dnsdist::metrics::g_stats
.securityStatus
) + "\n";
2228 luaCtx
.writeFunction("setSecurityPollSuffix", [](const std::string
& suffix
) {
2229 if (!checkConfigurationTime("setSecurityPollSuffix")) {
2232 g_secPollSuffix
= suffix
;
2235 luaCtx
.writeFunction("setSecurityPollInterval", [](time_t newInterval
) {
2236 if (newInterval
<= 0) {
2237 warnlog("setSecurityPollInterval() should be > 0, skipping");
2238 g_outputBuffer
= "setSecurityPollInterval() should be > 0, skipping";
2241 g_secPollInterval
= newInterval
;
2243 #endif /* DISABLE_SECPOLL */
2245 luaCtx
.writeFunction("setSyslogFacility", [](boost::variant
<int, std::string
> facility
) {
2246 if (!checkConfigurationTime("setSyslogFacility")) {
2250 if (facility
.type() == typeid(std::string
)) {
2251 static std::map
<std::string
, int> const facilities
= {
2252 {"local0", LOG_LOCAL0
},
2253 {"log_local0", LOG_LOCAL0
},
2254 {"local1", LOG_LOCAL1
},
2255 {"log_local1", LOG_LOCAL1
},
2256 {"local2", LOG_LOCAL2
},
2257 {"log_local2", LOG_LOCAL2
},
2258 {"local3", LOG_LOCAL3
},
2259 {"log_local3", LOG_LOCAL3
},
2260 {"local4", LOG_LOCAL4
},
2261 {"log_local4", LOG_LOCAL4
},
2262 {"local5", LOG_LOCAL5
},
2263 {"log_local5", LOG_LOCAL5
},
2264 {"local6", LOG_LOCAL6
},
2265 {"log_local6", LOG_LOCAL6
},
2266 {"local7", LOG_LOCAL7
},
2267 {"log_local7", LOG_LOCAL7
},
2268 /* most of these likely make very little sense
2269 for dnsdist, but why not? */
2271 {"log_kern", LOG_KERN
},
2273 {"log_user", LOG_USER
},
2275 {"log_mail", LOG_MAIL
},
2276 {"daemon", LOG_DAEMON
},
2277 {"log_daemon", LOG_DAEMON
},
2279 {"log_auth", LOG_AUTH
},
2280 {"syslog", LOG_SYSLOG
},
2281 {"log_syslog", LOG_SYSLOG
},
2283 {"log_lpr", LOG_LPR
},
2285 {"log_news", LOG_NEWS
},
2287 {"log_uucp", LOG_UUCP
},
2289 {"log_cron", LOG_CRON
},
2290 {"authpriv", LOG_AUTHPRIV
},
2291 {"log_authpriv", LOG_AUTHPRIV
},
2293 {"log_ftp", LOG_FTP
}};
2294 auto facilityStr
= boost::get
<std::string
>(facility
);
2295 toLowerInPlace(facilityStr
);
2296 auto it
= facilities
.find(facilityStr
);
2297 if (it
== facilities
.end()) {
2298 g_outputBuffer
= "Unknown facility '" + facilityStr
+ "' passed to setSyslogFacility()!\n";
2301 setSyslogFacility(it
->second
);
2304 setSyslogFacility(boost::get
<int>(facility
));
2308 typedef std::unordered_map
<std::string
, std::string
> tlscertificateopts_t
;
2309 luaCtx
.writeFunction("newTLSCertificate", [client
](const std::string
& cert
, boost::optional
<tlscertificateopts_t
> opts
) {
2310 std::shared_ptr
<TLSCertKeyPair
> result
= nullptr;
2314 #if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
2315 std::optional
<std::string
> key
, password
;
2317 if (opts
->count("key")) {
2318 key
= boost::get
<const string
>((*opts
)["key"]);
2320 if (opts
->count("password")) {
2321 password
= boost::get
<const string
>((*opts
)["password"]);
2324 result
= std::make_shared
<TLSCertKeyPair
>(TLSCertKeyPair
{cert
, key
, password
});
2329 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
) {
2333 #ifdef HAVE_DNS_OVER_HTTPS
2334 if (!checkConfigurationTime("addDOHLocal")) {
2339 auto frontend
= std::make_shared
<DOHFrontend
>();
2340 if (getOptionalValue
<std::string
>(vars
, "library", frontend
->d_library
) == 0) {
2342 frontend
->d_library
= "nghttp2";
2343 #else /* HAVE_NGHTTP2 */
2344 frontend
->d_library
= "h2o";
2345 #endif /* HAVE_NGHTTP2 */
2347 if (frontend
->d_library
== "h2o") {
2348 #ifdef HAVE_LIBH2OEVLOOP
2349 frontend
= std::make_shared
<H2ODOHFrontend
>();
2350 // we _really_ need to set it again, as we just replaced the generic frontend by a new one
2351 frontend
->d_library
= "h2o";
2352 #else /* HAVE_LIBH2OEVLOOP */
2353 errlog("DOH bind %s is configured to use libh2o but the library is not available", addr
);
2355 #endif /* HAVE_LIBH2OEVLOOP */
2357 else if (frontend
->d_library
== "nghttp2") {
2358 #ifndef HAVE_NGHTTP2
2359 errlog("DOH bind %s is configured to use nghttp2 but the library is not available", addr
);
2361 #endif /* HAVE_NGHTTP2 */
2364 errlog("DOH bind %s is configured to use an unknown library ('%s')", addr
, frontend
->d_library
);
2369 if (certFiles
&& !certFiles
->empty()) {
2370 if (!loadTLSCertificateAndKeys("addDOHLocal", frontend
->d_tlsContext
.d_tlsConfig
.d_certKeyPairs
, *certFiles
, *keyFiles
)) {
2374 frontend
->d_tlsContext
.d_addr
= ComboAddress(addr
, 443);
2377 frontend
->d_tlsContext
.d_addr
= ComboAddress(addr
, 80);
2378 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());
2383 if (urls
->type() == typeid(std::string
)) {
2384 frontend
->d_urls
.insert(boost::get
<std::string
>(*urls
));
2386 else if (urls
->type() == typeid(LuaArray
<std::string
>)) {
2387 auto urlsVect
= boost::get
<LuaArray
<std::string
>>(*urls
);
2388 for (const auto& p
: urlsVect
) {
2389 frontend
->d_urls
.insert(p
.second
);
2394 frontend
->d_urls
.insert("/dns-query");
2397 bool reusePort
= false;
2398 int tcpFastOpenQueueSize
= 0;
2399 int tcpListenQueueSize
= 0;
2400 uint64_t maxInFlightQueriesPerConn
= 0;
2401 uint64_t tcpMaxConcurrentConnections
= 0;
2402 std::string interface
;
2404 std::vector
<std::pair
<ComboAddress
, int>> additionalAddresses
;
2407 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConnections
);
2408 getOptionalValue
<int>(vars
, "idleTimeout", frontend
->d_idleTimeout
);
2409 getOptionalValue
<std::string
>(vars
, "serverTokens", frontend
->d_serverTokens
);
2410 getOptionalValue
<std::string
>(vars
, "provider", frontend
->d_tlsContext
.d_provider
);
2411 boost::algorithm::to_lower(frontend
->d_tlsContext
.d_provider
);
2412 getOptionalValue
<bool>(vars
, "proxyProtocolOutsideTLS", frontend
->d_tlsContext
.d_proxyProtocolOutsideTLS
);
2414 LuaAssociativeTable
<std::string
> customResponseHeaders
;
2415 if (getOptionalValue
<decltype(customResponseHeaders
)>(vars
, "customResponseHeaders", customResponseHeaders
) > 0) {
2416 for (auto const& headerMap
: customResponseHeaders
) {
2417 auto headerResponse
= std::pair(boost::to_lower_copy(headerMap
.first
), headerMap
.second
);
2418 frontend
->d_customResponseHeaders
.insert(headerResponse
);
2422 getOptionalValue
<bool>(vars
, "sendCacheControlHeaders", frontend
->d_sendCacheControlHeaders
);
2423 getOptionalValue
<bool>(vars
, "keepIncomingHeaders", frontend
->d_keepIncomingHeaders
);
2424 getOptionalValue
<bool>(vars
, "trustForwardedForHeader", frontend
->d_trustForwardedForHeader
);
2425 getOptionalValue
<bool>(vars
, "earlyACLDrop", frontend
->d_earlyACLDrop
);
2426 getOptionalValue
<int>(vars
, "internalPipeBufferSize", frontend
->d_internalPipeBufferSize
);
2427 getOptionalValue
<bool>(vars
, "exactPathMatching", frontend
->d_exactPathMatching
);
2429 LuaArray
<std::string
> addresses
;
2430 if (getOptionalValue
<decltype(addresses
)>(vars
, "additionalAddresses", addresses
) > 0) {
2431 for (const auto& [_
, add
] : addresses
) {
2433 ComboAddress
address(add
);
2434 additionalAddresses
.emplace_back(address
, -1);
2436 catch (const PDNSException
& e
) {
2437 errlog("Unable to parse additional address %s for DOH bind: %s", add
, e
.reason
);
2443 parseTLSConfig(frontend
->d_tlsContext
.d_tlsConfig
, "addDOHLocal", vars
);
2445 bool ignoreTLSConfigurationErrors
= false;
2446 if (getOptionalValue
<bool>(vars
, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors
) > 0 && ignoreTLSConfigurationErrors
) {
2447 // we are asked to try to load the certificates so we can return a potential error
2448 // and properly ignore the frontend before actually launching it
2450 std::map
<int, std::string
> ocspResponses
= {};
2451 auto ctx
= libssl_init_server_context(frontend
->d_tlsContext
.d_tlsConfig
, ocspResponses
);
2453 catch (const std::runtime_error
& e
) {
2454 errlog("Ignoring DoH frontend: '%s'", e
.what());
2459 checkAllParametersConsumed("addDOHLocal", vars
);
2462 if (useTLS
&& frontend
->d_library
== "nghttp2") {
2463 if (!frontend
->d_tlsContext
.d_provider
.empty()) {
2464 vinfolog("Loading TLS provider '%s'", frontend
->d_tlsContext
.d_provider
);
2468 const std::string
provider("openssl");
2470 const std::string
provider("gnutls");
2472 vinfolog("Loading default TLS provider '%s'", provider
);
2476 g_dohlocals
.push_back(frontend
);
2477 auto cs
= std::make_unique
<ClientState
>(frontend
->d_tlsContext
.d_addr
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
2478 cs
->dohFrontend
= frontend
;
2479 cs
->d_additionalAddresses
= std::move(additionalAddresses
);
2481 if (tcpListenQueueSize
> 0) {
2482 cs
->tcpListenQueueSize
= tcpListenQueueSize
;
2484 if (tcpMaxConcurrentConnections
> 0) {
2485 cs
->d_tcpConcurrentConnectionsLimit
= tcpMaxConcurrentConnections
;
2487 g_frontends
.push_back(std::move(cs
));
2488 #else /* HAVE_DNS_OVER_HTTPS */
2489 throw std::runtime_error("addDOHLocal() called but DNS over HTTPS support is not present!");
2490 #endif /* HAVE_DNS_OVER_HTTPS */
2493 // NOLINTNEXTLINE(performance-unnecessary-value-param): somehow clang-tidy gets confused about the fact vars could be const while it cannot
2494 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
) {
2498 #ifdef HAVE_DNS_OVER_QUIC
2499 if (!checkConfigurationTime("addDOQLocal")) {
2504 auto frontend
= std::make_shared
<DOQFrontend
>();
2505 if (!loadTLSCertificateAndKeys("addDOQLocal", frontend
->d_tlsConfig
.d_certKeyPairs
, certFiles
, keyFiles
)) {
2508 frontend
->d_local
= ComboAddress(addr
, 853);
2510 bool reusePort
= false;
2511 int tcpFastOpenQueueSize
= 0;
2512 int tcpListenQueueSize
= 0;
2513 uint64_t maxInFlightQueriesPerConn
= 0;
2514 uint64_t tcpMaxConcurrentConnections
= 0;
2515 std::string interface
;
2517 std::vector
<std::pair
<ComboAddress
, int>> additionalAddresses
;
2520 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConnections
);
2521 if (maxInFlightQueriesPerConn
> 0) {
2522 frontend
->d_maxInFlight
= maxInFlightQueriesPerConn
;
2524 getOptionalValue
<int>(vars
, "internalPipeBufferSize", frontend
->d_internalPipeBufferSize
);
2525 getOptionalValue
<int>(vars
, "idleTimeout", frontend
->d_idleTimeout
);
2526 getOptionalValue
<std::string
>(vars
, "keyLogFile", frontend
->d_keyLogFile
);
2528 std::string valueStr
;
2529 if (getOptionalValue
<std::string
>(vars
, "congestionControlAlgo", valueStr
) > 0) {
2530 if (DOQFrontend::s_available_cc_algorithms
.count(valueStr
) > 0) {
2531 frontend
->d_ccAlgo
= valueStr
;
2534 warnlog("Ignoring unknown value '%s' for 'congestionControlAlgo' on 'addDOQLocal'", valueStr
);
2538 parseTLSConfig(frontend
->d_tlsConfig
, "addDOQLocal", vars
);
2540 bool ignoreTLSConfigurationErrors
= false;
2541 if (getOptionalValue
<bool>(vars
, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors
) > 0 && ignoreTLSConfigurationErrors
) {
2542 // we are asked to try to load the certificates so we can return a potential error
2543 // and properly ignore the frontend before actually launching it
2545 std::map
<int, std::string
> ocspResponses
= {};
2546 auto ctx
= libssl_init_server_context(frontend
->d_tlsConfig
, ocspResponses
);
2548 catch (const std::runtime_error
& e
) {
2549 errlog("Ignoring DoQ frontend: '%s'", e
.what());
2554 checkAllParametersConsumed("addDOQLocal", vars
);
2556 g_doqlocals
.push_back(frontend
);
2557 auto cs
= std::make_unique
<ClientState
>(frontend
->d_local
, false, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
2558 cs
->doqFrontend
= frontend
;
2559 cs
->d_additionalAddresses
= std::move(additionalAddresses
);
2561 g_frontends
.push_back(std::move(cs
));
2563 throw std::runtime_error("addDOQLocal() called but DNS over QUIC support is not present!");
2567 luaCtx
.writeFunction("showDOQFrontends", []() {
2568 #ifdef HAVE_DNS_OVER_QUIC
2569 setLuaNoSideEffect();
2572 boost::format
fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d");
2573 ret
<< (fmt
% "#" % "Address" % "Bad Version" % "Invalid Token" % "Errors" % "Valid") << endl
;
2575 for (const auto& ctx
: g_doqlocals
) {
2576 ret
<< (fmt
% counter
% ctx
->d_local
.toStringWithPort() % ctx
->d_doqUnsupportedVersionErrors
% ctx
->d_doqInvalidTokensReceived
% ctx
->d_errorResponses
% ctx
->d_validResponses
) << endl
;
2579 g_outputBuffer
= ret
.str();
2581 catch (const std::exception
& e
) {
2582 g_outputBuffer
= e
.what();
2586 g_outputBuffer
= "DNS over QUIC support is not present!\n";
2590 luaCtx
.writeFunction("showDOHFrontends", []() {
2591 #ifdef HAVE_DNS_OVER_HTTPS
2592 setLuaNoSideEffect();
2595 boost::format
fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d");
2596 ret
<< (fmt
% "#" % "Address" % "HTTP" % "HTTP/1" % "HTTP/2" % "GET" % "POST" % "Bad" % "Errors" % "Redirects" % "Valid" % "# ticket keys" % "Rotation delay" % "Next rotation") << endl
;
2598 for (const auto& ctx
: g_dohlocals
) {
2599 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
;
2602 g_outputBuffer
= ret
.str();
2604 catch (const std::exception
& e
) {
2605 g_outputBuffer
= e
.what();
2609 g_outputBuffer
= "DNS over HTTPS support is not present!\n";
2613 luaCtx
.writeFunction("showDOHResponseCodes", []() {
2614 #ifdef HAVE_DNS_OVER_HTTPS
2615 setLuaNoSideEffect();
2618 boost::format
fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d %-15d %-15d");
2619 g_outputBuffer
= "\n- HTTP/1:\n\n";
2620 ret
<< (fmt
% "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others") << endl
;
2622 for (const auto& ctx
: g_dohlocals
) {
2623 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
;
2626 g_outputBuffer
+= ret
.str();
2629 g_outputBuffer
+= "\n- HTTP/2:\n\n";
2630 ret
<< (fmt
% "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others") << endl
;
2632 for (const auto& ctx
: g_dohlocals
) {
2633 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
;
2636 g_outputBuffer
+= ret
.str();
2638 catch (const std::exception
& e
) {
2639 g_outputBuffer
= e
.what();
2643 g_outputBuffer
= "DNS over HTTPS support is not present!\n";
2647 luaCtx
.writeFunction("getDOHFrontend", [client
](uint64_t index
) {
2648 std::shared_ptr
<DOHFrontend
> result
= nullptr;
2652 #ifdef HAVE_DNS_OVER_HTTPS
2653 setLuaNoSideEffect();
2655 if (index
< g_dohlocals
.size()) {
2656 result
= g_dohlocals
.at(index
);
2659 errlog("Error: trying to get DOH frontend with index %d but we only have %d frontend(s)\n", index
, g_dohlocals
.size());
2660 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";
2663 catch (const std::exception
& e
) {
2664 g_outputBuffer
= "Error while trying to get DOH frontend with index " + std::to_string(index
) + ": " + string(e
.what()) + "\n";
2665 errlog("Error while trying to get DOH frontend with index %d: %s\n", index
, string(e
.what()));
2668 g_outputBuffer
="DNS over HTTPS support is not present!\n";
2673 luaCtx
.writeFunction("getDOHFrontendCount", []() {
2674 setLuaNoSideEffect();
2675 return g_dohlocals
.size();
2678 luaCtx
.registerFunction
<void (std::shared_ptr
<DOHFrontend
>::*)()>("reloadCertificates", [](std::shared_ptr
<DOHFrontend
> frontend
) {
2679 if (frontend
!= nullptr) {
2680 frontend
->reloadCertificates();
2684 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
) {
2685 #ifdef HAVE_DNS_OVER_HTTPS
2686 if (frontend
!= nullptr) {
2687 if (loadTLSCertificateAndKeys("DOHFrontend::loadNewCertificatesAndKeys", frontend
->d_tlsContext
.d_tlsConfig
.d_certKeyPairs
, certFiles
, keyFiles
)) {
2688 frontend
->reloadCertificates();
2694 luaCtx
.registerFunction
<void (std::shared_ptr
<DOHFrontend
>::*)()>("rotateTicketsKey", [](std::shared_ptr
<DOHFrontend
> frontend
) {
2695 if (frontend
!= nullptr) {
2696 frontend
->rotateTicketsKey(time(nullptr));
2700 luaCtx
.registerFunction
<void (std::shared_ptr
<DOHFrontend
>::*)(const std::string
&)>("loadTicketsKeys", [](std::shared_ptr
<DOHFrontend
> frontend
, const std::string
& file
) {
2701 if (frontend
!= nullptr) {
2702 frontend
->loadTicketsKeys(file
);
2706 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
) {
2707 if (frontend
!= nullptr) {
2708 auto newMap
= std::make_shared
<std::vector
<std::shared_ptr
<DOHResponseMapEntry
>>>();
2709 newMap
->reserve(map
.size());
2711 for (const auto& entry
: map
) {
2712 newMap
->push_back(entry
.second
);
2715 frontend
->d_responsesMap
= std::move(newMap
);
2719 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
) {
2723 #ifdef HAVE_DNS_OVER_TLS
2724 if (!checkConfigurationTime("addTLSLocal")) {
2729 auto frontend
= std::make_shared
<TLSFrontend
>(TLSFrontend::ALPN::DoT
);
2730 if (!loadTLSCertificateAndKeys("addTLSLocal", frontend
->d_tlsConfig
.d_certKeyPairs
, certFiles
, keyFiles
)) {
2734 bool reusePort
= false;
2735 int tcpFastOpenQueueSize
= 0;
2736 int tcpListenQueueSize
= 0;
2737 uint64_t maxInFlightQueriesPerConn
= 0;
2738 uint64_t tcpMaxConcurrentConns
= 0;
2739 std::string interface
;
2741 std::vector
<std::pair
<ComboAddress
, int>> additionalAddresses
;
2744 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConns
);
2746 getOptionalValue
<std::string
>(vars
, "provider", frontend
->d_provider
);
2747 boost::algorithm::to_lower(frontend
->d_provider
);
2748 getOptionalValue
<bool>(vars
, "proxyProtocolOutsideTLS", frontend
->d_proxyProtocolOutsideTLS
);
2750 LuaArray
<std::string
> addresses
;
2751 if (getOptionalValue
<decltype(addresses
)>(vars
, "additionalAddresses", addresses
) > 0) {
2752 for (const auto& [_
, add
] : addresses
) {
2754 ComboAddress
address(add
);
2755 additionalAddresses
.emplace_back(address
, -1);
2757 catch (const PDNSException
& e
) {
2758 errlog("Unable to parse additional address %s for DoT bind: %s", add
, e
.reason
);
2764 parseTLSConfig(frontend
->d_tlsConfig
, "addTLSLocal", vars
);
2766 bool ignoreTLSConfigurationErrors
= false;
2767 if (getOptionalValue
<bool>(vars
, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors
) > 0 && ignoreTLSConfigurationErrors
) {
2768 // we are asked to try to load the certificates so we can return a potential error
2769 // and properly ignore the frontend before actually launching it
2771 std::map
<int, std::string
> ocspResponses
= {};
2772 auto ctx
= libssl_init_server_context(frontend
->d_tlsConfig
, ocspResponses
);
2774 catch (const std::runtime_error
& e
) {
2775 errlog("Ignoring TLS frontend: '%s'", e
.what());
2780 checkAllParametersConsumed("addTLSLocal", vars
);
2784 frontend
->d_addr
= ComboAddress(addr
, 853);
2785 if (!frontend
->d_provider
.empty()) {
2786 vinfolog("Loading TLS provider '%s'", frontend
->d_provider
);
2790 const std::string
provider("openssl");
2792 const std::string
provider("gnutls");
2794 vinfolog("Loading default TLS provider '%s'", provider
);
2796 // only works pre-startup, so no sync necessary
2797 auto cs
= std::make_unique
<ClientState
>(frontend
->d_addr
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
2798 cs
->tlsFrontend
= frontend
;
2799 cs
->d_additionalAddresses
= std::move(additionalAddresses
);
2800 if (tcpListenQueueSize
> 0) {
2801 cs
->tcpListenQueueSize
= tcpListenQueueSize
;
2803 if (maxInFlightQueriesPerConn
> 0) {
2804 cs
->d_maxInFlightQueriesPerConn
= maxInFlightQueriesPerConn
;
2806 if (tcpMaxConcurrentConns
> 0) {
2807 cs
->d_tcpConcurrentConnectionsLimit
= tcpMaxConcurrentConns
;
2810 g_tlslocals
.push_back(cs
->tlsFrontend
);
2811 g_frontends
.push_back(std::move(cs
));
2813 catch (const std::exception
& e
) {
2814 g_outputBuffer
= "Error: " + string(e
.what()) + "\n";
2817 throw std::runtime_error("addTLSLocal() called but DNS over TLS support is not present!");
2821 luaCtx
.writeFunction("showTLSContexts", []() {
2822 #ifdef HAVE_DNS_OVER_TLS
2823 setLuaNoSideEffect();
2826 boost::format
fmt("%1$-3d %2$-20.20s %|25t|%3$-14d %|40t|%4$-14d %|54t|%5$-21.21s");
2828 ret
<< (fmt
% "#" % "Address" % "# ticket keys" % "Rotation delay" % "Next rotation") << endl
;
2830 for (const auto& ctx
: g_tlslocals
) {
2831 ret
<< (fmt
% counter
% ctx
->d_addr
.toStringWithPort() % ctx
->getTicketsKeysCount() % ctx
->getTicketsKeyRotationDelay() % ctx
->getNextTicketsKeyRotation()) << endl
;
2834 g_outputBuffer
= ret
.str();
2836 catch (const std::exception
& e
) {
2837 g_outputBuffer
= e
.what();
2841 g_outputBuffer
= "DNS over TLS support is not present!\n";
2845 luaCtx
.writeFunction("getTLSContext", [](uint64_t index
) {
2846 std::shared_ptr
<TLSCtx
> result
= nullptr;
2847 #ifdef HAVE_DNS_OVER_TLS
2848 setLuaNoSideEffect();
2850 if (index
< g_tlslocals
.size()) {
2851 result
= g_tlslocals
.at(index
)->getContext();
2854 errlog("Error: trying to get TLS context with index %d but we only have %d context(s)\n", index
, g_tlslocals
.size());
2855 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";
2858 catch (const std::exception
& e
) {
2859 g_outputBuffer
= "Error while trying to get TLS context with index " + std::to_string(index
) + ": " + string(e
.what()) + "\n";
2860 errlog("Error while trying to get TLS context with index %d: %s\n", index
, string(e
.what()));
2863 g_outputBuffer
="DNS over TLS support is not present!\n";
2868 luaCtx
.writeFunction("getTLSFrontend", [](uint64_t index
) {
2869 std::shared_ptr
<TLSFrontend
> result
= nullptr;
2870 #ifdef HAVE_DNS_OVER_TLS
2871 setLuaNoSideEffect();
2873 if (index
< g_tlslocals
.size()) {
2874 result
= g_tlslocals
.at(index
);
2877 errlog("Error: trying to get TLS frontend with index %d but we only have %d frontends\n", index
, g_tlslocals
.size());
2878 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";
2881 catch (const std::exception
& e
) {
2882 g_outputBuffer
= "Error while trying to get TLS frontend with index " + std::to_string(index
) + ": " + string(e
.what()) + "\n";
2883 errlog("Error while trying to get TLS frontend with index %d: %s\n", index
, string(e
.what()));
2886 g_outputBuffer
="DNS over TLS support is not present!\n";
2891 luaCtx
.writeFunction("getTLSFrontendCount", []() {
2892 setLuaNoSideEffect();
2893 return g_tlslocals
.size();
2896 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSCtx
>::*)()>("rotateTicketsKey", [](std::shared_ptr
<TLSCtx
>& ctx
) {
2897 if (ctx
!= nullptr) {
2898 ctx
->rotateTicketsKey(time(nullptr));
2902 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSCtx
>::*)(const std::string
&)>("loadTicketsKeys", [](std::shared_ptr
<TLSCtx
>& ctx
, const std::string
& file
) {
2903 if (ctx
!= nullptr) {
2904 ctx
->loadTicketsKeys(file
);
2908 luaCtx
.registerFunction
<std::string (std::shared_ptr
<TLSFrontend
>::*)() const>("getAddressAndPort", [](const std::shared_ptr
<TLSFrontend
>& frontend
) {
2909 if (frontend
== nullptr) {
2910 return std::string();
2912 return frontend
->d_addr
.toStringWithPort();
2915 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSFrontend
>::*)()>("rotateTicketsKey", [](std::shared_ptr
<TLSFrontend
>& frontend
) {
2916 if (frontend
== nullptr) {
2919 auto ctx
= frontend
->getContext();
2921 ctx
->rotateTicketsKey(time(nullptr));
2925 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSFrontend
>::*)(const std::string
&)>("loadTicketsKeys", [](std::shared_ptr
<TLSFrontend
>& frontend
, const std::string
& file
) {
2926 if (frontend
== nullptr) {
2929 auto ctx
= frontend
->getContext();
2931 ctx
->loadTicketsKeys(file
);
2935 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSFrontend
>::*)()>("reloadCertificates", [](std::shared_ptr
<TLSFrontend
>& frontend
) {
2936 if (frontend
== nullptr) {
2939 frontend
->setupTLS();
2942 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
) {
2943 #ifdef HAVE_DNS_OVER_TLS
2944 if (loadTLSCertificateAndKeys("TLSFrontend::loadNewCertificatesAndKeys", frontend
->d_tlsConfig
.d_certKeyPairs
, certFiles
, keyFiles
)) {
2945 frontend
->setupTLS();
2950 luaCtx
.writeFunction("reloadAllCertificates", []() {
2951 for (auto& frontend
: g_frontends
) {
2956 #ifdef HAVE_DNSCRYPT
2957 if (frontend
->dnscryptCtx
) {
2958 frontend
->dnscryptCtx
->reloadCertificates();
2960 #endif /* HAVE_DNSCRYPT */
2961 #ifdef HAVE_DNS_OVER_TLS
2962 if (frontend
->tlsFrontend
) {
2963 frontend
->tlsFrontend
->setupTLS();
2965 #endif /* HAVE_DNS_OVER_TLS */
2966 #ifdef HAVE_DNS_OVER_HTTPS
2967 if (frontend
->dohFrontend
) {
2968 frontend
->dohFrontend
->reloadCertificates();
2970 #endif /* HAVE_DNS_OVER_HTTPS */
2972 catch (const std::exception
& e
) {
2973 errlog("Error reloading certificates for frontend %s: %s", frontend
->local
.toStringWithPort(), e
.what());
2978 luaCtx
.writeFunction("setAllowEmptyResponse", [](bool allow
) { g_allowEmptyResponse
= allow
; });
2979 luaCtx
.writeFunction("setDropEmptyQueries", [](bool drop
) { extern bool g_dropEmptyQueries
; g_dropEmptyQueries
= drop
; });
2981 #if defined(HAVE_LIBSSL) && defined(HAVE_OCSP_BASIC_SIGN) && !defined(DISABLE_OCSP_STAPLING)
2982 luaCtx
.writeFunction("generateOCSPResponse", [client
](const std::string
& certFile
, const std::string
& caCert
, const std::string
& caKey
, const std::string
& outFile
, int ndays
, int nmin
) {
2987 libssl_generate_ocsp_response(certFile
, caCert
, caKey
, outFile
, ndays
, nmin
);
2989 #endif /* HAVE_LIBSSL && HAVE_OCSP_BASIC_SIGN && !DISABLE_OCSP_STAPLING */
2991 luaCtx
.writeFunction("addCapabilitiesToRetain", [](LuaTypeOrArrayOf
<std::string
> caps
) {
2992 if (!checkConfigurationTime("addCapabilitiesToRetain")) {
2996 if (caps
.type() == typeid(std::string
)) {
2997 g_capabilitiesToRetain
.insert(boost::get
<std::string
>(caps
));
2999 else if (caps
.type() == typeid(LuaArray
<std::string
>)) {
3000 for (const auto& cap
: boost::get
<LuaArray
<std::string
>>(caps
)) {
3001 g_capabilitiesToRetain
.insert(cap
.second
);
3006 luaCtx
.writeFunction("setUDPSocketBufferSizes", [client
](uint64_t recv
, uint64_t snd
) {
3010 if (!checkConfigurationTime("setUDPSocketBufferSizes")) {
3013 checkParameterBound("setUDPSocketBufferSizes", recv
, std::numeric_limits
<uint32_t>::max());
3014 checkParameterBound("setUDPSocketBufferSizes", snd
, std::numeric_limits
<uint32_t>::max());
3017 g_socketUDPSendBuffer
= snd
;
3018 g_socketUDPRecvBuffer
= recv
;
3021 luaCtx
.writeFunction("setRandomizedOutgoingSockets", [](bool randomized
) {
3022 DownstreamState::s_randomizeSockets
= randomized
;
3025 luaCtx
.writeFunction("setRandomizedIdsOverUDP", [](bool randomized
) {
3026 DownstreamState::s_randomizeIDs
= randomized
;
3029 #if defined(HAVE_LIBSSL) && !defined(HAVE_TLS_PROVIDERS)
3030 luaCtx
.writeFunction("loadTLSEngine", [client
](const std::string
& engineName
, boost::optional
<std::string
> defaultString
) {
3035 auto [success
, error
] = libssl_load_engine(engineName
, defaultString
? std::optional
<std::string
>(*defaultString
) : std::nullopt
);
3037 g_outputBuffer
= "Error while trying to load TLS engine '" + engineName
+ "': " + error
+ "\n";
3038 errlog("Error while trying to load TLS engine '%s': %s", engineName
, error
);
3041 #endif /* HAVE_LIBSSL && !HAVE_TLS_PROVIDERS */
3043 #if defined(HAVE_LIBSSL) && OPENSSL_VERSION_MAJOR >= 3 && defined(HAVE_TLS_PROVIDERS)
3044 luaCtx
.writeFunction("loadTLSProvider", [client
](const std::string
& providerName
) {
3049 auto [success
, error
] = libssl_load_provider(providerName
);
3051 g_outputBuffer
= "Error while trying to load TLS provider '" + providerName
+ "': " + error
+ "\n";
3052 errlog("Error while trying to load TLS provider '%s': %s", providerName
, error
);
3055 #endif /* HAVE_LIBSSL && OPENSSL_VERSION_MAJOR >= 3 && HAVE_TLS_PROVIDERS */
3057 luaCtx
.writeFunction("newThread", [client
, configCheck
](const std::string
& code
) {
3058 if (client
|| configCheck
) {
3061 std::thread
newThread(LuaThread
, code
);
3066 luaCtx
.writeFunction("declareMetric", [](const std::string
& name
, const std::string
& type
, const std::string
& description
, boost::optional
<std::string
> customName
) {
3067 auto result
= dnsdist::metrics::declareCustomMetric(name
, type
, description
, customName
? std::optional
<std::string
>(*customName
) : std::nullopt
);
3069 g_outputBuffer
+= *result
+ "\n";
3070 errlog("Error in declareMetric: %s", *result
);
3075 luaCtx
.writeFunction("incMetric", [](const std::string
& name
, boost::optional
<uint64_t> step
) {
3076 auto result
= dnsdist::metrics::incrementCustomCounter(name
, step
? *step
: 1);
3077 if (const auto* errorStr
= std::get_if
<dnsdist::metrics::Error
>(&result
)) {
3078 g_outputBuffer
= *errorStr
+ "'\n";
3079 errlog("Error in incMetric: %s", *errorStr
);
3080 return static_cast<uint64_t>(0);
3082 return std::get
<uint64_t>(result
);
3084 luaCtx
.writeFunction("decMetric", [](const std::string
& name
, boost::optional
<uint64_t> step
) {
3085 auto result
= dnsdist::metrics::decrementCustomCounter(name
, step
? *step
: 1);
3086 if (const auto* errorStr
= std::get_if
<dnsdist::metrics::Error
>(&result
)) {
3087 g_outputBuffer
= *errorStr
+ "'\n";
3088 errlog("Error in decMetric: %s", *errorStr
);
3089 return static_cast<uint64_t>(0);
3091 return std::get
<uint64_t>(result
);
3093 luaCtx
.writeFunction("setMetric", [](const std::string
& name
, const double value
) -> double {
3094 auto result
= dnsdist::metrics::setCustomGauge(name
, value
);
3095 if (const auto* errorStr
= std::get_if
<dnsdist::metrics::Error
>(&result
)) {
3096 g_outputBuffer
= *errorStr
+ "'\n";
3097 errlog("Error in setMetric: %s", *errorStr
);
3100 return std::get
<double>(result
);
3102 luaCtx
.writeFunction("getMetric", [](const std::string
& name
) {
3103 auto result
= dnsdist::metrics::getCustomMetric(name
);
3104 if (const auto* errorStr
= std::get_if
<dnsdist::metrics::Error
>(&result
)) {
3105 g_outputBuffer
= *errorStr
+ "'\n";
3106 errlog("Error in getMetric: %s", *errorStr
);
3109 return std::get
<double>(result
);
3113 vector
<std::function
<void(void)>> setupLua(LuaContext
& luaCtx
, bool client
, bool configCheck
, const std::string
& config
)
3115 // this needs to exist only during the parsing of the configuration
3116 // and cannot be captured by lambdas
3117 g_launchWork
= std::vector
<std::function
<void(void)>>();
3119 setupLuaActions(luaCtx
);
3120 setupLuaConfig(luaCtx
, client
, configCheck
);
3121 setupLuaBindings(luaCtx
, client
);
3122 setupLuaBindingsDNSCrypt(luaCtx
, client
);
3123 setupLuaBindingsDNSParser(luaCtx
);
3124 setupLuaBindingsDNSQuestion(luaCtx
);
3125 setupLuaBindingsKVS(luaCtx
, client
);
3126 setupLuaBindingsNetwork(luaCtx
, client
);
3127 setupLuaBindingsPacketCache(luaCtx
, client
);
3128 setupLuaBindingsProtoBuf(luaCtx
, client
, configCheck
);
3129 setupLuaBindingsRings(luaCtx
, client
);
3130 setupLuaInspection(luaCtx
);
3131 setupLuaRules(luaCtx
);
3132 setupLuaVars(luaCtx
);
3133 setupLuaWeb(luaCtx
);
3135 #ifdef LUAJIT_VERSION
3136 luaCtx
.executeCode(getLuaFFIWrappers());
3139 std::ifstream
ifs(config
);
3142 throw std::runtime_error("Unable to read configuration file from " + config
);
3145 warnlog("Unable to read configuration from '%s'", config
);
3149 vinfolog("Read configuration from '%s'", config
);
3152 luaCtx
.executeCode(ifs
);
3154 auto ret
= *g_launchWork
;
3155 g_launchWork
= boost::none
;