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-console.hh"
41 #include "dnsdist-dynblocks.hh"
42 #include "dnsdist-discovery.hh"
43 #include "dnsdist-ecs.hh"
44 #include "dnsdist-healthchecks.hh"
45 #include "dnsdist-lua.hh"
47 #include "dnsdist-lua-ffi.hh"
48 #endif /* LUAJIT_VERSION */
49 #include "dnsdist-nghttp2.hh"
50 #include "dnsdist-proxy-protocol.hh"
51 #include "dnsdist-rings.hh"
52 #include "dnsdist-secpoll.hh"
53 #include "dnsdist-session-cache.hh"
54 #include "dnsdist-tcp-downstream.hh"
55 #include "dnsdist-web.hh"
59 #include "sodcrypto.hh"
65 #include <boost/logic/tribool.hpp>
66 #include <boost/uuid/string_generator.hpp>
69 #include <systemd/sd-daemon.h>
74 static boost::optional
<std::vector
<std::function
<void(void)>>> g_launchWork
= boost::none
;
76 boost::tribool g_noLuaSideEffect
;
77 static bool g_included
{false};
79 /* this is a best effort way to prevent logging calls with no side-effects in the output of delta()
80 Functions can declare setLuaNoSideEffect() and if nothing else does declare a side effect, or nothing
81 has done so before on this invocation, this call won't be part of delta() output */
82 void setLuaNoSideEffect()
84 if (g_noLuaSideEffect
== false) // there has been a side effect already
86 g_noLuaSideEffect
= true;
89 void setLuaSideEffect()
91 g_noLuaSideEffect
= false;
94 bool getLuaNoSideEffect()
96 if (g_noLuaSideEffect
) {
102 void resetLuaSideEffect()
104 g_noLuaSideEffect
= boost::logic::indeterminate
;
107 using localbind_t
= LuaAssociativeTable
<boost::variant
<bool, int, std::string
, LuaArray
<int>, LuaArray
<std::string
>, LuaAssociativeTable
<std::string
>>>;
109 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
)
112 if (vars
->count("reusePort")) {
113 reusePort
= boost::get
<bool>((*vars
)["reusePort"]);
115 if (vars
->count("tcpFastOpenQueueSize")) {
116 tcpFastOpenQueueSize
= boost::get
<int>((*vars
)["tcpFastOpenQueueSize"]);
118 if (vars
->count("tcpListenQueueSize")) {
119 tcpListenQueueSize
= boost::get
<int>((*vars
)["tcpListenQueueSize"]);
121 if (vars
->count("maxConcurrentTCPConnections")) {
122 tcpMaxConcurrentConnections
= boost::get
<int>((*vars
)["maxConcurrentTCPConnections"]);
124 if (vars
->count("maxInFlight")) {
125 maxInFlightQueriesPerConnection
= boost::get
<int>((*vars
)["maxInFlight"]);
127 if (vars
->count("interface")) {
128 interface
= boost::get
<std::string
>((*vars
)["interface"]);
130 if (vars
->count("cpus")) {
131 for (const auto& cpu
: boost::get
<LuaArray
<int>>((*vars
)["cpus"])) {
132 cpus
.insert(cpu
.second
);
138 #if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
139 static bool loadTLSCertificateAndKeys(const std::string
& context
, std::vector
<TLSCertKeyPair
>& pairs
, boost::variant
<std::string
, std::shared_ptr
<TLSCertKeyPair
>, LuaArray
<std::string
>, LuaArray
<std::shared_ptr
<TLSCertKeyPair
>>> certFiles
, LuaTypeOrArrayOf
<std::string
> keyFiles
)
141 if (certFiles
.type() == typeid(std::string
) && keyFiles
.type() == typeid(std::string
)) {
142 auto certFile
= boost::get
<std::string
>(certFiles
);
143 auto keyFile
= boost::get
<std::string
>(keyFiles
);
145 pairs
.emplace_back(certFile
, keyFile
);
147 else if (certFiles
.type() == typeid(std::shared_ptr
<TLSCertKeyPair
>)) {
148 auto cert
= boost::get
<std::shared_ptr
<TLSCertKeyPair
>>(certFiles
);
150 pairs
.emplace_back(*cert
);
152 else if (certFiles
.type() == typeid(LuaArray
<std::shared_ptr
<TLSCertKeyPair
>>)) {
153 auto certs
= boost::get
<LuaArray
<std::shared_ptr
<TLSCertKeyPair
>>>(certFiles
);
155 for (const auto& cert
: certs
) {
156 pairs
.emplace_back(*(cert
.second
));
159 else if (certFiles
.type() == typeid(LuaArray
<std::string
>) && keyFiles
.type() == typeid(LuaArray
<std::string
>)) {
160 auto certFilesVect
= boost::get
<LuaArray
<std::string
>>(certFiles
);
161 auto keyFilesVect
= boost::get
<LuaArray
<std::string
>>(keyFiles
);
162 if (certFilesVect
.size() == keyFilesVect
.size()) {
164 for (size_t idx
= 0; idx
< certFilesVect
.size(); idx
++) {
165 pairs
.emplace_back(certFilesVect
.at(idx
).second
, keyFilesVect
.at(idx
).second
);
169 errlog("Error, mismatching number of certificates and keys in call to %s()!", context
);
170 g_outputBuffer
= "Error, mismatching number of certificates and keys in call to " + context
+ "()!";
175 errlog("Error, mismatching number of certificates and keys in call to %s()!", context
);
176 g_outputBuffer
= "Error, mismatching number of certificates and keys in call to " + context
+ "()!";
183 static void parseTLSConfig(TLSConfig
& config
, const std::string
& context
, boost::optional
<localbind_t
> vars
)
185 if (vars
->count("ciphers")) {
186 config
.d_ciphers
= boost::get
<const string
>((*vars
)["ciphers"]);
189 if (vars
->count("ciphersTLS13")) {
190 config
.d_ciphers13
= boost::get
<const string
>((*vars
)["ciphersTLS13"]);
194 if (vars
->count("minTLSVersion")) {
195 config
.d_minTLSVersion
= libssl_tls_version_from_string(boost::get
<const string
>((*vars
)["minTLSVersion"]));
197 #endif /* HAVE_LIBSSL */
199 if (vars
->count("ticketKeyFile")) {
200 config
.d_ticketKeyFile
= boost::get
<const string
>((*vars
)["ticketKeyFile"]);
203 if (vars
->count("ticketsKeysRotationDelay")) {
204 config
.d_ticketsKeyRotationDelay
= boost::get
<int>((*vars
)["ticketsKeysRotationDelay"]);
207 if (vars
->count("numberOfTicketsKeys")) {
208 config
.d_numberOfTicketsKeys
= boost::get
<int>((*vars
)["numberOfTicketsKeys"]);
211 if (vars
->count("preferServerCiphers")) {
212 config
.d_preferServerCiphers
= boost::get
<bool>((*vars
)["preferServerCiphers"]);
215 if (vars
->count("sessionTimeout")) {
216 config
.d_sessionTimeout
= boost::get
<int>((*vars
)["sessionTimeout"]);
219 if (vars
->count("sessionTickets")) {
220 config
.d_enableTickets
= boost::get
<bool>((*vars
)["sessionTickets"]);
223 if (vars
->count("numberOfStoredSessions")) {
224 auto value
= boost::get
<int>((*vars
)["numberOfStoredSessions"]);
226 errlog("Invalid value '%d' for %s() parameter 'numberOfStoredSessions', should be >= 0, dismissing", value
, context
);
227 g_outputBuffer
= "Invalid value '" + std::to_string(value
) + "' for " + context
+ "() parameter 'numberOfStoredSessions', should be >= 0, dismissing";
229 config
.d_maxStoredSessions
= value
;
232 if (vars
->count("ocspResponses")) {
233 auto files
= boost::get
<LuaArray
<std::string
>>((*vars
)["ocspResponses"]);
234 for (const auto& file
: files
) {
235 config
.d_ocspFiles
.push_back(file
.second
);
239 if (vars
->count("keyLogFile")) {
240 #ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
241 config
.d_keyLogFile
= boost::get
<const string
>((*vars
)["keyLogFile"]);
243 errlog("TLS Key logging has been enabled using the 'keyLogFile' parameter to %s(), but this version of OpenSSL does not support it", context
);
244 g_outputBuffer
= "TLS Key logging has been enabled using the 'keyLogFile' parameter to " + context
+ "(), but this version of OpenSSL does not support it";
248 if (vars
->count("releaseBuffers")) {
249 config
.d_releaseBuffers
= boost::get
<bool>((*vars
)["releaseBuffers"]);
252 if (vars
->count("enableRenegotiation")) {
253 config
.d_enableRenegotiation
= boost::get
<bool>((*vars
)["enableRenegotiation"]);
256 if (vars
->count("tlsAsyncMode")) {
257 config
.d_asyncMode
= boost::get
<bool>((*vars
).at("tlsAsyncMode"));
261 #endif // defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
263 void checkParameterBound(const std::string
& parameter
, uint64_t value
, size_t max
)
266 throw std::runtime_error("The value (" + std::to_string(value
) + ") passed to " + parameter
+ " is too large, the maximum is " + std::to_string(max
));
270 static void LuaThread(const std::string code
)
274 // mask SIGTERM on threads so the signal always comes to dnsdist itself
275 sigset_t blockSignals
;
277 sigemptyset(&blockSignals
);
278 sigaddset(&blockSignals
, SIGTERM
);
280 pthread_sigmask(SIG_BLOCK
, &blockSignals
, nullptr);
282 // submitToMainThread is camelcased, threadmessage is not.
283 // This follows our tradition of hooks we call being lowercased but functions the user can call being camelcased.
284 l
.writeFunction("submitToMainThread", [](std::string cmd
, LuaAssociativeTable
<std::string
> data
) {
285 auto lua
= g_lua
.lock();
286 // maybe offer more than `void`
287 auto func
= lua
->readVariable
<boost::optional
<std::function
<void(std::string cmd
, LuaAssociativeTable
<std::string
> data
)>>>("threadmessage");
289 func
.get()(cmd
, data
);
292 errlog("Lua thread called submitToMainThread but no threadmessage receiver is defined");
296 // function threadmessage(cmd, data) print("got thread data:", cmd) for k,v in pairs(data) do print(k,v) end end
301 errlog("Lua thread exited, restarting in 5 seconds");
303 catch (const std::exception
& e
) {
304 errlog("Lua thread crashed, restarting in 5 seconds: %s", e
.what());
307 errlog("Lua thread crashed, restarting in 5 seconds");
313 static void setupLuaConfig(LuaContext
& luaCtx
, bool client
, bool configCheck
)
315 typedef LuaAssociativeTable
<boost::variant
<bool, std::string
, LuaArray
<std::string
>, DownstreamState::checkfunc_t
>> newserver_t
;
316 luaCtx
.writeFunction("inClientStartup", [client
]() {
317 return client
&& !g_configurationDone
;
320 luaCtx
.writeFunction("inConfigCheck", [configCheck
]() {
324 luaCtx
.writeFunction("newServer",
325 [client
, configCheck
](boost::variant
<string
, newserver_t
> pvars
, boost::optional
<int> qps
) {
329 DownstreamState::Config config
;
331 std::string serverAddressStr
;
332 if (auto addrStr
= boost::get
<string
>(&pvars
)) {
333 serverAddressStr
= *addrStr
;
335 vars
["qps"] = std::to_string(*qps
);
339 vars
= boost::get
<newserver_t
>(pvars
);
340 serverAddressStr
= boost::get
<string
>(vars
["address"]);
343 if (vars
.count("source")) {
344 /* handle source in the following forms:
345 - v4 address ("192.0.2.1")
346 - v6 address ("2001:DB8::1")
347 - interface name ("eth0")
348 - v4 address and interface name ("192.0.2.1@eth0")
349 - v6 address and interface name ("2001:DB8::1@eth0")
351 const string source
= boost::get
<string
>(vars
["source"]);
353 std::string::size_type pos
= source
.find("@");
354 if (pos
== std::string::npos
) {
355 /* no '@', try to parse that as a valid v4/v6 address */
357 config
.sourceAddr
= ComboAddress(source
);
364 if (parsed
== false) {
365 /* try to parse as interface name, or v4/v6@itf */
366 config
.sourceItfName
= source
.substr(pos
== std::string::npos
? 0 : pos
+ 1);
367 unsigned int itfIdx
= if_nametoindex(config
.sourceItfName
.c_str());
370 if (pos
== 0 || pos
== std::string::npos
) {
371 /* "eth0" or "@eth0" */
372 config
.sourceItf
= itfIdx
;
375 /* "192.0.2.1@eth0" */
376 config
.sourceAddr
= ComboAddress(source
.substr(0, pos
));
377 config
.sourceItf
= itfIdx
;
379 #ifdef SO_BINDTODEVICE
380 /* we need to retain CAP_NET_RAW to be able to set SO_BINDTODEVICE in the health checks */
381 g_capabilitiesToRetain
.insert("CAP_NET_RAW");
385 warnlog("Dismissing source %s because '%s' is not a valid interface name", source
, config
.sourceItfName
);
390 if (vars
.count("sockets")) {
391 config
.d_numberOfSockets
= std::stoul(boost::get
<string
>(vars
["sockets"]));
392 if (config
.d_numberOfSockets
== 0) {
393 warnlog("Dismissing invalid number of sockets '%s', using 1 instead", boost::get
<string
>(vars
["sockets"]));
394 config
.d_numberOfSockets
= 1;
398 if (vars
.count("qps")) {
399 config
.d_qpsLimit
= std::stoi(boost::get
<string
>(vars
["qps"]));
402 if (vars
.count("order")) {
403 config
.order
= std::stoi(boost::get
<string
>(vars
["order"]));
406 if (vars
.count("weight")) {
408 config
.d_weight
= std::stoi(boost::get
<string
>(vars
["weight"]));
410 if (config
.d_weight
< 1) {
411 errlog("Error creating new server: downstream weight value must be greater than 0.");
412 return std::shared_ptr
<DownstreamState
>();
415 catch (const std::exception
& e
) {
416 // std::stoi will throw an exception if the string isn't in a value int range
417 errlog("Error creating new server: downstream weight value must be between %s and %s", 1, std::numeric_limits
<int>::max());
418 return std::shared_ptr
<DownstreamState
>();
422 if (vars
.count("retries")) {
423 config
.d_retries
= std::stoi(boost::get
<string
>(vars
["retries"]));
426 if (vars
.count("checkInterval")) {
427 config
.checkInterval
= static_cast<unsigned int>(std::stoul(boost::get
<string
>(vars
["checkInterval"])));
430 if (vars
.count("tcpConnectTimeout")) {
431 config
.tcpConnectTimeout
= std::stoi(boost::get
<string
>(vars
["tcpConnectTimeout"]));
434 if (vars
.count("tcpSendTimeout")) {
435 config
.tcpSendTimeout
= std::stoi(boost::get
<string
>(vars
["tcpSendTimeout"]));
438 if (vars
.count("tcpRecvTimeout")) {
439 config
.tcpRecvTimeout
= std::stoi(boost::get
<string
>(vars
["tcpRecvTimeout"]));
442 if (vars
.count("tcpFastOpen")) {
443 bool fastOpen
= boost::get
<bool>(vars
["tcpFastOpen"]);
446 config
.tcpFastOpen
= true;
448 warnlog("TCP Fast Open has been configured on downstream server %s but is not supported", boost::get
<string
>(vars
["address"]));
453 if (vars
.count("maxInFlight")) {
454 config
.d_maxInFlightQueriesPerConn
= std::stoi(boost::get
<string
>(vars
["maxInFlight"]));
457 if (vars
.count("name")) {
458 config
.name
= boost::get
<string
>(vars
["name"]);
461 if (vars
.count("id")) {
462 config
.id
= boost::uuids::string_generator()(boost::get
<string
>(vars
["id"]));
465 if (vars
.count("checkName")) {
466 config
.checkName
= DNSName(boost::get
<string
>(vars
["checkName"]));
469 if (vars
.count("checkType")) {
470 config
.checkType
= boost::get
<string
>(vars
["checkType"]);
473 if (vars
.count("checkClass")) {
474 config
.checkClass
= std::stoi(boost::get
<string
>(vars
["checkClass"]));
477 if (vars
.count("checkFunction")) {
478 config
.checkFunction
= boost::get
<DownstreamState::checkfunc_t
>(vars
["checkFunction"]);
481 if (vars
.count("checkTimeout")) {
482 config
.checkTimeout
= std::stoi(boost::get
<string
>(vars
["checkTimeout"]));
485 if (vars
.count("checkTCP")) {
486 config
.d_tcpCheck
= boost::get
<bool>(vars
.at("checkTCP"));
489 if (vars
.count("setCD")) {
490 config
.setCD
= boost::get
<bool>(vars
["setCD"]);
493 if (vars
.count("mustResolve")) {
494 config
.mustResolve
= boost::get
<bool>(vars
["mustResolve"]);
497 if (vars
.count("useClientSubnet")) {
498 config
.useECS
= boost::get
<bool>(vars
["useClientSubnet"]);
501 if (vars
.count("useProxyProtocol")) {
502 config
.useProxyProtocol
= boost::get
<bool>(vars
["useProxyProtocol"]);
505 if (vars
.count("disableZeroScope")) {
506 config
.disableZeroScope
= boost::get
<bool>(vars
["disableZeroScope"]);
509 if (vars
.count("ipBindAddrNoPort")) {
510 config
.ipBindAddrNoPort
= boost::get
<bool>(vars
["ipBindAddrNoPort"]);
513 if (vars
.count("addXPF")) {
514 config
.xpfRRCode
= std::stoi(boost::get
<string
>(vars
["addXPF"]));
517 if (vars
.count("maxCheckFailures")) {
518 config
.maxCheckFailures
= std::stoi(boost::get
<string
>(vars
["maxCheckFailures"]));
521 if (vars
.count("rise")) {
522 config
.minRiseSuccesses
= std::stoi(boost::get
<string
>(vars
["rise"]));
525 if (vars
.count("reconnectOnUp")) {
526 config
.reconnectOnUp
= boost::get
<bool>(vars
["reconnectOnUp"]);
529 if (vars
.count("cpus")) {
530 for (const auto& cpu
: boost::get
<LuaArray
<std::string
>>(vars
["cpus"])) {
531 config
.d_cpus
.insert(std::stoi(cpu
.second
));
535 if (vars
.count("tcpOnly")) {
536 config
.d_tcpOnly
= boost::get
<bool>(vars
.at("tcpOnly"));
539 std::shared_ptr
<TLSCtx
> tlsCtx
;
540 if (vars
.count("ciphers")) {
541 config
.d_tlsParams
.d_ciphers
= boost::get
<string
>(vars
.at("ciphers"));
543 if (vars
.count("ciphers13")) {
544 config
.d_tlsParams
.d_ciphers13
= boost::get
<string
>(vars
.at("ciphers13"));
546 if (vars
.count("caStore")) {
547 config
.d_tlsParams
.d_caStore
= boost::get
<string
>(vars
.at("caStore"));
549 if (vars
.count("validateCertificates")) {
550 config
.d_tlsParams
.d_validateCertificates
= boost::get
<bool>(vars
.at("validateCertificates"));
552 if (vars
.count("releaseBuffers")) {
553 config
.d_tlsParams
.d_releaseBuffers
= boost::get
<bool>(vars
.at("releaseBuffers"));
555 if (vars
.count("enableRenegotiation")) {
556 config
.d_tlsParams
.d_enableRenegotiation
= boost::get
<bool>(vars
.at("enableRenegotiation"));
558 if (vars
.count("subjectName")) {
559 config
.d_tlsSubjectName
= boost::get
<string
>(vars
.at("subjectName"));
561 else if (vars
.count("subjectAddr")) {
563 ComboAddress
ca(boost::get
<string
>(vars
.at("subjectAddr")));
564 config
.d_tlsSubjectName
= ca
.toString();
565 config
.d_tlsSubjectIsAddr
= true;
567 catch (const std::exception
& e
) {
568 errlog("Error creating new server: downstream subjectAddr value must be a valid IP address");
569 return std::shared_ptr
<DownstreamState
>();
573 uint16_t serverPort
= 53;
575 if (vars
.count("tls")) {
577 config
.d_tlsParams
.d_provider
= boost::get
<string
>(vars
.at("tls"));
578 tlsCtx
= getTLSContext(config
.d_tlsParams
);
580 if (vars
.count("dohPath")) {
582 throw std::runtime_error("Outgoing DNS over HTTPS support requested (via 'dohPath' on newServer()) but nghttp2 support is not available");
586 config
.d_dohPath
= boost::get
<string
>(vars
.at("dohPath"));
588 if (vars
.count("addXForwardedHeaders")) {
589 config
.d_addXForwardedHeaders
= boost::get
<bool>(vars
.at("addXForwardedHeaders"));
595 config
.remote
= ComboAddress(serverAddressStr
, serverPort
);
597 catch (const PDNSException
& e
) {
598 g_outputBuffer
= "Error creating new server: " + string(e
.reason
);
599 errlog("Error creating new server with address %s: %s", serverAddressStr
, e
.reason
);
600 return std::shared_ptr
<DownstreamState
>();
602 catch (const std::exception
& e
) {
603 g_outputBuffer
= "Error creating new server: " + string(e
.what());
604 errlog("Error creating new server with address %s: %s", serverAddressStr
, e
.what());
605 return std::shared_ptr
<DownstreamState
>();
608 if (IsAnyAddress(config
.remote
)) {
609 g_outputBuffer
= "Error creating new server: invalid address for a downstream server.";
610 errlog("Error creating new server: %s is not a valid address for a downstream server", serverAddressStr
);
611 return std::shared_ptr
<DownstreamState
>();
614 if (vars
.count("pool")) {
615 if (auto* pool
= boost::get
<string
>(&vars
["pool"])) {
616 config
.pools
.insert(*pool
);
619 auto pools
= boost::get
<LuaArray
<std::string
>>(vars
["pool"]);
620 for (auto& p
: pools
) {
621 config
.pools
.insert(p
.second
);
626 bool autoUpgrade
= false;
627 bool keepAfterUpgrade
= false;
628 uint32_t upgradeInterval
= 3600;
629 uint16_t upgradeDoHKey
= dnsdist::ServiceDiscovery::s_defaultDoHSVCKey
;
630 std::string upgradePool
;
632 if (vars
.count("autoUpgrade") && boost::get
<bool>(vars
.at("autoUpgrade"))) {
635 if (vars
.count("autoUpgradeInterval")) {
637 upgradeInterval
= static_cast<uint32_t>(std::stoul(boost::get
<string
>(vars
.at("autoUpgradeInterval"))));
639 catch (const std::exception
& e
) {
640 warnlog("Error parsing 'autoUpgradeInterval' value: %s", e
.what());
643 if (vars
.count("autoUpgradeKeep")) {
644 keepAfterUpgrade
= boost::get
<bool>(vars
.at("autoUpgradeKeep"));
646 if (vars
.count("autoUpgradePool")) {
647 upgradePool
= boost::get
<string
>(vars
.at("autoUpgradePool"));
649 if (vars
.count("autoUpgradeDoHKey")) {
651 upgradeDoHKey
= static_cast<uint16_t>(std::stoul(boost::get
<string
>(vars
.at("autoUpgradeDoHKey"))));
653 catch (const std::exception
& e
) {
654 warnlog("Error parsing 'autoUpgradeDoHKey' value: %s", e
.what());
659 // create but don't connect the socket in client or check-config modes
660 auto ret
= std::make_shared
<DownstreamState
>(std::move(config
), std::move(tlsCtx
), !(client
|| configCheck
));
661 if (!(client
|| configCheck
)) {
662 infolog("Added downstream server %s", ret
->d_config
.remote
.toStringWithPort());
665 if (autoUpgrade
&& ret
->getProtocol() != dnsdist::Protocol::DoT
&& ret
->getProtocol() != dnsdist::Protocol::DoH
) {
666 dnsdist::ServiceDiscovery::addUpgradeableServer(ret
, upgradeInterval
, upgradePool
, upgradeDoHKey
, keepAfterUpgrade
);
669 /* this needs to be done _AFTER_ the order has been set,
670 since the server are kept ordered inside the pool */
671 auto localPools
= g_pools
.getCopy();
672 if (!ret
->d_config
.pools
.empty()) {
673 for (const auto& poolName
: ret
->d_config
.pools
) {
674 addServerToPool(localPools
, poolName
, ret
);
678 addServerToPool(localPools
, "", ret
);
680 g_pools
.setState(localPools
);
682 if (ret
->connected
) {
684 g_launchWork
->push_back([ret
]() {
693 auto states
= g_dstates
.getCopy();
694 states
.push_back(ret
);
695 std::stable_sort(states
.begin(), states
.end(), [](const decltype(ret
)& a
, const decltype(ret
)& b
) {
696 return a
->d_config
.order
< b
->d_config
.order
;
698 g_dstates
.setState(states
);
702 luaCtx
.writeFunction("rmServer",
703 [](boost::variant
<std::shared_ptr
<DownstreamState
>, int, std::string
> var
) {
705 shared_ptr
<DownstreamState
> server
= nullptr;
706 auto states
= g_dstates
.getCopy();
707 if (auto* rem
= boost::get
<shared_ptr
<DownstreamState
>>(&var
)) {
710 else if (auto str
= boost::get
<std::string
>(&var
)) {
711 const auto uuid
= getUniqueID(*str
);
712 for (auto& state
: states
) {
713 if (*state
->d_config
.id
== uuid
) {
719 int idx
= boost::get
<int>(var
);
720 server
= states
.at(idx
);
723 throw std::runtime_error("unable to locate the requested server");
725 auto localPools
= g_pools
.getCopy();
726 for (const string
& poolName
: server
->d_config
.pools
) {
727 removeServerFromPool(localPools
, poolName
, server
);
729 /* the server might also be in the default pool */
730 removeServerFromPool(localPools
, "", server
);
731 g_pools
.setState(localPools
);
732 states
.erase(remove(states
.begin(), states
.end(), server
), states
.end());
733 g_dstates
.setState(states
);
737 luaCtx
.writeFunction("truncateTC", [](bool tc
) { setLuaSideEffect(); g_truncateTC
=tc
; });
738 luaCtx
.writeFunction("fixupCase", [](bool fu
) { setLuaSideEffect(); g_fixupCase
=fu
; });
740 luaCtx
.writeFunction("addACL", [](const std::string
& domain
) {
742 g_ACL
.modify([domain
](NetmaskGroup
& nmg
) { nmg
.addMask(domain
); });
745 luaCtx
.writeFunction("rmACL", [](const std::string
& netmask
) {
747 g_ACL
.modify([netmask
](NetmaskGroup
& nmg
) { nmg
.deleteMask(netmask
); });
750 luaCtx
.writeFunction("setLocal", [client
](const std::string
& addr
, boost::optional
<localbind_t
> vars
) {
754 if (g_configurationDone
) {
755 g_outputBuffer
= "setLocal cannot be used at runtime!\n";
758 bool reusePort
= false;
759 int tcpFastOpenQueueSize
= 0;
760 int tcpListenQueueSize
= 0;
761 uint64_t maxInFlightQueriesPerConn
= 0;
762 uint64_t tcpMaxConcurrentConnections
= 0;
763 std::string interface
;
766 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConnections
);
769 ComboAddress
loc(addr
, 53);
770 for (auto it
= g_frontends
.begin(); it
!= g_frontends
.end();) {
771 /* DoH, DoT and DNSCrypt frontends are separate */
772 if ((*it
)->tlsFrontend
== nullptr && (*it
)->dnscryptCtx
== nullptr && (*it
)->dohFrontend
== nullptr) {
773 it
= g_frontends
.erase(it
);
780 // only works pre-startup, so no sync necessary
781 g_frontends
.push_back(std::make_unique
<ClientState
>(loc
, false, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
));
782 auto tcpCS
= std::make_unique
<ClientState
>(loc
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
783 if (tcpListenQueueSize
> 0) {
784 tcpCS
->tcpListenQueueSize
= tcpListenQueueSize
;
786 if (maxInFlightQueriesPerConn
> 0) {
787 tcpCS
->d_maxInFlightQueriesPerConn
= maxInFlightQueriesPerConn
;
789 if (tcpMaxConcurrentConnections
> 0) {
790 tcpCS
->d_tcpConcurrentConnectionsLimit
= tcpMaxConcurrentConnections
;
793 g_frontends
.push_back(std::move(tcpCS
));
795 catch (const std::exception
& e
) {
796 g_outputBuffer
= "Error: " + string(e
.what()) + "\n";
800 luaCtx
.writeFunction("addLocal", [client
](const std::string
& addr
, boost::optional
<localbind_t
> vars
) {
804 if (g_configurationDone
) {
805 g_outputBuffer
= "addLocal cannot be used at runtime!\n";
808 bool reusePort
= false;
809 int tcpFastOpenQueueSize
= 0;
810 int tcpListenQueueSize
= 0;
811 uint64_t maxInFlightQueriesPerConn
= 0;
812 uint64_t tcpMaxConcurrentConnections
= 0;
813 std::string interface
;
816 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConnections
);
819 ComboAddress
loc(addr
, 53);
820 // only works pre-startup, so no sync necessary
821 g_frontends
.push_back(std::make_unique
<ClientState
>(loc
, false, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
));
822 auto tcpCS
= std::make_unique
<ClientState
>(loc
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
823 if (tcpListenQueueSize
> 0) {
824 tcpCS
->tcpListenQueueSize
= tcpListenQueueSize
;
826 if (maxInFlightQueriesPerConn
> 0) {
827 tcpCS
->d_maxInFlightQueriesPerConn
= maxInFlightQueriesPerConn
;
829 if (tcpMaxConcurrentConnections
> 0) {
830 tcpCS
->d_tcpConcurrentConnectionsLimit
= tcpMaxConcurrentConnections
;
832 g_frontends
.push_back(std::move(tcpCS
));
834 catch (std::exception
& e
) {
835 g_outputBuffer
= "Error: " + string(e
.what()) + "\n";
836 errlog("Error while trying to listen on %s: %s\n", addr
, string(e
.what()));
840 luaCtx
.writeFunction("setACL", [](LuaTypeOrArrayOf
<std::string
> inp
) {
843 if (auto str
= boost::get
<string
>(&inp
)) {
847 for (const auto& p
: boost::get
<LuaArray
<std::string
>>(inp
)) {
848 nmg
.addMask(p
.second
);
853 luaCtx
.writeFunction("setACLFromFile", [](const std::string
& file
) {
859 throw std::runtime_error("Could not open '" + file
+ "': " + stringerror());
862 string::size_type pos
;
864 while (getline(ifs
, line
)) {
865 pos
= line
.find('#');
866 if (pos
!= string::npos
)
878 luaCtx
.writeFunction("showACL", []() {
879 setLuaNoSideEffect();
882 g_ACL
.getLocal()->toStringVector(&vec
);
884 for (const auto& s
: vec
)
885 g_outputBuffer
+= s
+ "\n";
888 luaCtx
.writeFunction("shutdown", []() {
890 sd_notify(0, "STOPPING=1");
891 #endif /* HAVE_SYSTEMD */
893 // Useful for debugging leaks, but might lead to race under load
894 // since other threads are still running.
895 for(auto& frontend
: g_tlslocals
) {
904 typedef LuaAssociativeTable
<boost::variant
<bool, std::string
>> showserversopts_t
;
906 luaCtx
.writeFunction("showServers", [](boost::optional
<showserversopts_t
> vars
) {
907 setLuaNoSideEffect();
908 bool showUUIDs
= false;
910 if (vars
->count("showUUIDs")) {
911 showUUIDs
= boost::get
<bool>((*vars
)["showUUIDs"]);
918 fmt
= boost::format("%1$-3d %15$-36s %2$-20.20s %|62t|%3% %|92t|%4$5s %|88t|%5$7.1f %|103t|%6$7d %|106t|%7$3d %|115t|%8$2d %|117t|%9$10d %|123t|%10$7d %|128t|%11$5.1f %|146t|%12$5.1f %|152t|%13$11d %14%");
919 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
920 ret
<< (fmt
% "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools" % "UUID") << endl
;
923 fmt
= boost::format("%1$-3d %2$-20.20s %|25t|%3% %|55t|%4$5s %|51t|%5$7.1f %|66t|%6$7d %|69t|%7$3d %|78t|%8$2d %|80t|%9$10d %|86t|%10$7d %|91t|%11$5.1f %|109t|%12$5.1f %|115t|%13$11d %14%");
924 ret
<< (fmt
% "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools") << endl
;
927 uint64_t totQPS
{0}, totQueries
{0}, totDrops
{0};
929 auto states
= g_dstates
.getLocal();
930 for (const auto& s
: *states
) {
931 string status
= s
->getStatus();
933 for (const auto& p
: s
->d_config
.pools
) {
934 if (!pools
.empty()) {
940 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
) % (s
->latencyUsec
/ 1000.0) % s
->outstanding
.load() % pools
% *s
->d_config
.id
) << endl
;
943 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
) % (s
->latencyUsec
/ 1000.0) % s
->outstanding
.load() % pools
) << endl
;
945 totQPS
+= s
->queryLoad
;
946 totQueries
+= s
->queries
.load();
947 totDrops
+= s
->reuseds
.load();
951 ret
<< (fmt
% "All" % "" % "" % ""
952 % (double)totQPS
% "" % "" % "" % totQueries
% totDrops
% "" % "" % "" % "" % "")
956 ret
<< (fmt
% "All" % "" % "" % ""
957 % (double)totQPS
% "" % "" % "" % totQueries
% totDrops
% "" % "" % "" % "")
961 g_outputBuffer
= ret
.str();
963 catch (std::exception
& e
) {
964 g_outputBuffer
= e
.what();
969 luaCtx
.writeFunction("getServers", []() {
970 setLuaNoSideEffect();
971 LuaArray
<std::shared_ptr
<DownstreamState
>> ret
;
973 for (const auto& s
: g_dstates
.getCopy()) {
974 ret
.push_back(make_pair(count
++, s
));
979 luaCtx
.writeFunction("getPoolServers", [](string pool
) {
980 const auto poolServers
= getDownstreamCandidates(g_pools
.getCopy(), pool
);
984 luaCtx
.writeFunction("getServer", [client
](boost::variant
<int, std::string
> i
) {
986 return std::make_shared
<DownstreamState
>(ComboAddress());
988 auto states
= g_dstates
.getCopy();
989 if (auto str
= boost::get
<std::string
>(&i
)) {
990 const auto uuid
= getUniqueID(*str
);
991 for (auto& state
: states
) {
992 if (*state
->d_config
.id
== uuid
) {
997 else if (auto pos
= boost::get
<int>(&i
)) {
998 return states
.at(*pos
);
1001 g_outputBuffer
= "Error: no rule matched\n";
1002 return std::shared_ptr
<DownstreamState
>(nullptr);
1005 #ifndef DISABLE_CARBON
1006 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
) {
1008 auto ours
= g_carbon
.getCopy();
1009 ours
.push_back({ComboAddress(address
, 2003),
1010 (namespace_name
&& !namespace_name
->empty()) ? *namespace_name
: "dnsdist",
1011 ourName
? *ourName
: "",
1012 (instance_name
&& !instance_name
->empty()) ? *instance_name
: "main",
1013 (interval
&& *interval
< std::numeric_limits
<unsigned int>::max()) ? static_cast<unsigned int>(*interval
) : 30});
1014 g_carbon
.setState(ours
);
1016 #endif /* DISABLE_CARBON */
1018 luaCtx
.writeFunction("webserver", [client
, configCheck
](const std::string
& address
) {
1022 local
= ComboAddress(address
);
1024 catch (const PDNSException
& e
) {
1025 throw std::runtime_error(std::string("Error parsing the bind address for the webserver: ") + e
.reason
);
1028 if (client
|| configCheck
) {
1033 int sock
= SSocket(local
.sin4
.sin_family
, SOCK_STREAM
, 0);
1034 SSetsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, 1);
1037 auto launch
= [sock
, local
]() {
1038 thread
t(dnsdistWebserverThread
, sock
, local
);
1042 g_launchWork
->push_back(launch
);
1048 catch (const std::exception
& e
) {
1049 g_outputBuffer
= "Unable to bind to webserver socket on " + local
.toStringWithPort() + ": " + e
.what();
1050 errlog("Unable to bind to webserver socket on %s: %s", local
.toStringWithPort(), e
.what());
1054 typedef LuaAssociativeTable
<boost::variant
<bool, std::string
, LuaAssociativeTable
<std::string
>>> webserveropts_t
;
1056 luaCtx
.writeFunction("setWebserverConfig", [](boost::optional
<webserveropts_t
> vars
) {
1063 bool hashPlaintextCredentials
= false;
1064 if (vars
->count("hashPlaintextCredentials")) {
1065 hashPlaintextCredentials
= boost::get
<bool>(vars
->at("hashPlaintextCredentials"));
1068 if (vars
->count("password")) {
1069 std::string password
= boost::get
<std::string
>(vars
->at("password"));
1070 auto holder
= make_unique
<CredentialsHolder
>(std::move(password
), hashPlaintextCredentials
);
1071 if (!holder
->wasHashed() && holder
->isHashingAvailable()) {
1072 infolog("Passing a plain-text password via the 'password' parameter to 'setWebserverConfig()' is not advised, please consider generating a hashed one using 'hashPassword()' instead.");
1075 setWebserverPassword(std::move(holder
));
1078 if (vars
->count("apiKey")) {
1079 std::string apiKey
= boost::get
<std::string
>(vars
->at("apiKey"));
1080 auto holder
= make_unique
<CredentialsHolder
>(std::move(apiKey
), hashPlaintextCredentials
);
1081 if (!holder
->wasHashed() && holder
->isHashingAvailable()) {
1082 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.");
1085 setWebserverAPIKey(std::move(holder
));
1088 if (vars
->count("acl")) {
1089 const std::string acl
= boost::get
<std::string
>(vars
->at("acl"));
1091 setWebserverACL(acl
);
1094 if (vars
->count("customHeaders")) {
1095 const auto headers
= boost::get
<std::unordered_map
<std::string
, std::string
>>(vars
->at("customHeaders"));
1097 setWebserverCustomHeaders(headers
);
1100 if (vars
->count("statsRequireAuthentication")) {
1101 setWebserverStatsRequireAuthentication(boost::get
<bool>(vars
->at("statsRequireAuthentication")));
1104 if (vars
->count("apiRequiresAuthentication")) {
1105 setWebserverAPIRequiresAuthentication(boost::get
<bool>(vars
->at("apiRequiresAuthentication")));
1108 if (vars
->count("maxConcurrentConnections")) {
1109 setWebserverMaxConcurrentConnections(std::stoi(boost::get
<std::string
>(vars
->at("maxConcurrentConnections"))));
1113 luaCtx
.writeFunction("showWebserverConfig", []() {
1114 setLuaNoSideEffect();
1115 return getWebserverConfig();
1118 luaCtx
.writeFunction("hashPassword", [](const std::string
& password
, boost::optional
<uint64_t> workFactor
) {
1120 return hashPassword(password
, *workFactor
, CredentialsHolder::s_defaultParallelFactor
, CredentialsHolder::s_defaultBlockSize
);
1122 return hashPassword(password
);
1125 luaCtx
.writeFunction("controlSocket", [client
, configCheck
](const std::string
& str
) {
1127 ComboAddress
local(str
, 5199);
1129 if (client
|| configCheck
) {
1130 g_serverControl
= local
;
1134 g_consoleEnabled
= true;
1135 #ifdef HAVE_LIBSODIUM
1136 if (g_configurationDone
&& g_consoleKey
.empty()) {
1137 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");
1142 int sock
= SSocket(local
.sin4
.sin_family
, SOCK_STREAM
, 0);
1143 SSetsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, 1);
1146 auto launch
= [sock
, local
]() {
1147 thread
t(controlThread
, sock
, local
);
1151 g_launchWork
->push_back(launch
);
1157 catch (std::exception
& e
) {
1158 g_outputBuffer
= "Unable to bind to control socket on " + local
.toStringWithPort() + ": " + e
.what();
1159 errlog("Unable to bind to control socket on %s: %s", local
.toStringWithPort(), e
.what());
1163 luaCtx
.writeFunction("addConsoleACL", [](const std::string
& netmask
) {
1165 #ifndef HAVE_LIBSODIUM
1166 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
1169 g_consoleACL
.modify([netmask
](NetmaskGroup
& nmg
) { nmg
.addMask(netmask
); });
1172 luaCtx
.writeFunction("setConsoleACL", [](LuaTypeOrArrayOf
<std::string
> inp
) {
1175 #ifndef HAVE_LIBSODIUM
1176 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
1180 if (auto str
= boost::get
<string
>(&inp
)) {
1184 for (const auto& p
: boost::get
<LuaArray
<std::string
>>(inp
)) {
1185 nmg
.addMask(p
.second
);
1187 g_consoleACL
.setState(nmg
);
1190 luaCtx
.writeFunction("showConsoleACL", []() {
1191 setLuaNoSideEffect();
1193 #ifndef HAVE_LIBSODIUM
1194 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
1198 g_consoleACL
.getLocal()->toStringVector(&vec
);
1200 for (const auto& s
: vec
) {
1201 g_outputBuffer
+= s
+ "\n";
1205 luaCtx
.writeFunction("setConsoleMaximumConcurrentConnections", [](uint64_t max
) {
1207 setConsoleMaximumConcurrentConnections(max
);
1210 luaCtx
.writeFunction("clearQueryCounters", []() {
1211 unsigned int size
{0};
1213 auto records
= g_qcount
.records
.write_lock();
1214 size
= records
->size();
1218 boost::format
fmt("%d records cleared from query counter buffer\n");
1219 g_outputBuffer
= (fmt
% size
).str();
1222 luaCtx
.writeFunction("getQueryCounters", [](boost::optional
<uint64_t> optMax
) {
1223 setLuaNoSideEffect();
1224 auto records
= g_qcount
.records
.read_lock();
1225 g_outputBuffer
= "query counting is currently: ";
1226 g_outputBuffer
+= g_qcount
.enabled
? "enabled" : "disabled";
1227 g_outputBuffer
+= (boost::format(" (%d records in buffer)\n") % records
->size()).str();
1229 boost::format
fmt("%-3d %s: %d request(s)\n");
1230 uint64_t max
= optMax
? *optMax
: 10U;
1232 for (auto it
= records
->begin(); it
!= records
->end() && index
<= max
; ++it
, ++index
) {
1233 g_outputBuffer
+= (fmt
% index
% it
->first
% it
->second
).str();
1237 luaCtx
.writeFunction("setQueryCount", [](bool enabled
) { g_qcount
.enabled
= enabled
; });
1239 luaCtx
.writeFunction("setQueryCountFilter", [](QueryCountFilter func
) {
1240 g_qcount
.filter
= func
;
1243 luaCtx
.writeFunction("makeKey", []() {
1244 setLuaNoSideEffect();
1245 g_outputBuffer
= "setKey(" + newKey() + ")\n";
1248 luaCtx
.writeFunction("setKey", [](const std::string
& key
) {
1249 if (!g_configurationDone
&& !g_consoleKey
.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
1250 return; // but later setKeys() trump the -k value again
1252 #ifndef HAVE_LIBSODIUM
1253 warnlog("Calling setKey() while libsodium support has not been enabled is not secure, and will result in cleartext communications");
1258 if (B64Decode(key
, newkey
) < 0) {
1259 g_outputBuffer
= string("Unable to decode ") + key
+ " as Base64";
1260 errlog("%s", g_outputBuffer
);
1263 g_consoleKey
= newkey
;
1266 luaCtx
.writeFunction("clearConsoleHistory", []() {
1267 clearConsoleHistory();
1270 luaCtx
.writeFunction("testCrypto", [](boost::optional
<string
> optTestMsg
) {
1271 setLuaNoSideEffect();
1272 #ifdef HAVE_LIBSODIUM
1277 testmsg
= *optTestMsg
;
1280 testmsg
= "testStringForCryptoTests";
1283 SodiumNonce sn
, sn2
;
1286 string encrypted
= sodEncryptSym(testmsg
, g_consoleKey
, sn
);
1287 string decrypted
= sodDecryptSym(encrypted
, g_consoleKey
, sn2
);
1292 encrypted
= sodEncryptSym(testmsg
, g_consoleKey
, sn
);
1293 decrypted
= sodDecryptSym(encrypted
, g_consoleKey
, sn2
);
1295 if (testmsg
== decrypted
)
1296 g_outputBuffer
= "Everything is ok!\n";
1298 g_outputBuffer
= "Crypto failed.. (the decoded value does not match the cleartext one)\n";
1300 catch (const std::exception
& e
) {
1301 g_outputBuffer
= "Crypto failed: " + std::string(e
.what()) + "\n";
1304 g_outputBuffer
= "Crypto failed..\n";
1307 g_outputBuffer
= "Crypto not available.\n";
1311 luaCtx
.writeFunction("setTCPRecvTimeout", [](int timeout
) { g_tcpRecvTimeout
= timeout
; });
1313 luaCtx
.writeFunction("setTCPSendTimeout", [](int timeout
) { g_tcpSendTimeout
= timeout
; });
1315 luaCtx
.writeFunction("setUDPTimeout", [](int timeout
) { DownstreamState::s_udpTimeout
= timeout
; });
1317 luaCtx
.writeFunction("setMaxUDPOutstanding", [](uint64_t max
) {
1318 if (!g_configurationDone
) {
1319 checkParameterBound("setMaxUDPOutstanding", max
);
1320 g_maxOutstanding
= max
;
1323 g_outputBuffer
= "Max UDP outstanding cannot be altered at runtime!\n";
1327 luaCtx
.writeFunction("setMaxTCPClientThreads", [](uint64_t max
) {
1328 if (!g_configurationDone
) {
1329 g_maxTCPClientThreads
= max
;
1332 g_outputBuffer
= "Maximum TCP client threads count cannot be altered at runtime!\n";
1336 luaCtx
.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max
) {
1337 if (!g_configurationDone
) {
1338 g_maxTCPQueuedConnections
= max
;
1341 g_outputBuffer
= "The maximum number of queued TCP connections cannot be altered at runtime!\n";
1345 luaCtx
.writeFunction("setMaxTCPQueriesPerConnection", [](uint64_t max
) {
1346 if (!g_configurationDone
) {
1347 g_maxTCPQueriesPerConn
= max
;
1350 g_outputBuffer
= "The maximum number of queries per TCP connection cannot be altered at runtime!\n";
1354 luaCtx
.writeFunction("setMaxTCPConnectionsPerClient", [](uint64_t max
) {
1355 if (!g_configurationDone
) {
1356 g_maxTCPConnectionsPerClient
= max
;
1359 g_outputBuffer
= "The maximum number of TCP connection per client cannot be altered at runtime!\n";
1363 luaCtx
.writeFunction("setMaxTCPConnectionDuration", [](uint64_t max
) {
1364 if (!g_configurationDone
) {
1365 g_maxTCPConnectionDuration
= max
;
1368 g_outputBuffer
= "The maximum duration of a TCP connection cannot be altered at runtime!\n";
1372 luaCtx
.writeFunction("setMaxCachedTCPConnectionsPerDownstream", [](uint64_t max
) {
1373 setTCPDownstreamMaxIdleConnectionsPerBackend(max
);
1376 luaCtx
.writeFunction("setMaxIdleDoHConnectionsPerDownstream", [](uint64_t max
) {
1377 setDoHDownstreamMaxIdleConnectionsPerBackend(max
);
1380 luaCtx
.writeFunction("setOutgoingDoHWorkerThreads", [](uint64_t workers
) {
1381 if (!g_configurationDone
) {
1382 g_outgoingDoHWorkerThreads
= workers
;
1385 g_outputBuffer
= "The amount of outgoing DoH worker threads cannot be altered at runtime!\n";
1389 luaCtx
.writeFunction("setOutgoingTLSSessionsCacheMaxTicketsPerBackend", [](uint64_t max
) {
1390 if (g_configurationDone
) {
1391 g_outputBuffer
= "setOutgoingTLSSessionsCacheMaxTicketsPerBackend() cannot be called at runtime!\n";
1394 TLSSessionCache::setMaxTicketsPerBackend(max
);
1397 luaCtx
.writeFunction("setOutgoingTLSSessionsCacheCleanupDelay", [](time_t delay
) {
1398 if (g_configurationDone
) {
1399 g_outputBuffer
= "setOutgoingTLSSessionsCacheCleanupDelay() cannot be called at runtime!\n";
1402 TLSSessionCache::setCleanupDelay(delay
);
1405 luaCtx
.writeFunction("setOutgoingTLSSessionsCacheMaxTicketValidity", [](time_t validity
) {
1406 if (g_configurationDone
) {
1407 g_outputBuffer
= "setOutgoingTLSSessionsCacheMaxTicketValidity() cannot be called at runtime!\n";
1410 TLSSessionCache::setSessionValidity(validity
);
1413 luaCtx
.writeFunction("getOutgoingTLSSessionCacheSize", []() {
1414 setLuaNoSideEffect();
1415 return g_sessionCache
.getSize();
1418 luaCtx
.writeFunction("setCacheCleaningDelay", [](uint64_t delay
) {
1419 checkParameterBound("setCacheCleaningDelay", delay
, std::numeric_limits
<uint32_t>::max());
1420 g_cacheCleaningDelay
= delay
;
1423 luaCtx
.writeFunction("setCacheCleaningPercentage", [](uint64_t percentage
) { if (percentage
< 100) g_cacheCleaningPercentage
= percentage
; else g_cacheCleaningPercentage
= 100; });
1425 luaCtx
.writeFunction("setECSSourcePrefixV4", [](uint64_t prefix
) {
1426 checkParameterBound("setECSSourcePrefixV4", prefix
, std::numeric_limits
<uint16_t>::max());
1427 g_ECSSourcePrefixV4
= prefix
;
1430 luaCtx
.writeFunction("setECSSourcePrefixV6", [](uint64_t prefix
) {
1431 checkParameterBound("setECSSourcePrefixV6", prefix
, std::numeric_limits
<uint16_t>::max());
1432 g_ECSSourcePrefixV6
= prefix
;
1435 luaCtx
.writeFunction("setECSOverride", [](bool override
) { g_ECSOverride
= override
; });
1437 luaCtx
.writeFunction("showDynBlocks", []() {
1438 setLuaNoSideEffect();
1439 auto slow
= g_dynblockNMG
.getCopy();
1440 struct timespec now
;
1442 boost::format
fmt("%-24s %8d %8d %-10s %-20s %s\n");
1443 g_outputBuffer
= (fmt
% "What" % "Seconds" % "Blocks" % "Warning" % "Action" % "Reason").str();
1444 for (const auto& e
: slow
) {
1445 if (now
< e
.second
.until
) {
1446 uint64_t counter
= e
.second
.blocks
;
1447 if (g_defaultBPFFilter
&& e
.second
.bpf
) {
1448 counter
+= g_defaultBPFFilter
->getHits(e
.first
.getNetwork());
1450 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
) % e
.second
.reason
).str();
1453 auto slow2
= g_dynblockSMT
.getCopy();
1454 slow2
.visit([&now
, &fmt
](const SuffixMatchTree
<DynBlock
>& node
) {
1455 if (now
< node
.d_value
.until
) {
1456 string
dom("empty");
1457 if (!node
.d_value
.domain
.empty())
1458 dom
= node
.d_value
.domain
.toString();
1459 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();
1464 luaCtx
.writeFunction("clearDynBlocks", []() {
1467 g_dynblockNMG
.setState(nmg
);
1468 SuffixMatchTree
<DynBlock
> smt
;
1469 g_dynblockSMT
.setState(smt
);
1472 #ifndef DISABLE_DEPRECATED_DYNBLOCK
1473 luaCtx
.writeFunction("addDynBlocks",
1474 [](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
) {
1479 auto slow
= g_dynblockNMG
.getCopy();
1480 struct timespec until
, now
;
1483 int actualSeconds
= seconds
? *seconds
: 10;
1484 until
.tv_sec
+= actualSeconds
;
1485 for (const auto& capair
: m
) {
1486 unsigned int count
= 0;
1487 /* this legacy interface does not support ranges or ports, use DynBlockRulesGroup instead */
1488 AddressAndPortRange
requestor(capair
.first
, capair
.first
.isIPv4() ? 32 : 128, 0);
1489 auto got
= slow
.lookup(requestor
);
1490 bool expired
= false;
1492 if (until
< got
->second
.until
) {
1493 // had a longer policy
1496 if (now
< got
->second
.until
) {
1497 // only inherit count on fresh query we are extending
1498 count
= got
->second
.blocks
;
1504 DynBlock db
{msg
, until
, DNSName(), (action
? *action
: DNSAction::Action::None
)};
1506 if (!got
|| expired
) {
1507 warnlog("Inserting dynamic block for %s for %d seconds: %s", capair
.first
.toString(), actualSeconds
, msg
);
1509 slow
.insert(requestor
).second
= db
;
1511 g_dynblockNMG
.setState(slow
);
1514 luaCtx
.writeFunction("addDynBlockSMT",
1515 [](const LuaArray
<std::string
>& names
, const std::string
& msg
, boost::optional
<int> seconds
, boost::optional
<DNSAction::Action
> action
) {
1516 if (names
.empty()) {
1520 auto slow
= g_dynblockSMT
.getCopy();
1521 struct timespec until
, now
;
1524 int actualSeconds
= seconds
? *seconds
: 10;
1525 until
.tv_sec
+= actualSeconds
;
1527 for (const auto& capair
: names
) {
1528 unsigned int count
= 0;
1529 DNSName
domain(capair
.second
);
1530 domain
.makeUsLowerCase();
1531 auto got
= slow
.lookup(domain
);
1532 bool expired
= false;
1534 if (until
< got
->until
) // had a longer policy
1536 if (now
< got
->until
) // only inherit count on fresh query we are extending
1537 count
= got
->blocks
;
1542 DynBlock db
{msg
, until
, domain
, (action
? *action
: DNSAction::Action::None
)};
1544 if (!got
|| expired
)
1545 warnlog("Inserting dynamic block for %s for %d seconds: %s", domain
, actualSeconds
, msg
);
1546 slow
.add(domain
, std::move(db
));
1548 g_dynblockSMT
.setState(slow
);
1551 luaCtx
.writeFunction("setDynBlocksAction", [](DNSAction::Action action
) {
1552 if (!g_configurationDone
) {
1553 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
) {
1554 g_dynBlockAction
= action
;
1557 errlog("Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!");
1558 g_outputBuffer
= "Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!\n";
1562 g_outputBuffer
= "Dynamic blocks action cannot be altered at runtime!\n";
1565 #endif /* DISABLE_DEPRECATED_DYNBLOCK */
1567 luaCtx
.writeFunction("setDynBlocksPurgeInterval", [](uint64_t interval
) {
1568 DynBlockMaintenance::s_expiredDynBlocksPurgeInterval
= interval
;
1571 #ifdef HAVE_DNSCRYPT
1572 luaCtx
.writeFunction("addDNSCryptBind", [](const std::string
& addr
, const std::string
& providerName
, LuaTypeOrArrayOf
<std::string
> certFiles
, LuaTypeOrArrayOf
<std::string
> keyFiles
, boost::optional
<localbind_t
> vars
) {
1573 if (g_configurationDone
) {
1574 g_outputBuffer
= "addDNSCryptBind cannot be used at runtime!\n";
1577 bool reusePort
= false;
1578 int tcpFastOpenQueueSize
= 0;
1579 int tcpListenQueueSize
= 0;
1580 uint64_t maxInFlightQueriesPerConn
= 0;
1581 uint64_t tcpMaxConcurrentConnections
= 0;
1582 std::string interface
;
1584 std::vector
<DNSCryptContext::CertKeyPaths
> certKeys
;
1586 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConnections
);
1588 if (certFiles
.type() == typeid(std::string
) && keyFiles
.type() == typeid(std::string
)) {
1589 auto certFile
= boost::get
<std::string
>(certFiles
);
1590 auto keyFile
= boost::get
<std::string
>(keyFiles
);
1591 certKeys
.push_back({certFile
, keyFile
});
1593 else if (certFiles
.type() == typeid(LuaArray
<std::string
>) && keyFiles
.type() == typeid(LuaArray
<std::string
>)) {
1594 auto certFilesVect
= boost::get
<LuaArray
<std::string
>>(certFiles
);
1595 auto keyFilesVect
= boost::get
<LuaArray
<std::string
>>(keyFiles
);
1596 if (certFilesVect
.size() == keyFilesVect
.size()) {
1597 for (size_t idx
= 0; idx
< certFilesVect
.size(); idx
++) {
1598 certKeys
.push_back({certFilesVect
.at(idx
).second
, keyFilesVect
.at(idx
).second
});
1602 errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind!");
1603 g_outputBuffer
= "Error, mismatching number of certificates and keys in call to addDNSCryptBind()!";
1608 errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind()!");
1609 g_outputBuffer
= "Error, mismatching number of certificates and keys in call to addDNSCryptBind()!";
1614 auto ctx
= std::make_shared
<DNSCryptContext
>(providerName
, certKeys
);
1617 auto cs
= std::make_unique
<ClientState
>(ComboAddress(addr
, 443), false, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
1618 cs
->dnscryptCtx
= ctx
;
1619 g_dnsCryptLocals
.push_back(ctx
);
1620 g_frontends
.push_back(std::move(cs
));
1623 cs
= std::make_unique
<ClientState
>(ComboAddress(addr
, 443), true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
1624 cs
->dnscryptCtx
= ctx
;
1625 if (tcpListenQueueSize
> 0) {
1626 cs
->tcpListenQueueSize
= tcpListenQueueSize
;
1628 if (maxInFlightQueriesPerConn
> 0) {
1629 cs
->d_maxInFlightQueriesPerConn
= maxInFlightQueriesPerConn
;
1631 if (tcpMaxConcurrentConnections
> 0) {
1632 cs
->d_tcpConcurrentConnectionsLimit
= tcpMaxConcurrentConnections
;
1635 g_frontends
.push_back(std::move(cs
));
1637 catch (std::exception
& e
) {
1639 g_outputBuffer
= "Error: " + string(e
.what()) + "\n";
1643 luaCtx
.writeFunction("showDNSCryptBinds", []() {
1644 setLuaNoSideEffect();
1646 boost::format
fmt("%1$-3d %2% %|25t|%3$-20.20s");
1647 ret
<< (fmt
% "#" % "Address" % "Provider Name") << endl
;
1650 std::unordered_set
<std::shared_ptr
<DNSCryptContext
>> contexts
;
1651 for (const auto& frontend
: g_frontends
) {
1652 const std::shared_ptr
<DNSCryptContext
> ctx
= frontend
->dnscryptCtx
;
1653 if (!ctx
|| contexts
.count(ctx
) != 0) {
1656 contexts
.insert(ctx
);
1657 ret
<< (fmt
% idx
% frontend
->local
.toStringWithPort() % ctx
->getProviderName()) << endl
;
1661 g_outputBuffer
= ret
.str();
1664 luaCtx
.writeFunction("getDNSCryptBind", [](uint64_t idx
) {
1665 setLuaNoSideEffect();
1666 std::shared_ptr
<DNSCryptContext
> ret
= nullptr;
1667 if (idx
< g_dnsCryptLocals
.size()) {
1668 ret
= g_dnsCryptLocals
.at(idx
);
1673 luaCtx
.writeFunction("getDNSCryptBindCount", []() {
1674 setLuaNoSideEffect();
1675 return g_dnsCryptLocals
.size();
1677 #endif /* HAVE_DNSCRYPT */
1679 luaCtx
.writeFunction("showPools", []() {
1680 setLuaNoSideEffect();
1683 boost::format
fmt("%1$-20.20s %|25t|%2$20s %|25t|%3$20s %|50t|%4%");
1685 ret
<< (fmt
% "Name" % "Cache" % "ServerPolicy" % "Servers") << endl
;
1687 const auto localPools
= g_pools
.getCopy();
1688 for (const auto& entry
: localPools
) {
1689 const string
& name
= entry
.first
;
1690 const std::shared_ptr
<ServerPool
> pool
= entry
.second
;
1691 string cache
= pool
->packetCache
!= nullptr ? pool
->packetCache
->toString() : "";
1692 string policy
= g_policy
.getLocal()->getName();
1693 if (pool
->policy
!= nullptr) {
1694 policy
= pool
->policy
->getName();
1698 const auto poolServers
= pool
->getServers();
1699 for (const auto& server
: *poolServers
) {
1700 if (!servers
.empty()) {
1703 if (!server
.second
->getName().empty()) {
1704 servers
+= server
.second
->getName();
1707 servers
+= server
.second
->d_config
.remote
.toStringWithPort();
1710 ret
<< (fmt
% name
% cache
% policy
% servers
) << endl
;
1712 g_outputBuffer
= ret
.str();
1714 catch (std::exception
& e
) {
1715 g_outputBuffer
= e
.what();
1720 luaCtx
.writeFunction("getPool", [client
](const string
& poolName
) {
1722 return std::make_shared
<ServerPool
>();
1724 auto localPools
= g_pools
.getCopy();
1725 std::shared_ptr
<ServerPool
> pool
= createPoolIfNotExists(localPools
, poolName
);
1726 g_pools
.setState(localPools
);
1730 luaCtx
.writeFunction("setVerbose", [](bool verbose
) { g_verbose
= verbose
; });
1731 luaCtx
.writeFunction("getVerbose", []() { return g_verbose
; });
1732 luaCtx
.writeFunction("setVerboseHealthChecks", [](bool verbose
) { g_verboseHealthChecks
= verbose
; });
1733 luaCtx
.writeFunction("setVerboseLogDestination", [](const std::string
& dest
) {
1734 if (g_configurationDone
) {
1735 g_outputBuffer
= "setVerboseLogDestination() cannot be used at runtime!\n";
1739 auto stream
= std::ofstream(dest
.c_str());
1740 g_verboseStream
= std::move(stream
);
1742 catch (const std::exception
& e
) {
1743 errlog("Error while opening the verbose logging destination file %s: %s", dest
, e
.what());
1747 luaCtx
.writeFunction("setStaleCacheEntriesTTL", [](uint64_t ttl
) {
1748 checkParameterBound("setStaleCacheEntriesTTL", ttl
, std::numeric_limits
<uint32_t>::max());
1749 g_staleCacheEntriesTTL
= ttl
;
1752 luaCtx
.writeFunction("showBinds", []() {
1753 setLuaNoSideEffect();
1756 boost::format
fmt("%1$-3d %2$-20.20s %|35t|%3$-20.20s %|57t|%4%");
1758 ret
<< (fmt
% "#" % "Address" % "Protocol" % "Queries") << endl
;
1761 for (const auto& front
: g_frontends
) {
1762 ret
<< (fmt
% counter
% front
->local
.toStringWithPort() % front
->getType() % front
->queries
) << endl
;
1765 g_outputBuffer
= ret
.str();
1767 catch (std::exception
& e
) {
1768 g_outputBuffer
= e
.what();
1773 luaCtx
.writeFunction("getBind", [](uint64_t num
) {
1774 setLuaNoSideEffect();
1775 ClientState
* ret
= nullptr;
1776 if (num
< g_frontends
.size()) {
1777 ret
= g_frontends
[num
].get();
1782 luaCtx
.writeFunction("getBindCount", []() {
1783 setLuaNoSideEffect();
1784 return g_frontends
.size();
1787 luaCtx
.writeFunction("help", [](boost::optional
<std::string
> command
) {
1788 setLuaNoSideEffect();
1789 g_outputBuffer
= "";
1790 #ifndef DISABLE_COMPLETION
1791 for (const auto& keyword
: g_consoleKeywords
) {
1793 g_outputBuffer
+= keyword
.toString() + "\n";
1795 else if (keyword
.name
== command
) {
1796 g_outputBuffer
= keyword
.toString() + "\n";
1800 #endif /* DISABLE_COMPLETION */
1802 g_outputBuffer
= "Nothing found for " + *command
+ "\n";
1806 luaCtx
.writeFunction("showVersion", []() {
1807 setLuaNoSideEffect();
1808 g_outputBuffer
= "dnsdist " + std::string(VERSION
) + "\n";
1812 luaCtx
.writeFunction("setDefaultBPFFilter", [](std::shared_ptr
<BPFFilter
> bpf
) {
1813 if (g_configurationDone
) {
1814 g_outputBuffer
= "setDefaultBPFFilter() cannot be used at runtime!\n";
1817 g_defaultBPFFilter
= bpf
;
1820 luaCtx
.writeFunction("registerDynBPFFilter", [](std::shared_ptr
<DynBPFFilter
> dbpf
) {
1822 g_dynBPFFilters
.push_back(dbpf
);
1826 luaCtx
.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr
<DynBPFFilter
> dbpf
) {
1828 for (auto it
= g_dynBPFFilters
.begin(); it
!= g_dynBPFFilters
.end(); it
++) {
1830 g_dynBPFFilters
.erase(it
);
1837 #ifndef DISABLE_DEPRECATED_DYNBLOCK
1838 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
) {
1843 struct timespec until
, now
;
1844 clock_gettime(CLOCK_MONOTONIC
, &now
);
1846 int actualSeconds
= seconds
? *seconds
: 10;
1847 until
.tv_sec
+= actualSeconds
;
1848 for (const auto& capair
: m
) {
1849 if (dynbpf
->block(capair
.first
, until
)) {
1850 warnlog("Inserting eBPF dynamic block for %s for %d seconds: %s", capair
.first
.toString(), actualSeconds
, msg
? *msg
: "");
1854 #endif /* DISABLE_DEPRECATED_DYNBLOCK */
1856 #endif /* HAVE_EBPF */
1858 luaCtx
.writeFunction
<LuaAssociativeTable
<uint64_t>()>("getStatisticsCounters", []() {
1859 setLuaNoSideEffect();
1860 std::unordered_map
<string
, uint64_t> res
;
1861 for (const auto& entry
: g_stats
.entries
) {
1862 if (const auto& val
= boost::get
<pdns::stat_t
*>(&entry
.second
))
1863 res
[entry
.first
] = (*val
)->load();
1868 luaCtx
.writeFunction("includeDirectory", [&luaCtx
](const std::string
& dirname
) {
1869 if (g_configurationDone
) {
1870 errlog("includeDirectory() cannot be used at runtime!");
1871 g_outputBuffer
= "includeDirectory() cannot be used at runtime!\n";
1876 errlog("includeDirectory() cannot be used recursively!");
1877 g_outputBuffer
= "includeDirectory() cannot be used recursively!\n";
1882 if (stat(dirname
.c_str(), &st
)) {
1883 errlog("The included directory %s does not exist!", dirname
.c_str());
1884 g_outputBuffer
= "The included directory " + dirname
+ " does not exist!";
1888 if (!S_ISDIR(st
.st_mode
)) {
1889 errlog("The included directory %s is not a directory!", dirname
.c_str());
1890 g_outputBuffer
= "The included directory " + dirname
+ " is not a directory!";
1896 std::vector
<std::string
> files
;
1897 if (!(dirp
= opendir(dirname
.c_str()))) {
1898 errlog("Error opening the included directory %s!", dirname
.c_str());
1899 g_outputBuffer
= "Error opening the included directory " + dirname
+ "!";
1903 while ((ent
= readdir(dirp
)) != NULL
) {
1904 if (ent
->d_name
[0] == '.') {
1908 if (boost::ends_with(ent
->d_name
, ".conf")) {
1909 std::ostringstream namebuf
;
1910 namebuf
<< dirname
.c_str() << "/" << ent
->d_name
;
1912 if (stat(namebuf
.str().c_str(), &st
) || !S_ISREG(st
.st_mode
)) {
1916 files
.push_back(namebuf
.str());
1921 std::sort(files
.begin(), files
.end());
1925 for (const auto& file
: files
) {
1926 std::ifstream
ifs(file
);
1928 warnlog("Unable to read configuration from '%s'", file
);
1931 vinfolog("Read configuration from '%s'", file
);
1935 luaCtx
.executeCode(ifs
);
1942 luaCtx
.executeCode(ifs
);
1948 luaCtx
.writeFunction("setAPIWritable", [](bool writable
, boost::optional
<std::string
> apiConfigDir
) {
1950 g_apiReadWrite
= writable
;
1952 if (!(*apiConfigDir
).empty()) {
1953 g_apiConfigDirectory
= *apiConfigDir
;
1956 errlog("The API configuration directory value cannot be empty!");
1957 g_outputBuffer
= "The API configuration directory value cannot be empty!";
1962 luaCtx
.writeFunction("setServFailWhenNoServer", [](bool servfail
) {
1964 g_servFailOnNoPolicy
= servfail
;
1967 luaCtx
.writeFunction("setRoundRobinFailOnNoServer", [](bool fail
) {
1969 g_roundrobinFailOnNoServer
= fail
;
1972 luaCtx
.writeFunction("setConsistentHashingBalancingFactor", [](double factor
) {
1974 if (factor
>= 1.0) {
1975 g_consistentHashBalancingFactor
= factor
;
1978 errlog("Invalid value passed to setConsistentHashingBalancingFactor()!");
1979 g_outputBuffer
= "Invalid value passed to setConsistentHashingBalancingFactor()!\n";
1984 luaCtx
.writeFunction("setWeightedBalancingFactor", [](double factor
) {
1986 if (factor
>= 1.0) {
1987 g_weightedBalancingFactor
= factor
;
1990 errlog("Invalid value passed to setWeightedBalancingFactor()!");
1991 g_outputBuffer
= "Invalid value passed to setWeightedBalancingFactor()!\n";
1996 luaCtx
.writeFunction("setRingBuffersSize", [client
](uint64_t capacity
, boost::optional
<uint64_t> numberOfShards
) {
1998 if (g_configurationDone
) {
1999 errlog("setRingBuffersSize() cannot be used at runtime!");
2000 g_outputBuffer
= "setRingBuffersSize() cannot be used at runtime!\n";
2004 g_rings
.setCapacity(capacity
, numberOfShards
? *numberOfShards
: 10);
2007 g_rings
.setCapacity(0, 1);
2011 luaCtx
.writeFunction("setRingBuffersLockRetries", [](uint64_t retries
) {
2013 g_rings
.setNumberOfLockRetries(retries
);
2016 luaCtx
.writeFunction("setWHashedPertubation", [](uint64_t perturb
) {
2018 checkParameterBound("setWHashedPertubation", perturb
, std::numeric_limits
<uint32_t>::max());
2019 g_hashperturb
= perturb
;
2022 luaCtx
.writeFunction("setTCPInternalPipeBufferSize", [](uint64_t size
) { g_tcpInternalPipeBufferSize
= size
; });
2023 luaCtx
.writeFunction("setTCPFastOpenKey", [](const std::string
& keyString
) {
2025 uint32_t key
[4] = {};
2026 auto ret
= sscanf(keyString
.c_str(), "%" SCNx32
"-%" SCNx32
"-%" SCNx32
"-%" SCNx32
, &key
[0], &key
[1], &key
[2], &key
[3]);
2028 g_outputBuffer
= "Invalid value passed to setTCPFastOpenKey()!\n";
2031 extern vector
<uint32_t> g_TCPFastOpenKey
;
2032 for (const auto i
: key
) {
2033 g_TCPFastOpenKey
.push_back(i
);
2037 #ifdef HAVE_NET_SNMP
2038 luaCtx
.writeFunction("snmpAgent", [client
, configCheck
](bool enableTraps
, boost::optional
<std::string
> daemonSocket
) {
2039 if (client
|| configCheck
)
2041 if (g_configurationDone
) {
2042 errlog("snmpAgent() cannot be used at runtime!");
2043 g_outputBuffer
= "snmpAgent() cannot be used at runtime!\n";
2047 if (g_snmpEnabled
) {
2048 errlog("snmpAgent() cannot be used twice!");
2049 g_outputBuffer
= "snmpAgent() cannot be used twice!\n";
2053 g_snmpEnabled
= true;
2054 g_snmpTrapsEnabled
= enableTraps
;
2055 g_snmpAgent
= new DNSDistSNMPAgent("dnsdist", daemonSocket
? *daemonSocket
: std::string());
2058 luaCtx
.writeFunction("sendCustomTrap", [](const std::string
& str
) {
2059 if (g_snmpAgent
&& g_snmpTrapsEnabled
) {
2060 g_snmpAgent
->sendCustomTrap(str
);
2063 #endif /* HAVE_NET_SNMP */
2065 #ifndef DISABLE_POLICIES_BINDINGS
2066 luaCtx
.writeFunction("setServerPolicy", [](const ServerPolicy
& policy
) {
2068 g_policy
.setState(policy
);
2071 luaCtx
.writeFunction("setServerPolicyLua", [](string name
, ServerPolicy::policyfunc_t policy
) {
2073 g_policy
.setState(ServerPolicy
{name
, policy
, true});
2076 luaCtx
.writeFunction("setServerPolicyLuaFFI", [](string name
, ServerPolicy::ffipolicyfunc_t policy
) {
2078 auto pol
= ServerPolicy(name
, policy
);
2079 g_policy
.setState(std::move(pol
));
2082 luaCtx
.writeFunction("setServerPolicyLuaFFIPerThread", [](string name
, const std::string
& policyCode
) {
2084 auto pol
= ServerPolicy(name
, policyCode
);
2085 g_policy
.setState(std::move(pol
));
2088 luaCtx
.writeFunction("showServerPolicy", []() {
2090 g_outputBuffer
= g_policy
.getLocal()->getName() + "\n";
2093 luaCtx
.writeFunction("setPoolServerPolicy", [](ServerPolicy policy
, string pool
) {
2095 auto localPools
= g_pools
.getCopy();
2096 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(policy
));
2097 g_pools
.setState(localPools
);
2100 luaCtx
.writeFunction("setPoolServerPolicyLua", [](string name
, ServerPolicy::policyfunc_t policy
, string pool
) {
2102 auto localPools
= g_pools
.getCopy();
2103 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(ServerPolicy
{name
, policy
, true}));
2104 g_pools
.setState(localPools
);
2107 luaCtx
.writeFunction("setPoolServerPolicyLuaFFI", [](string name
, ServerPolicy::ffipolicyfunc_t policy
, string pool
) {
2109 auto localPools
= g_pools
.getCopy();
2110 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(ServerPolicy
{name
, policy
}));
2111 g_pools
.setState(localPools
);
2114 luaCtx
.writeFunction("setPoolServerPolicyLuaFFIPerThread", [](string name
, const std::string
& policyCode
, string pool
) {
2116 auto localPools
= g_pools
.getCopy();
2117 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(ServerPolicy
{name
, policyCode
}));
2118 g_pools
.setState(localPools
);
2121 luaCtx
.writeFunction("showPoolServerPolicy", [](string pool
) {
2123 auto localPools
= g_pools
.getCopy();
2124 auto poolObj
= getPool(localPools
, pool
);
2125 if (poolObj
->policy
== nullptr) {
2126 g_outputBuffer
= g_policy
.getLocal()->getName() + "\n";
2129 g_outputBuffer
= poolObj
->policy
->getName() + "\n";
2132 #endif /* DISABLE_POLICIES_BINDINGS */
2134 luaCtx
.writeFunction("setTCPDownstreamCleanupInterval", [](uint64_t interval
) {
2136 checkParameterBound("setTCPDownstreamCleanupInterval", interval
);
2137 setTCPDownstreamCleanupInterval(interval
);
2140 luaCtx
.writeFunction("setDoHDownstreamCleanupInterval", [](uint64_t interval
) {
2142 checkParameterBound("setDoHDownstreamCleanupInterval", interval
);
2143 setDoHDownstreamCleanupInterval(interval
);
2146 luaCtx
.writeFunction("setTCPDownstreamMaxIdleTime", [](uint64_t max
) {
2148 checkParameterBound("setTCPDownstreamMaxIdleTime", max
);
2149 setTCPDownstreamMaxIdleTime(max
);
2152 luaCtx
.writeFunction("setDoHDownstreamMaxIdleTime", [](uint64_t max
) {
2154 checkParameterBound("setDoHDownstreamMaxIdleTime", max
);
2155 setDoHDownstreamMaxIdleTime(max
);
2158 luaCtx
.writeFunction("setConsoleConnectionsLogging", [](bool enabled
) {
2159 g_logConsoleConnections
= enabled
;
2162 luaCtx
.writeFunction("setConsoleOutputMaxMsgSize", [](uint64_t size
) {
2163 checkParameterBound("setConsoleOutputMaxMsgSize", size
, std::numeric_limits
<uint32_t>::max());
2164 g_consoleOutputMsgMaxSize
= size
;
2167 luaCtx
.writeFunction("setProxyProtocolACL", [](LuaTypeOrArrayOf
<std::string
> inp
) {
2168 if (g_configurationDone
) {
2169 errlog("setProxyProtocolACL() cannot be used at runtime!");
2170 g_outputBuffer
= "setProxyProtocolACL() cannot be used at runtime!\n";
2175 if (auto str
= boost::get
<string
>(&inp
)) {
2179 for (const auto& p
: boost::get
<LuaArray
<std::string
>>(inp
)) {
2180 nmg
.addMask(p
.second
);
2183 g_proxyProtocolACL
= std::move(nmg
);
2186 luaCtx
.writeFunction("setProxyProtocolApplyACLToProxiedClients", [](bool apply
) {
2187 if (g_configurationDone
) {
2188 errlog("setProxyProtocolApplyACLToProxiedClients() cannot be used at runtime!");
2189 g_outputBuffer
= "setProxyProtocolApplyACLToProxiedClients() cannot be used at runtime!\n";
2193 g_applyACLToProxiedClients
= apply
;
2196 luaCtx
.writeFunction("setProxyProtocolMaximumPayloadSize", [](uint64_t size
) {
2197 if (g_configurationDone
) {
2198 errlog("setProxyProtocolMaximumPayloadSize() cannot be used at runtime!");
2199 g_outputBuffer
= "setProxyProtocolMaximumPayloadSize() cannot be used at runtime!\n";
2203 g_proxyProtocolMaximumSize
= std::max(static_cast<uint64_t>(16), size
);
2206 #ifndef DISABLE_RECVMMSG
2207 luaCtx
.writeFunction("setUDPMultipleMessagesVectorSize", [](uint64_t vSize
) {
2208 if (g_configurationDone
) {
2209 errlog("setUDPMultipleMessagesVectorSize() cannot be used at runtime!");
2210 g_outputBuffer
= "setUDPMultipleMessagesVectorSize() cannot be used at runtime!\n";
2213 #if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
2215 g_udpVectorSize
= vSize
;
2217 errlog("recvmmsg() support is not available!");
2218 g_outputBuffer
= "recvmmsg support is not available!\n";
2221 #endif /* DISABLE_RECVMMSG */
2223 luaCtx
.writeFunction("setAddEDNSToSelfGeneratedResponses", [](bool add
) {
2224 g_addEDNSToSelfGeneratedResponses
= add
;
2227 luaCtx
.writeFunction("setPayloadSizeOnSelfGeneratedAnswers", [](uint64_t payloadSize
) {
2228 if (payloadSize
< 512) {
2229 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!");
2230 g_outputBuffer
= "setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!";
2233 if (payloadSize
> s_udpIncomingBufferSize
) {
2234 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to %d instead!", s_udpIncomingBufferSize
);
2235 g_outputBuffer
= "setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to " + std::to_string(s_udpIncomingBufferSize
) + " instead";
2236 payloadSize
= s_udpIncomingBufferSize
;
2238 g_PayloadSizeSelfGenAnswers
= payloadSize
;
2241 #ifndef DISABLE_SECPOLL
2242 luaCtx
.writeFunction("showSecurityStatus", []() {
2243 setLuaNoSideEffect();
2244 g_outputBuffer
= std::to_string(g_stats
.securityStatus
) + "\n";
2247 luaCtx
.writeFunction("setSecurityPollSuffix", [](const std::string
& suffix
) {
2248 if (g_configurationDone
) {
2249 g_outputBuffer
= "setSecurityPollSuffix() cannot be used at runtime!\n";
2253 g_secPollSuffix
= suffix
;
2256 luaCtx
.writeFunction("setSecurityPollInterval", [](time_t newInterval
) {
2257 if (newInterval
<= 0) {
2258 warnlog("setSecurityPollInterval() should be > 0, skipping");
2259 g_outputBuffer
= "setSecurityPollInterval() should be > 0, skipping";
2262 g_secPollInterval
= newInterval
;
2264 #endif /* DISABLE_SECPOLL */
2266 luaCtx
.writeFunction("setSyslogFacility", [](boost::variant
<int, std::string
> facility
) {
2268 if (g_configurationDone
) {
2269 g_outputBuffer
= "setSyslogFacility cannot be used at runtime!\n";
2272 if (facility
.type() == typeid(std::string
)) {
2273 static std::map
<std::string
, int> const facilities
= {
2274 {"local0", LOG_LOCAL0
},
2275 {"log_local0", LOG_LOCAL0
},
2276 {"local1", LOG_LOCAL1
},
2277 {"log_local1", LOG_LOCAL1
},
2278 {"local2", LOG_LOCAL2
},
2279 {"log_local2", LOG_LOCAL2
},
2280 {"local3", LOG_LOCAL3
},
2281 {"log_local3", LOG_LOCAL3
},
2282 {"local4", LOG_LOCAL4
},
2283 {"log_local4", LOG_LOCAL4
},
2284 {"local5", LOG_LOCAL5
},
2285 {"log_local5", LOG_LOCAL5
},
2286 {"local6", LOG_LOCAL6
},
2287 {"log_local6", LOG_LOCAL6
},
2288 {"local7", LOG_LOCAL7
},
2289 {"log_local7", LOG_LOCAL7
},
2290 /* most of these likely make very little sense
2291 for dnsdist, but why not? */
2293 {"log_kern", LOG_KERN
},
2295 {"log_user", LOG_USER
},
2297 {"log_mail", LOG_MAIL
},
2298 {"daemon", LOG_DAEMON
},
2299 {"log_daemon", LOG_DAEMON
},
2301 {"log_auth", LOG_AUTH
},
2302 {"syslog", LOG_SYSLOG
},
2303 {"log_syslog", LOG_SYSLOG
},
2305 {"log_lpr", LOG_LPR
},
2307 {"log_news", LOG_NEWS
},
2309 {"log_uucp", LOG_UUCP
},
2311 {"log_cron", LOG_CRON
},
2312 {"authpriv", LOG_AUTHPRIV
},
2313 {"log_authpriv", LOG_AUTHPRIV
},
2315 {"log_ftp", LOG_FTP
}};
2316 auto facilityStr
= boost::get
<std::string
>(facility
);
2317 toLowerInPlace(facilityStr
);
2318 auto it
= facilities
.find(facilityStr
);
2319 if (it
== facilities
.end()) {
2320 g_outputBuffer
= "Unknown facility '" + facilityStr
+ "' passed to setSyslogFacility()!\n";
2323 setSyslogFacility(it
->second
);
2326 setSyslogFacility(boost::get
<int>(facility
));
2330 typedef std::unordered_map
<std::string
, std::string
> tlscertificateopts_t
;
2331 luaCtx
.writeFunction("newTLSCertificate", [client
](const std::string
& cert
, boost::optional
<tlscertificateopts_t
> opts
) {
2332 std::shared_ptr
<TLSCertKeyPair
> result
= nullptr;
2336 #if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
2337 std::optional
<std::string
> key
, password
;
2339 if (opts
->count("key")) {
2340 key
= boost::get
<const string
>((*opts
)["key"]);
2342 if (opts
->count("password")) {
2343 password
= boost::get
<const string
>((*opts
)["password"]);
2346 result
= std::make_shared
<TLSCertKeyPair
>(TLSCertKeyPair
{cert
, key
, password
});
2351 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
) {
2355 #ifdef HAVE_DNS_OVER_HTTPS
2357 if (g_configurationDone
) {
2358 g_outputBuffer
= "addDOHLocal cannot be used at runtime!\n";
2361 auto frontend
= std::make_shared
<DOHFrontend
>();
2363 if (certFiles
&& !certFiles
->empty()) {
2364 if (!loadTLSCertificateAndKeys("addDOHLocal", frontend
->d_tlsConfig
.d_certKeyPairs
, *certFiles
, *keyFiles
)) {
2368 frontend
->d_local
= ComboAddress(addr
, 443);
2371 frontend
->d_local
= ComboAddress(addr
, 80);
2372 infolog("No certificate provided for DoH endpoint %s, running in DNS over HTTP mode instead of DNS over HTTPS", frontend
->d_local
.toStringWithPort());
2376 if (urls
->type() == typeid(std::string
)) {
2377 frontend
->d_urls
.push_back(boost::get
<std::string
>(*urls
));
2379 else if (urls
->type() == typeid(LuaArray
<std::string
>)) {
2380 auto urlsVect
= boost::get
<LuaArray
<std::string
>>(*urls
);
2381 for (const auto& p
: urlsVect
) {
2382 frontend
->d_urls
.push_back(p
.second
);
2387 frontend
->d_urls
= {"/dns-query"};
2390 bool reusePort
= false;
2391 int tcpFastOpenQueueSize
= 0;
2392 int tcpListenQueueSize
= 0;
2393 uint64_t maxInFlightQueriesPerConn
= 0;
2394 uint64_t tcpMaxConcurrentConnections
= 0;
2395 std::string interface
;
2399 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConnections
);
2401 if (vars
->count("idleTimeout")) {
2402 frontend
->d_idleTimeout
= boost::get
<int>((*vars
)["idleTimeout"]);
2405 if (vars
->count("serverTokens")) {
2406 frontend
->d_serverTokens
= boost::get
<const string
>((*vars
)["serverTokens"]);
2409 if (vars
->count("customResponseHeaders")) {
2410 for (auto const& headerMap
: boost::get
<LuaAssociativeTable
<std::string
>>((*vars
).at("customResponseHeaders"))) {
2411 frontend
->d_customResponseHeaders
[boost::to_lower_copy(headerMap
.first
)] = headerMap
.second
;
2415 if (vars
->count("sendCacheControlHeaders")) {
2416 frontend
->d_sendCacheControlHeaders
= boost::get
<bool>((*vars
)["sendCacheControlHeaders"]);
2419 if (vars
->count("trustForwardedForHeader")) {
2420 frontend
->d_trustForwardedForHeader
= boost::get
<bool>((*vars
)["trustForwardedForHeader"]);
2423 if (vars
->count("internalPipeBufferSize")) {
2424 frontend
->d_internalPipeBufferSize
= boost::get
<int>((*vars
)["internalPipeBufferSize"]);
2427 if (vars
->count("exactPathMatching")) {
2428 frontend
->d_exactPathMatching
= boost::get
<bool>((*vars
)["exactPathMatching"]);
2431 parseTLSConfig(frontend
->d_tlsConfig
, "addDOHLocal", vars
);
2433 g_dohlocals
.push_back(frontend
);
2434 auto cs
= std::make_unique
<ClientState
>(frontend
->d_local
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
2435 cs
->dohFrontend
= frontend
;
2436 if (tcpListenQueueSize
> 0) {
2437 cs
->tcpListenQueueSize
= tcpListenQueueSize
;
2439 if (tcpMaxConcurrentConnections
> 0) {
2440 cs
->d_tcpConcurrentConnectionsLimit
= tcpMaxConcurrentConnections
;
2442 g_frontends
.push_back(std::move(cs
));
2444 throw std::runtime_error("addDOHLocal() called but DNS over HTTPS support is not present!");
2448 luaCtx
.writeFunction("showDOHFrontends", []() {
2449 #ifdef HAVE_DNS_OVER_HTTPS
2450 setLuaNoSideEffect();
2453 boost::format
fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d");
2454 ret
<< (fmt
% "#" % "Address" % "HTTP" % "HTTP/1" % "HTTP/2" % "GET" % "POST" % "Bad" % "Errors" % "Redirects" % "Valid" % "# ticket keys" % "Rotation delay" % "Next rotation") << endl
;
2456 for (const auto& ctx
: g_dohlocals
) {
2457 ret
<< (fmt
% counter
% ctx
->d_local
.toStringWithPort() % ctx
->d_httpconnects
% ctx
->d_http1Stats
.d_nbQueries
% ctx
->d_http2Stats
.d_nbQueries
% ctx
->d_getqueries
% ctx
->d_postqueries
% ctx
->d_badrequests
% ctx
->d_errorresponses
% ctx
->d_redirectresponses
% ctx
->d_validresponses
% ctx
->getTicketsKeysCount() % ctx
->getTicketsKeyRotationDelay() % ctx
->getNextTicketsKeyRotation()) << endl
;
2460 g_outputBuffer
= ret
.str();
2462 catch (const std::exception
& e
) {
2463 g_outputBuffer
= e
.what();
2467 g_outputBuffer
= "DNS over HTTPS support is not present!\n";
2471 luaCtx
.writeFunction("showDOHResponseCodes", []() {
2472 #ifdef HAVE_DNS_OVER_HTTPS
2473 setLuaNoSideEffect();
2476 boost::format
fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d %-15d %-15d");
2477 g_outputBuffer
= "\n- HTTP/1:\n\n";
2478 ret
<< (fmt
% "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others") << endl
;
2480 for (const auto& ctx
: g_dohlocals
) {
2481 ret
<< (fmt
% counter
% ctx
->d_local
.toStringWithPort() % ctx
->d_http1Stats
.d_nb200Responses
% ctx
->d_http1Stats
.d_nb400Responses
% ctx
->d_http1Stats
.d_nb403Responses
% ctx
->d_http1Stats
.d_nb500Responses
% ctx
->d_http1Stats
.d_nb502Responses
% ctx
->d_http1Stats
.d_nbOtherResponses
) << endl
;
2484 g_outputBuffer
+= ret
.str();
2487 g_outputBuffer
+= "\n- HTTP/2:\n\n";
2488 ret
<< (fmt
% "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others") << endl
;
2490 for (const auto& ctx
: g_dohlocals
) {
2491 ret
<< (fmt
% counter
% ctx
->d_local
.toStringWithPort() % ctx
->d_http2Stats
.d_nb200Responses
% ctx
->d_http2Stats
.d_nb400Responses
% ctx
->d_http2Stats
.d_nb403Responses
% ctx
->d_http2Stats
.d_nb500Responses
% ctx
->d_http2Stats
.d_nb502Responses
% ctx
->d_http2Stats
.d_nbOtherResponses
) << endl
;
2494 g_outputBuffer
+= ret
.str();
2496 catch (const std::exception
& e
) {
2497 g_outputBuffer
= e
.what();
2501 g_outputBuffer
= "DNS over HTTPS support is not present!\n";
2505 luaCtx
.writeFunction("getDOHFrontend", [client
](uint64_t index
) {
2506 std::shared_ptr
<DOHFrontend
> result
= nullptr;
2510 #ifdef HAVE_DNS_OVER_HTTPS
2511 setLuaNoSideEffect();
2513 if (index
< g_dohlocals
.size()) {
2514 result
= g_dohlocals
.at(index
);
2517 errlog("Error: trying to get DOH frontend with index %zu but we only have %zu frontend(s)\n", index
, g_dohlocals
.size());
2518 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";
2521 catch (const std::exception
& e
) {
2522 g_outputBuffer
= "Error while trying to get DOH frontend with index " + std::to_string(index
) + ": " + string(e
.what()) + "\n";
2523 errlog("Error while trying to get DOH frontend with index %zu: %s\n", index
, string(e
.what()));
2526 g_outputBuffer
="DNS over HTTPS support is not present!\n";
2531 luaCtx
.writeFunction("getDOHFrontendCount", []() {
2532 setLuaNoSideEffect();
2533 return g_dohlocals
.size();
2536 luaCtx
.registerFunction
<void (std::shared_ptr
<DOHFrontend
>::*)()>("reloadCertificates", [](std::shared_ptr
<DOHFrontend
> frontend
) {
2537 if (frontend
!= nullptr) {
2538 frontend
->reloadCertificates();
2542 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
) {
2543 #ifdef HAVE_DNS_OVER_HTTPS
2544 if (frontend
!= nullptr) {
2545 if (loadTLSCertificateAndKeys("DOHFrontend::loadNewCertificatesAndKeys", frontend
->d_tlsConfig
.d_certKeyPairs
, certFiles
, keyFiles
)) {
2546 frontend
->reloadCertificates();
2552 luaCtx
.registerFunction
<void (std::shared_ptr
<DOHFrontend
>::*)()>("rotateTicketsKey", [](std::shared_ptr
<DOHFrontend
> frontend
) {
2553 if (frontend
!= nullptr) {
2554 frontend
->rotateTicketsKey(time(nullptr));
2558 luaCtx
.registerFunction
<void (std::shared_ptr
<DOHFrontend
>::*)(const std::string
&)>("loadTicketsKeys", [](std::shared_ptr
<DOHFrontend
> frontend
, const std::string
& file
) {
2559 if (frontend
!= nullptr) {
2560 frontend
->loadTicketsKeys(file
);
2564 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
) {
2565 if (frontend
!= nullptr) {
2566 auto newMap
= std::make_shared
<std::vector
<std::shared_ptr
<DOHResponseMapEntry
>>>();
2567 newMap
->reserve(map
.size());
2569 for (const auto& entry
: map
) {
2570 newMap
->push_back(entry
.second
);
2573 frontend
->d_responsesMap
= std::move(newMap
);
2577 luaCtx
.registerFunction
<std::string (std::shared_ptr
<DOHFrontend
>::*)() const>("getAddressAndPort", [](const std::shared_ptr
<DOHFrontend
>& frontend
) {
2578 if (frontend
== nullptr) {
2579 return std::string();
2581 return frontend
->d_local
.toStringWithPort();
2584 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
) {
2588 #ifdef HAVE_DNS_OVER_TLS
2590 if (g_configurationDone
) {
2591 g_outputBuffer
= "addTLSLocal cannot be used at runtime!\n";
2594 shared_ptr
<TLSFrontend
> frontend
= std::make_shared
<TLSFrontend
>();
2596 if (!loadTLSCertificateAndKeys("addTLSLocal", frontend
->d_tlsConfig
.d_certKeyPairs
, certFiles
, keyFiles
)) {
2600 bool reusePort
= false;
2601 int tcpFastOpenQueueSize
= 0;
2602 int tcpListenQueueSize
= 0;
2603 uint64_t maxInFlightQueriesPerConn
= 0;
2604 uint64_t tcpMaxConcurrentConns
= 0;
2605 std::string interface
;
2609 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
, maxInFlightQueriesPerConn
, tcpMaxConcurrentConns
);
2611 if (vars
->count("provider")) {
2612 frontend
->d_provider
= boost::get
<const string
>((*vars
)["provider"]);
2613 boost::algorithm::to_lower(frontend
->d_provider
);
2616 parseTLSConfig(frontend
->d_tlsConfig
, "addTLSLocal", vars
);
2620 frontend
->d_addr
= ComboAddress(addr
, 853);
2621 if (!frontend
->d_provider
.empty()) {
2622 vinfolog("Loading TLS provider '%s'", frontend
->d_provider
);
2626 vinfolog("Loading default TLS provider 'openssl'");
2628 vinfolog("Loading default TLS provider 'gnutls'");
2631 // only works pre-startup, so no sync necessary
2632 auto cs
= std::make_unique
<ClientState
>(frontend
->d_addr
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
2633 cs
->tlsFrontend
= frontend
;
2634 if (tcpListenQueueSize
> 0) {
2635 cs
->tcpListenQueueSize
= tcpListenQueueSize
;
2637 if (maxInFlightQueriesPerConn
> 0) {
2638 cs
->d_maxInFlightQueriesPerConn
= maxInFlightQueriesPerConn
;
2640 if (tcpMaxConcurrentConns
> 0) {
2641 cs
->d_tcpConcurrentConnectionsLimit
= tcpMaxConcurrentConns
;
2644 g_tlslocals
.push_back(cs
->tlsFrontend
);
2645 g_frontends
.push_back(std::move(cs
));
2647 catch (const std::exception
& e
) {
2648 g_outputBuffer
= "Error: " + string(e
.what()) + "\n";
2651 throw std::runtime_error("addTLSLocal() called but DNS over TLS support is not present!");
2655 luaCtx
.writeFunction("showTLSContexts", []() {
2656 #ifdef HAVE_DNS_OVER_TLS
2657 setLuaNoSideEffect();
2660 boost::format
fmt("%1$-3d %2$-20.20s %|25t|%3$-14d %|40t|%4$-14d %|54t|%5$-21.21s");
2662 ret
<< (fmt
% "#" % "Address" % "# ticket keys" % "Rotation delay" % "Next rotation") << endl
;
2664 for (const auto& ctx
: g_tlslocals
) {
2665 ret
<< (fmt
% counter
% ctx
->d_addr
.toStringWithPort() % ctx
->getTicketsKeysCount() % ctx
->getTicketsKeyRotationDelay() % ctx
->getNextTicketsKeyRotation()) << endl
;
2668 g_outputBuffer
= ret
.str();
2670 catch (const std::exception
& e
) {
2671 g_outputBuffer
= e
.what();
2675 g_outputBuffer
= "DNS over TLS support is not present!\n";
2679 luaCtx
.writeFunction("getTLSContext", [](uint64_t index
) {
2680 std::shared_ptr
<TLSCtx
> result
= nullptr;
2681 #ifdef HAVE_DNS_OVER_TLS
2682 setLuaNoSideEffect();
2684 if (index
< g_tlslocals
.size()) {
2685 result
= g_tlslocals
.at(index
)->getContext();
2688 errlog("Error: trying to get TLS context with index %zu but we only have %zu context(s)\n", index
, g_tlslocals
.size());
2689 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";
2692 catch (const std::exception
& e
) {
2693 g_outputBuffer
= "Error while trying to get TLS context with index " + std::to_string(index
) + ": " + string(e
.what()) + "\n";
2694 errlog("Error while trying to get TLS context with index %zu: %s\n", index
, string(e
.what()));
2697 g_outputBuffer
="DNS over TLS support is not present!\n";
2702 luaCtx
.writeFunction("getTLSFrontend", [](uint64_t index
) {
2703 std::shared_ptr
<TLSFrontend
> result
= nullptr;
2704 #ifdef HAVE_DNS_OVER_TLS
2705 setLuaNoSideEffect();
2707 if (index
< g_tlslocals
.size()) {
2708 result
= g_tlslocals
.at(index
);
2711 errlog("Error: trying to get TLS frontend with index %zu but we only have %zu frontends\n", index
, g_tlslocals
.size());
2712 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";
2715 catch (const std::exception
& e
) {
2716 g_outputBuffer
= "Error while trying to get TLS frontend with index " + std::to_string(index
) + ": " + string(e
.what()) + "\n";
2717 errlog("Error while trying to get TLS frontend with index %zu: %s\n", index
, string(e
.what()));
2720 g_outputBuffer
="DNS over TLS support is not present!\n";
2725 luaCtx
.writeFunction("getTLSFrontendCount", []() {
2726 setLuaNoSideEffect();
2727 return g_tlslocals
.size();
2730 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSCtx
>::*)()>("rotateTicketsKey", [](std::shared_ptr
<TLSCtx
>& ctx
) {
2731 if (ctx
!= nullptr) {
2732 ctx
->rotateTicketsKey(time(nullptr));
2736 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSCtx
>::*)(const std::string
&)>("loadTicketsKeys", [](std::shared_ptr
<TLSCtx
>& ctx
, const std::string
& file
) {
2737 if (ctx
!= nullptr) {
2738 ctx
->loadTicketsKeys(file
);
2742 luaCtx
.registerFunction
<std::string (std::shared_ptr
<TLSFrontend
>::*)() const>("getAddressAndPort", [](const std::shared_ptr
<TLSFrontend
>& frontend
) {
2743 if (frontend
== nullptr) {
2744 return std::string();
2746 return frontend
->d_addr
.toStringWithPort();
2749 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSFrontend
>::*)()>("rotateTicketsKey", [](std::shared_ptr
<TLSFrontend
>& frontend
) {
2750 if (frontend
== nullptr) {
2753 auto ctx
= frontend
->getContext();
2755 ctx
->rotateTicketsKey(time(nullptr));
2759 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSFrontend
>::*)(const std::string
&)>("loadTicketsKeys", [](std::shared_ptr
<TLSFrontend
>& frontend
, const std::string
& file
) {
2760 if (frontend
== nullptr) {
2763 auto ctx
= frontend
->getContext();
2765 ctx
->loadTicketsKeys(file
);
2769 luaCtx
.registerFunction
<void (std::shared_ptr
<TLSFrontend
>::*)()>("reloadCertificates", [](std::shared_ptr
<TLSFrontend
>& frontend
) {
2770 if (frontend
== nullptr) {
2773 frontend
->setupTLS();
2776 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
) {
2777 #ifdef HAVE_DNS_OVER_TLS
2778 if (loadTLSCertificateAndKeys("TLSFrontend::loadNewCertificatesAndKeys", frontend
->d_tlsConfig
.d_certKeyPairs
, certFiles
, keyFiles
)) {
2779 frontend
->setupTLS();
2784 luaCtx
.writeFunction("reloadAllCertificates", []() {
2785 for (auto& frontend
: g_frontends
) {
2790 #ifdef HAVE_DNSCRYPT
2791 if (frontend
->dnscryptCtx
) {
2792 frontend
->dnscryptCtx
->reloadCertificates();
2794 #endif /* HAVE_DNSCRYPT */
2795 #ifdef HAVE_DNS_OVER_TLS
2796 if (frontend
->tlsFrontend
) {
2797 frontend
->tlsFrontend
->setupTLS();
2799 #endif /* HAVE_DNS_OVER_TLS */
2800 #ifdef HAVE_DNS_OVER_HTTPS
2801 if (frontend
->dohFrontend
) {
2802 frontend
->dohFrontend
->reloadCertificates();
2804 #endif /* HAVE_DNS_OVER_HTTPS */
2806 catch (const std::exception
& e
) {
2807 errlog("Error reloading certificates for frontend %s: %s", frontend
->local
.toStringWithPort(), e
.what());
2812 luaCtx
.writeFunction("setAllowEmptyResponse", [](bool allow
) { g_allowEmptyResponse
= allow
; });
2813 luaCtx
.writeFunction("setDropEmptyQueries", [](bool drop
) { extern bool g_dropEmptyQueries
; g_dropEmptyQueries
= drop
; });
2815 #if defined(HAVE_LIBSSL) && defined(HAVE_OCSP_BASIC_SIGN) && !defined(DISABLE_OCSP_STAPLING)
2816 luaCtx
.writeFunction("generateOCSPResponse", [client
](const std::string
& certFile
, const std::string
& caCert
, const std::string
& caKey
, const std::string
& outFile
, int ndays
, int nmin
) {
2821 libssl_generate_ocsp_response(certFile
, caCert
, caKey
, outFile
, ndays
, nmin
);
2823 #endif /* HAVE_LIBSSL && HAVE_OCSP_BASIC_SIGN && !DISABLE_OCSP_STAPLING */
2825 luaCtx
.writeFunction("addCapabilitiesToRetain", [](LuaTypeOrArrayOf
<std::string
> caps
) {
2827 if (g_configurationDone
) {
2828 g_outputBuffer
= "addCapabilitiesToRetain cannot be used at runtime!\n";
2831 if (caps
.type() == typeid(std::string
)) {
2832 g_capabilitiesToRetain
.insert(boost::get
<std::string
>(caps
));
2834 else if (caps
.type() == typeid(LuaArray
<std::string
>)) {
2835 for (const auto& cap
: boost::get
<LuaArray
<std::string
>>(caps
)) {
2836 g_capabilitiesToRetain
.insert(cap
.second
);
2841 luaCtx
.writeFunction("setUDPSocketBufferSizes", [client
](uint64_t recv
, uint64_t snd
) {
2845 checkParameterBound("setUDPSocketBufferSizes", recv
, std::numeric_limits
<uint32_t>::max());
2846 checkParameterBound("setUDPSocketBufferSizes", snd
, std::numeric_limits
<uint32_t>::max());
2849 if (g_configurationDone
) {
2850 g_outputBuffer
= "setUDPSocketBufferSizes cannot be used at runtime!\n";
2854 g_socketUDPSendBuffer
= snd
;
2855 g_socketUDPRecvBuffer
= recv
;
2858 luaCtx
.writeFunction("setRandomizedOutgoingSockets", [](bool randomized
) {
2859 DownstreamState::s_randomizeSockets
= randomized
;
2862 luaCtx
.writeFunction("setRandomizedIdsOverUDP", [](bool randomized
) {
2863 DownstreamState::s_randomizeIDs
= randomized
;
2866 #if defined(HAVE_LIBSSL)
2867 luaCtx
.writeFunction("loadTLSEngine", [client
](const std::string
& engineName
, boost::optional
<std::string
> defaultString
) {
2872 auto [success
, error
] = libssl_load_engine(engineName
, defaultString
? std::optional
<std::string
>(*defaultString
) : std::nullopt
);
2874 g_outputBuffer
= "Error while trying to load TLS engine '" + engineName
+ "': " + error
+ "\n";
2875 errlog("Error while trying to load TLS engine '%s': %s", engineName
, error
);
2878 #endif /* HAVE_LIBSSL */
2880 luaCtx
.writeFunction("newThread", [client
, configCheck
](const std::string
& code
) {
2881 if (client
|| configCheck
) {
2884 std::thread
newThread(LuaThread
, code
);
2889 luaCtx
.writeFunction("declareMetric", [](const std::string
& name
, const std::string
& type
, const std::string
& description
) {
2890 if (g_configurationDone
) {
2891 g_outputBuffer
= "declareMetric cannot be used at runtime!\n";
2894 if (!std::regex_match(name
, std::regex("^[a-z0-9-]+$"))) {
2895 g_outputBuffer
= "Unable to declare metric '" + name
+ "': invalid name\n";
2896 errlog("Unable to declare metric '%s': invalid name", name
);
2899 if (type
== "counter") {
2900 auto itp
= g_stats
.customCounters
.emplace(name
, 0);
2902 g_stats
.entries
.emplace_back(name
, &g_stats
.customCounters
[name
]);
2903 addMetricDefinition(name
, "counter", description
);
2906 else if (type
== "gauge") {
2907 auto itp
= g_stats
.customGauges
.emplace(name
, 0.);
2909 g_stats
.entries
.emplace_back(name
, &g_stats
.customGauges
[name
]);
2910 addMetricDefinition(name
, "gauge", description
);
2914 g_outputBuffer
= "declareMetric unknown type '" + type
+ "'\n";
2915 errlog("Unable to declareMetric '%s': no such type '%s'", name
, type
);
2920 luaCtx
.writeFunction("incMetric", [](const std::string
& name
) {
2921 auto metric
= g_stats
.customCounters
.find(name
);
2922 if (metric
!= g_stats
.customCounters
.end()) {
2923 return ++(metric
->second
);
2925 g_outputBuffer
= "incMetric no such metric '" + name
+ "'\n";
2926 errlog("Unable to incMetric: no such name '%s'", name
);
2929 luaCtx
.writeFunction("decMetric", [](const std::string
& name
) {
2930 auto metric
= g_stats
.customCounters
.find(name
);
2931 if (metric
!= g_stats
.customCounters
.end()) {
2932 return --(metric
->second
);
2934 g_outputBuffer
= "decMetric no such metric '" + name
+ "'\n";
2935 errlog("Unable to decMetric: no such name '%s'", name
);
2938 luaCtx
.writeFunction("setMetric", [](const std::string
& name
, const double& value
) {
2939 auto metric
= g_stats
.customGauges
.find(name
);
2940 if (metric
!= g_stats
.customGauges
.end()) {
2941 metric
->second
= value
;
2944 g_outputBuffer
= "setMetric no such metric '" + name
+ "'\n";
2945 errlog("Unable to setMetric: no such name '%s'", name
);
2948 luaCtx
.writeFunction("getMetric", [](const std::string
& name
) {
2949 auto counter
= g_stats
.customCounters
.find(name
);
2950 if (counter
!= g_stats
.customCounters
.end()) {
2951 return (double)counter
->second
.load();
2954 auto gauge
= g_stats
.customGauges
.find(name
);
2955 if (gauge
!= g_stats
.customGauges
.end()) {
2956 return gauge
->second
.load();
2959 g_outputBuffer
= "getMetric no such metric '" + name
+ "'\n";
2960 errlog("Unable to getMetric: no such name '%s'", name
);
2965 vector
<std::function
<void(void)>> setupLua(LuaContext
& luaCtx
, bool client
, bool configCheck
, const std::string
& config
)
2967 // this needs to exist only during the parsing of the configuration
2968 // and cannot be captured by lambdas
2969 g_launchWork
= std::vector
<std::function
<void(void)>>();
2971 setupLuaActions(luaCtx
);
2972 setupLuaConfig(luaCtx
, client
, configCheck
);
2973 setupLuaBindings(luaCtx
, client
);
2974 setupLuaBindingsDNSCrypt(luaCtx
, client
);
2975 setupLuaBindingsDNSQuestion(luaCtx
);
2976 setupLuaBindingsKVS(luaCtx
, client
);
2977 setupLuaBindingsPacketCache(luaCtx
, client
);
2978 setupLuaBindingsProtoBuf(luaCtx
, client
, configCheck
);
2979 setupLuaInspection(luaCtx
);
2980 setupLuaRules(luaCtx
);
2981 setupLuaVars(luaCtx
);
2982 setupLuaWeb(luaCtx
);
2984 #ifdef LUAJIT_VERSION
2985 luaCtx
.executeCode(getLuaFFIWrappers());
2988 std::ifstream
ifs(config
);
2990 warnlog("Unable to read configuration from '%s'", config
);
2992 vinfolog("Read configuration from '%s'", config
);
2994 luaCtx
.executeCode(ifs
);
2996 auto ret
= *g_launchWork
;
2997 g_launchWork
= boost::none
;