]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-lua.cc
dnsdist: Set the DNS over HTTP/3 default port to 443
[thirdparty/pdns.git] / pdns / dnsdist-lua.cc
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
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.
8 *
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.
12 *
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.
17 *
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.
21 */
6bb38cd6 22
caaa8dbd 23#include <cstdint>
6bb38cd6
RG
24#include <dirent.h>
25#include <fstream>
caaa8dbd 26#include <cinttypes>
aff9ca62
PD
27
28// for OpenBSD, sys/socket.h needs to come before net/if.h
29#include <sys/socket.h>
6bb38cd6 30#include <net/if.h>
aff9ca62 31
6211164a 32#include <regex>
9ef9b494 33#include <sys/types.h>
6bb38cd6 34#include <sys/stat.h>
df111b53 35#include <thread>
be5e1fca 36#include <vector>
6bb38cd6
RG
37
38#include "dnsdist.hh"
0b96a918 39#include "dnsdist-carbon.hh"
972d353a 40#include "dnsdist-concurrent-connections.hh"
b5521206 41#include "dnsdist-console.hh"
b03d051c 42#include "dnsdist-dynblocks.hh"
09708c45 43#include "dnsdist-discovery.hh"
e7c732b8 44#include "dnsdist-ecs.hh"
dd9c8246 45#include "dnsdist-healthchecks.hh"
6bb38cd6 46#include "dnsdist-lua.hh"
0ed8f0fa
RG
47#ifdef LUAJIT_VERSION
48#include "dnsdist-lua-ffi.hh"
49#endif /* LUAJIT_VERSION */
67cbba12 50#include "dnsdist-metrics.hh"
e82bf80f 51#include "dnsdist-nghttp2.hh"
448d66d4 52#include "dnsdist-proxy-protocol.hh"
03b00917 53#include "dnsdist-rings.hh"
5d4e1ef8 54#include "dnsdist-secpoll.hh"
65193dcf 55#include "dnsdist-session-cache.hh"
9fdcf7ca 56#include "dnsdist-tcp-downstream.hh"
1c90c6bd 57#include "dnsdist-web.hh"
6bb38cd6 58
df111b53 59#include "base64.hh"
b3356965 60#include "coverage.hh"
9eb152c4 61#include "doh.hh"
ed16c81f 62#include "doq-common.hh"
6bb38cd6 63#include "dolog.hh"
6bb38cd6 64#include "sodcrypto.hh"
7dd60afe 65#include "threadname.hh"
6bb38cd6 66
8c15553e
RG
67#ifdef HAVE_LIBSSL
68#include "libssl.hh"
69#endif
70
6bb38cd6 71#include <boost/logic/tribool.hpp>
905dae56 72#include <boost/uuid/string_generator.hpp>
df111b53 73
6ab65223
PL
74#ifdef HAVE_SYSTEMD
75#include <systemd/sd-daemon.h>
76#endif
77
df111b53 78using std::thread;
79
fd51c832 80static boost::optional<std::vector<std::function<void(void)>>> g_launchWork = boost::none;
2e72cc0e 81
6bb38cd6
RG
82boost::tribool g_noLuaSideEffect;
83static bool g_included{false};
d8d85a30 84
6bb38cd6
RG
85/* this is a best effort way to prevent logging calls with no side-effects in the output of delta()
86 Functions can declare setLuaNoSideEffect() and if nothing else does declare a side effect, or nothing
87 has done so before on this invocation, this call won't be part of delta() output */
88void setLuaNoSideEffect()
153d5065 89{
69108ebb 90 if (g_noLuaSideEffect == false) // there has been a side effect already
6bb38cd6 91 return;
69108ebb 92 g_noLuaSideEffect = true;
6bb38cd6 93}
153d5065 94
6bb38cd6 95void setLuaSideEffect()
d8d85a30 96{
69108ebb 97 g_noLuaSideEffect = false;
6bb38cd6 98}
f850b032 99
6bb38cd6
RG
100bool getLuaNoSideEffect()
101{
d7a26377
RG
102 if (g_noLuaSideEffect) {
103 return true;
104 }
105 return false;
d8d85a30 106}
107
6bb38cd6 108void resetLuaSideEffect()
2d11d1b2 109{
6bb38cd6 110 g_noLuaSideEffect = boost::logic::indeterminate;
2d11d1b2 111}
112
bfb79abc 113using localbind_t = LuaAssociativeTable<boost::variant<bool, int, std::string, LuaArray<int>, LuaArray<std::string>, LuaAssociativeTable<std::string>>>;
6bb38cd6 114
36800a60 115static void parseLocalBindVars(boost::optional<localbind_t>& vars, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface, std::set<int>& cpus, int& tcpListenQueueSize, uint64_t& maxInFlightQueriesPerConnection, uint64_t& tcpMaxConcurrentConnections, bool& enableProxyProtocol)
efd35aa8
RG
116{
117 if (vars) {
45f86d73
AT
118 LuaArray<int> setCpus;
119
120 getOptionalValue<bool>(vars, "reusePort", reusePort);
36800a60 121 getOptionalValue<bool>(vars, "enableProxyProtocol", enableProxyProtocol);
45f86d73
AT
122 getOptionalValue<int>(vars, "tcpFastOpenQueueSize", tcpFastOpenQueueSize);
123 getOptionalValue<int>(vars, "tcpListenQueueSize", tcpListenQueueSize);
124 getOptionalValue<int>(vars, "maxConcurrentTCPConnections", tcpMaxConcurrentConnections);
125 getOptionalValue<int>(vars, "maxInFlight", maxInFlightQueriesPerConnection);
126 getOptionalValue<std::string>(vars, "interface", interface);
127 if (getOptionalValue<decltype(setCpus)>(vars, "cpus", setCpus) > 0) {
128 for (const auto& cpu : setCpus) {
f0e4dcba
RG
129 cpus.insert(cpu.second);
130 }
131 }
efd35aa8
RG
132 }
133}
134
13a81ae6 135#if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS) || defined(HAVE_DNS_OVER_QUIC)
c92e6020 136static bool loadTLSCertificateAndKeys(const std::string& context, std::vector<TLSCertKeyPair>& pairs, const boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>>& certFiles, const LuaTypeOrArrayOf<std::string>& keyFiles)
8ef43a02
RG
137{
138 if (certFiles.type() == typeid(std::string) && keyFiles.type() == typeid(std::string)) {
139 auto certFile = boost::get<std::string>(certFiles);
140 auto keyFile = boost::get<std::string>(keyFiles);
bf8cd40d 141 pairs.clear();
afdad066 142 pairs.emplace_back(certFile, keyFile);
6de19ce2
CHB
143 }
144 else if (certFiles.type() == typeid(std::shared_ptr<TLSCertKeyPair>)) {
145 auto cert = boost::get<std::shared_ptr<TLSCertKeyPair>>(certFiles);
146 pairs.clear();
147 pairs.emplace_back(*cert);
148 }
bfb79abc
RG
149 else if (certFiles.type() == typeid(LuaArray<std::shared_ptr<TLSCertKeyPair>>)) {
150 auto certs = boost::get<LuaArray<std::shared_ptr<TLSCertKeyPair>>>(certFiles);
6de19ce2
CHB
151 pairs.clear();
152 for (const auto& cert : certs) {
153 pairs.emplace_back(*(cert.second));
154 }
8ef43a02 155 }
bfb79abc
RG
156 else if (certFiles.type() == typeid(LuaArray<std::string>) && keyFiles.type() == typeid(LuaArray<std::string>)) {
157 auto certFilesVect = boost::get<LuaArray<std::string>>(certFiles);
158 auto keyFilesVect = boost::get<LuaArray<std::string>>(keyFiles);
8ef43a02 159 if (certFilesVect.size() == keyFilesVect.size()) {
bf8cd40d 160 pairs.clear();
8ef43a02 161 for (size_t idx = 0; idx < certFilesVect.size(); idx++) {
afdad066 162 pairs.emplace_back(certFilesVect.at(idx).second, keyFilesVect.at(idx).second);
8ef43a02
RG
163 }
164 }
165 else {
bf8cd40d 166 errlog("Error, mismatching number of certificates and keys in call to %s()!", context);
69108ebb 167 g_outputBuffer = "Error, mismatching number of certificates and keys in call to " + context + "()!";
8ef43a02
RG
168 return false;
169 }
170 }
171 else {
bf8cd40d 172 errlog("Error, mismatching number of certificates and keys in call to %s()!", context);
69108ebb 173 g_outputBuffer = "Error, mismatching number of certificates and keys in call to " + context + "()!";
8ef43a02
RG
174 return false;
175 }
176
177 return true;
178}
b54e94dc 179
45f86d73 180static void parseTLSConfig(TLSConfig& config, const std::string& context, boost::optional<localbind_t>& vars)
b54e94dc 181{
45f86d73
AT
182 getOptionalValue<std::string>(vars, "ciphers", config.d_ciphers);
183 getOptionalValue<std::string>(vars, "ciphersTLS13", config.d_ciphers13);
b54e94dc
RG
184
185#ifdef HAVE_LIBSSL
45f86d73 186 std::string minVersion;
41366c40 187 if (getOptionalValue<std::string>(vars, "minTLSVersion", minVersion) > 0) {
45f86d73 188 config.d_minTLSVersion = libssl_tls_version_from_string(minVersion);
b54e94dc 189 }
45f86d73
AT
190#else /* HAVE_LIBSSL */
191 if (vars->erase("minTLSVersion") > 0)
192 warnlog("minTLSVersion has no effect with chosen TLS library");
b54e94dc
RG
193#endif /* HAVE_LIBSSL */
194
45f86d73
AT
195 getOptionalValue<std::string>(vars, "ticketKeyFile", config.d_ticketKeyFile);
196 getOptionalValue<int>(vars, "ticketsKeysRotationDelay", config.d_ticketsKeyRotationDelay);
197 getOptionalValue<int>(vars, "numberOfTicketsKeys", config.d_numberOfTicketsKeys);
198 getOptionalValue<bool>(vars, "preferServerCiphers", config.d_preferServerCiphers);
199 getOptionalValue<int>(vars, "sessionTimeout", config.d_sessionTimeout);
200 getOptionalValue<bool>(vars, "sessionTickets", config.d_enableTickets);
201 int numberOfStoredSessions{0};
202 if (getOptionalValue<int>(vars, "numberOfStoredSessions", numberOfStoredSessions) > 0) {
203 if (numberOfStoredSessions < 0) {
204 errlog("Invalid value '%d' for %s() parameter 'numberOfStoredSessions', should be >= 0, dismissing", numberOfStoredSessions, context);
aa69821b 205 g_outputBuffer = "Invalid value '" + std::to_string(numberOfStoredSessions) + "' for " + context + "() parameter 'numberOfStoredSessions', should be >= 0, dimissing";
c7abe16d
RG
206 }
207 else {
a8921ddc 208 config.d_maxStoredSessions = numberOfStoredSessions;
b54e94dc 209 }
b54e94dc
RG
210 }
211
45f86d73
AT
212 LuaArray<std::string> files;
213 if (getOptionalValue<decltype(files)>(vars, "ocspResponses", files) > 0) {
b54e94dc
RG
214 for (const auto& file : files) {
215 config.d_ocspFiles.push_back(file.second);
216 }
217 }
7f8a5a32 218
45f86d73 219 if (vars->count("keyLogFile") > 0) {
f8bbe49b 220#ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
45f86d73 221 getOptionalValue<std::string>(vars, "keyLogFile", config.d_keyLogFile);
f8bbe49b
RG
222#else
223 errlog("TLS Key logging has been enabled using the 'keyLogFile' parameter to %s(), but this version of OpenSSL does not support it", context);
224 g_outputBuffer = "TLS Key logging has been enabled using the 'keyLogFile' parameter to " + context + "(), but this version of OpenSSL does not support it";
225#endif
7f8a5a32 226 }
08d3b723 227
41366c40
RG
228 getOptionalValue<bool>(vars, "releaseBuffers", config.d_releaseBuffers);
229 getOptionalValue<bool>(vars, "enableRenegotiation", config.d_enableRenegotiation);
230 getOptionalValue<bool>(vars, "tlsAsyncMode", config.d_asyncMode);
5afc196c 231 getOptionalValue<bool>(vars, "ktls", config.d_ktls);
e80c3b09 232 getOptionalValue<bool>(vars, "readAhead", config.d_readAhead);
b54e94dc
RG
233}
234
39006144 235#endif // defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
8ef43a02 236
23a3bc71 237void checkParameterBound(const std::string& parameter, uint64_t value, size_t max)
4f699841 238{
1cf3aa80
RG
239 if (value > max) {
240 throw std::runtime_error("The value (" + std::to_string(value) + ") passed to " + parameter + " is too large, the maximum is " + std::to_string(max));
4f699841
RG
241 }
242}
243
26512612 244static void LuaThread(const std::string& code)
74a2ea87 245{
7dd60afe 246 setThreadName("dnsdist/lua-bg");
74a2ea87 247 LuaContext l;
055d1cec
PD
248
249 // mask SIGTERM on threads so the signal always comes to dnsdist itself
250 sigset_t blockSignals;
251
252 sigemptyset(&blockSignals);
253 sigaddset(&blockSignals, SIGTERM);
254
255 pthread_sigmask(SIG_BLOCK, &blockSignals, nullptr);
256
74a2ea87
PD
257 // submitToMainThread is camelcased, threadmessage is not.
258 // This follows our tradition of hooks we call being lowercased but functions the user can call being camelcased.
bfb79abc 259 l.writeFunction("submitToMainThread", [](std::string cmd, LuaAssociativeTable<std::string> data) {
74a2ea87
PD
260 auto lua = g_lua.lock();
261 // maybe offer more than `void`
bfb79abc 262 auto func = lua->readVariable<boost::optional<std::function<void(std::string cmd, LuaAssociativeTable<std::string> data)>>>("threadmessage");
74a2ea87 263 if (func) {
c92e6020 264 func.get()(std::move(cmd), std::move(data));
74a2ea87
PD
265 }
266 else {
267 errlog("Lua thread called submitToMainThread but no threadmessage receiver is defined");
268 }
269 });
270
271 // function threadmessage(cmd, data) print("got thread data:", cmd) for k,v in pairs(data) do print(k,v) end end
272
273 for (;;) {
274 try {
275 l.executeCode(code);
276 errlog("Lua thread exited, restarting in 5 seconds");
277 }
278 catch (const std::exception& e) {
279 errlog("Lua thread crashed, restarting in 5 seconds: %s", e.what());
280 }
281 catch (...) {
282 errlog("Lua thread crashed, restarting in 5 seconds");
283 }
284 sleep(5);
285 }
286}
287
c02b6723 288static bool checkConfigurationTime(const std::string& name)
f727b3c6
RG
289{
290 if (!g_configurationDone) {
291 return true;
292 }
293 g_outputBuffer = name + " cannot be used at runtime!\n";
294 errlog("%s cannot be used at runtime!", name);
295 return false;
296}
297
a7643425 298// NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold
fd51c832 299static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
df111b53 300{
bfb79abc 301 typedef LuaAssociativeTable<boost::variant<bool, std::string, LuaArray<std::string>, DownstreamState::checkfunc_t>> newserver_t;
fd51c832 302 luaCtx.writeFunction("inClientStartup", [client]() {
69108ebb 303 return client && !g_configurationDone;
6f2a4580
PD
304 });
305
fd51c832 306 luaCtx.writeFunction("inConfigCheck", [configCheck]() {
dd6dfd99 307 return configCheck;
203b5348
RG
308 });
309
fd51c832 310 luaCtx.writeFunction("newServer",
69108ebb
RG
311 [client, configCheck](boost::variant<string, newserver_t> pvars, boost::optional<int> qps) {
312 setLuaSideEffect();
313
c29eedcb 314 boost::optional<newserver_t> vars = newserver_t();
09708c45 315 DownstreamState::Config config;
69108ebb 316
69108ebb
RG
317 std::string serverAddressStr;
318 if (auto addrStr = boost::get<string>(&pvars)) {
319 serverAddressStr = *addrStr;
320 if (qps) {
c29eedcb 321 (*vars)["qps"] = std::to_string(*qps);
69108ebb
RG
322 }
323 }
324 else {
325 vars = boost::get<newserver_t>(pvars);
c29eedcb 326 getOptionalValue<std::string>(vars, "address", serverAddressStr);
69108ebb
RG
327 }
328
c29eedcb
AT
329 std::string source;
330 if (getOptionalValue<std::string>(vars, "source", source) > 0) {
69108ebb 331 /* handle source in the following forms:
d1f3d3bc
RG
332 - v4 address ("192.0.2.1")
333 - v6 address ("2001:DB8::1")
334 - interface name ("eth0")
335 - v4 address and interface name ("192.0.2.1@eth0")
336 - v6 address and interface name ("2001:DB8::1@eth0")
337 */
69108ebb
RG
338 bool parsed = false;
339 std::string::size_type pos = source.find("@");
340 if (pos == std::string::npos) {
341 /* no '@', try to parse that as a valid v4/v6 address */
342 try {
8cf75ac2 343 config.sourceAddr = ComboAddress(source);
69108ebb
RG
344 parsed = true;
345 }
346 catch (...) {
347 }
348 }
349
350 if (parsed == false) {
351 /* try to parse as interface name, or v4/v6@itf */
8cf75ac2
RG
352 config.sourceItfName = source.substr(pos == std::string::npos ? 0 : pos + 1);
353 unsigned int itfIdx = if_nametoindex(config.sourceItfName.c_str());
69108ebb
RG
354 if (itfIdx != 0) {
355 if (pos == 0 || pos == std::string::npos) {
356 /* "eth0" or "@eth0" */
8cf75ac2 357 config.sourceItf = itfIdx;
69108ebb
RG
358 }
359 else {
360 /* "192.0.2.1@eth0" */
8cf75ac2
RG
361 config.sourceAddr = ComboAddress(source.substr(0, pos));
362 config.sourceItf = itfIdx;
69108ebb 363 }
83fe2c55 364#ifdef SO_BINDTODEVICE
69108ebb
RG
365 /* we need to retain CAP_NET_RAW to be able to set SO_BINDTODEVICE in the health checks */
366 g_capabilitiesToRetain.insert("CAP_NET_RAW");
83fe2c55 367#endif
69108ebb
RG
368 }
369 else {
8cf75ac2 370 warnlog("Dismissing source %s because '%s' is not a valid interface name", source, config.sourceItfName);
69108ebb
RG
371 }
372 }
373 }
8592b46e 374
c7abe16d 375 std::string valueStr;
c29eedcb
AT
376 if (getOptionalValue<std::string>(vars, "sockets", valueStr) > 0) {
377 config.d_numberOfSockets = std::stoul(valueStr);
8cf75ac2 378 if (config.d_numberOfSockets == 0) {
c29eedcb 379 warnlog("Dismissing invalid number of sockets '%s', using 1 instead", valueStr);
8cf75ac2 380 config.d_numberOfSockets = 1;
69108ebb
RG
381 }
382 }
383
41366c40
RG
384 getOptionalIntegerValue("newServer", vars, "qps", config.d_qpsLimit);
385 getOptionalIntegerValue("newServer", vars, "order", config.order);
386 getOptionalIntegerValue("newServer", vars, "weight", config.d_weight);
387 if (config.d_weight < 1) {
388 errlog("Error creating new server: downstream weight value must be greater than 0.");
389 return std::shared_ptr<DownstreamState>();
69108ebb 390 }
8592b46e 391
41366c40
RG
392 getOptionalIntegerValue("newServer", vars, "retries", config.d_retries);
393 getOptionalIntegerValue("newServer", vars, "tcpConnectTimeout", config.tcpConnectTimeout);
394 getOptionalIntegerValue("newServer", vars, "tcpSendTimeout", config.tcpSendTimeout);
395 getOptionalIntegerValue("newServer", vars, "tcpRecvTimeout", config.tcpRecvTimeout);
8592b46e 396
c29eedcb
AT
397 if (getOptionalValue<std::string>(vars, "checkInterval", valueStr) > 0) {
398 config.checkInterval = static_cast<unsigned int>(std::stoul(valueStr));
69108ebb 399 }
8592b46e 400
c29eedcb
AT
401 bool fastOpen{false};
402 if (getOptionalValue<bool>(vars, "tcpFastOpen", fastOpen) > 0) {
69108ebb 403 if (fastOpen) {
d987f632 404#ifdef MSG_FASTOPEN
8cf75ac2 405 config.tcpFastOpen = true;
d987f632 406#else
c29eedcb 407 warnlog("TCP Fast Open has been configured on downstream server %s but is not supported", serverAddressStr);
d987f632 408#endif
69108ebb
RG
409 }
410 }
284d460c 411
41366c40
RG
412 getOptionalIntegerValue("newServer", vars, "maxInFlight", config.d_maxInFlightQueriesPerConn);
413 getOptionalIntegerValue("newServer", vars, "maxConcurrentTCPConnections", config.d_tcpConcurrentConnectionsLimit);
5c7fb70a 414
41366c40 415 getOptionalValue<std::string>(vars, "name", config.name);
18eeccc9 416
c29eedcb
AT
417 if (getOptionalValue<std::string>(vars, "id", valueStr) > 0) {
418 config.id = boost::uuids::string_generator()(valueStr);
69108ebb 419 }
1720247e 420
c29eedcb
AT
421 if (getOptionalValue<std::string>(vars, "healthCheckMode", valueStr) > 0) {
422 const auto& mode = valueStr;
e3ab12d9
RG
423 if (pdns_iequals(mode, "auto")) {
424 config.availability = DownstreamState::Availability::Auto;
425 }
426 else if (pdns_iequals(mode, "lazy")) {
427 config.availability = DownstreamState::Availability::Lazy;
428 }
429 else if (pdns_iequals(mode, "up")) {
430 config.availability = DownstreamState::Availability::Up;
431 }
432 else if (pdns_iequals(mode, "down")) {
433 config.availability = DownstreamState::Availability::Down;
434 }
435 else {
436 warnlog("Ignoring unknown value '%s' for 'healthCheckMode' on 'newServer'", mode);
437 }
438 }
439
c29eedcb
AT
440 if (getOptionalValue<std::string>(vars, "checkName", valueStr) > 0) {
441 config.checkName = DNSName(valueStr);
69108ebb 442 }
ad485896 443
c29eedcb 444 getOptionalValue<std::string>(vars, "checkType", config.checkType);
41366c40 445 getOptionalIntegerValue("newServer", vars, "checkClass", config.checkClass);
c29eedcb 446 getOptionalValue<DownstreamState::checkfunc_t>(vars, "checkFunction", config.checkFunction);
41366c40 447 getOptionalIntegerValue("newServer", vars, "checkTimeout", config.checkTimeout);
c29eedcb
AT
448 getOptionalValue<bool>(vars, "checkTCP", config.d_tcpCheck);
449 getOptionalValue<bool>(vars, "setCD", config.setCD);
450 getOptionalValue<bool>(vars, "mustResolve", config.mustResolve);
a6e02424 451
c29eedcb
AT
452 if (getOptionalValue<std::string>(vars, "lazyHealthCheckSampleSize", valueStr) > 0) {
453 const auto& value = std::stoi(valueStr);
107fc443 454 checkParameterBound("lazyHealthCheckSampleSize", value);
b8099809 455 config.d_lazyHealthCheckSampleSize = value;
e3ab12d9
RG
456 }
457
c29eedcb
AT
458 if (getOptionalValue<std::string>(vars, "lazyHealthCheckMinSampleCount", valueStr) > 0) {
459 const auto& value = std::stoi(valueStr);
107fc443 460 checkParameterBound("lazyHealthCheckMinSampleCount", value);
b8099809 461 config.d_lazyHealthCheckMinSampleCount = value;
e3ab12d9
RG
462 }
463
c29eedcb
AT
464 if (getOptionalValue<std::string>(vars, "lazyHealthCheckThreshold", valueStr) > 0) {
465 const auto& value = std::stoi(valueStr);
107fc443 466 checkParameterBound("lazyHealthCheckThreshold", value, std::numeric_limits<uint8_t>::max());
b8099809 467 config.d_lazyHealthCheckThreshold = value;
e3ab12d9
RG
468 }
469
c29eedcb
AT
470 if (getOptionalValue<std::string>(vars, "lazyHealthCheckFailedInterval", valueStr) > 0) {
471 const auto& value = std::stoi(valueStr);
107fc443 472 checkParameterBound("lazyHealthCheckFailedInterval", value);
b8099809 473 config.d_lazyHealthCheckFailedInterval = value;
e3ab12d9
RG
474 }
475
c29eedcb 476 getOptionalValue<bool>(vars, "lazyHealthCheckUseExponentialBackOff", config.d_lazyHealthCheckUseExponentialBackOff);
93b395a5 477
c29eedcb
AT
478 if (getOptionalValue<std::string>(vars, "lazyHealthCheckMaxBackOff", valueStr) > 0) {
479 const auto& value = std::stoi(valueStr);
93b395a5 480 checkParameterBound("lazyHealthCheckMaxBackOff", value);
b8099809 481 config.d_lazyHealthCheckMaxBackOff = value;
93b395a5
RG
482 }
483
c29eedcb
AT
484 if (getOptionalValue<std::string>(vars, "lazyHealthCheckMode", valueStr) > 0) {
485 const auto& mode = valueStr;
e3ab12d9 486 if (pdns_iequals(mode, "TimeoutOnly")) {
b8099809 487 config.d_lazyHealthCheckMode = DownstreamState::LazyHealthCheckMode::TimeoutOnly;
e3ab12d9
RG
488 }
489 else if (pdns_iequals(mode, "TimeoutOrServFail")) {
b8099809 490 config.d_lazyHealthCheckMode = DownstreamState::LazyHealthCheckMode::TimeoutOrServFail;
e3ab12d9
RG
491 }
492 else {
493 warnlog("Ignoring unknown value '%s' for 'lazyHealthCheckMode' on 'newServer'", mode);
494 }
495 }
496
c29eedcb 497 getOptionalValue<bool>(vars, "lazyHealthCheckWhenUpgraded", config.d_upgradeToLazyHealthChecks);
5485edd1 498
c29eedcb
AT
499 getOptionalValue<bool>(vars, "useClientSubnet", config.useECS);
500 getOptionalValue<bool>(vars, "useProxyProtocol", config.useProxyProtocol);
6ad3e2e3 501 getOptionalValue<bool>(vars, "proxyProtocolAdvertiseTLS", config.d_proxyProtocolAdvertiseTLS);
d85c923f 502 getOptionalValue<bool>(vars, "disableZeroScope", config.disableZeroScope);
c29eedcb 503 getOptionalValue<bool>(vars, "ipBindAddrNoPort", config.ipBindAddrNoPort);
996db8e9 504
41366c40
RG
505 getOptionalIntegerValue("newServer", vars, "addXPF", config.xpfRRCode);
506 getOptionalIntegerValue("newServer", vars, "maxCheckFailures", config.maxCheckFailures);
507 getOptionalIntegerValue("newServer", vars, "rise", config.minRiseSuccesses);
5cc8371b 508
c29eedcb 509 getOptionalValue<bool>(vars, "reconnectOnUp", config.reconnectOnUp);
3a8ee8bf 510
c29eedcb
AT
511 LuaArray<string> cpuMap;
512 if (getOptionalValue<decltype(cpuMap)>(vars, "cpus", cpuMap) > 0) {
513 for (const auto& cpu : cpuMap) {
8cf75ac2 514 config.d_cpus.insert(std::stoi(cpu.second));
69108ebb
RG
515 }
516 }
218889f3 517
c29eedcb 518 getOptionalValue<bool>(vars, "tcpOnly", config.d_tcpOnly);
218889f3 519
8cf75ac2 520 std::shared_ptr<TLSCtx> tlsCtx;
c29eedcb
AT
521 getOptionalValue<std::string>(vars, "ciphers", config.d_tlsParams.d_ciphers);
522 getOptionalValue<std::string>(vars, "ciphers13", config.d_tlsParams.d_ciphers13);
523 getOptionalValue<std::string>(vars, "caStore", config.d_tlsParams.d_caStore);
524 getOptionalValue<bool>(vars, "validateCertificates", config.d_tlsParams.d_validateCertificates);
525 getOptionalValue<bool>(vars, "releaseBuffers", config.d_tlsParams.d_releaseBuffers);
526 getOptionalValue<bool>(vars, "enableRenegotiation", config.d_tlsParams.d_enableRenegotiation);
5afc196c 527 getOptionalValue<bool>(vars, "ktls", config.d_tlsParams.d_ktls);
c29eedcb
AT
528 getOptionalValue<std::string>(vars, "subjectName", config.d_tlsSubjectName);
529
530 if (getOptionalValue<std::string>(vars, "subjectAddr", valueStr) > 0) {
74b08b2a 531 try {
c29eedcb 532 ComboAddress ca(valueStr);
74b08b2a
RG
533 config.d_tlsSubjectName = ca.toString();
534 config.d_tlsSubjectIsAddr = true;
535 }
536 catch (const std::exception& e) {
537 errlog("Error creating new server: downstream subjectAddr value must be a valid IP address");
538 return std::shared_ptr<DownstreamState>();
539 }
540 }
218889f3 541
d0832592
PD
542 uint16_t serverPort = 53;
543
c29eedcb 544 if (getOptionalValue<std::string>(vars, "tls", valueStr) > 0) {
d0832592 545 serverPort = 853;
c29eedcb 546 config.d_tlsParams.d_provider = valueStr;
8d2fe9be 547 tlsCtx = getTLSContext(config.d_tlsParams);
218889f3 548
c29eedcb 549 if (getOptionalValue<std::string>(vars, "dohPath", valueStr) > 0) {
cf25b82b 550#if !defined(HAVE_DNS_OVER_HTTPS) || !defined(HAVE_NGHTTP2)
8e61ffb9 551 throw std::runtime_error("Outgoing DNS over HTTPS support requested (via 'dohPath' on newServer()) but it is not available");
f468a7fe
RG
552#endif
553
d0832592 554 serverPort = 443;
c29eedcb 555 config.d_dohPath = valueStr;
5a96fcd2 556
c29eedcb 557 getOptionalValue<bool>(vars, "addXForwardedHeaders", config.d_addXForwardedHeaders);
69108ebb 558 }
69108ebb
RG
559 }
560
d0832592
PD
561 try {
562 config.remote = ComboAddress(serverAddressStr, serverPort);
563 }
564 catch (const PDNSException& e) {
565 g_outputBuffer = "Error creating new server: " + string(e.reason);
566 errlog("Error creating new server with address %s: %s", serverAddressStr, e.reason);
567 return std::shared_ptr<DownstreamState>();
568 }
569 catch (const std::exception& e) {
570 g_outputBuffer = "Error creating new server: " + string(e.what());
571 errlog("Error creating new server with address %s: %s", serverAddressStr, e.what());
572 return std::shared_ptr<DownstreamState>();
573 }
574
575 if (IsAnyAddress(config.remote)) {
576 g_outputBuffer = "Error creating new server: invalid address for a downstream server.";
577 errlog("Error creating new server: %s is not a valid address for a downstream server", serverAddressStr);
578 return std::shared_ptr<DownstreamState>();
579 }
580
c29eedcb 581 LuaArray<std::string> pools;
41366c40 582 if (getOptionalValue<std::string>(vars, "pool", valueStr, false) > 0) {
c29eedcb
AT
583 config.pools.insert(valueStr);
584 }
585 else if (getOptionalValue<decltype(pools)>(vars, "pool", pools) > 0) {
586 for (auto& p : pools) {
587 config.pools.insert(p.second);
69108ebb 588 }
8cf75ac2
RG
589 }
590
09708c45
RG
591 bool autoUpgrade = false;
592 bool keepAfterUpgrade = false;
593 uint32_t upgradeInterval = 3600;
16a06fd4 594 uint16_t upgradeDoHKey = dnsdist::ServiceDiscovery::s_defaultDoHSVCKey;
09708c45
RG
595 std::string upgradePool;
596
c29eedcb
AT
597 getOptionalValue<bool>(vars, "autoUpgrade", autoUpgrade);
598 if (autoUpgrade) {
599 if (getOptionalValue<std::string>(vars, "autoUpgradeInterval", valueStr) > 0) {
09708c45 600 try {
c29eedcb 601 upgradeInterval = static_cast<uint32_t>(std::stoul(valueStr));
09708c45
RG
602 }
603 catch (const std::exception& e) {
604 warnlog("Error parsing 'autoUpgradeInterval' value: %s", e.what());
605 }
606 }
c29eedcb
AT
607 getOptionalValue<bool>(vars, "autoUpgradeKeep", keepAfterUpgrade);
608 getOptionalValue<std::string>(vars, "autoUpgradePool", upgradePool);
609 if (getOptionalValue<std::string>(vars, "autoUpgradeDoHKey", valueStr) > 0) {
09708c45 610 try {
c29eedcb 611 upgradeDoHKey = static_cast<uint16_t>(std::stoul(valueStr));
09708c45
RG
612 }
613 catch (const std::exception& e) {
614 warnlog("Error parsing 'autoUpgradeDoHKey' value: %s", e.what());
615 }
616 }
617 }
618
8cf75ac2
RG
619 // create but don't connect the socket in client or check-config modes
620 auto ret = std::make_shared<DownstreamState>(std::move(config), std::move(tlsCtx), !(client || configCheck));
621 if (!(client || configCheck)) {
8cc2c57d 622 infolog("Added downstream server %s", ret->d_config.remote.toStringWithPort());
09708c45
RG
623 }
624
19266e4a 625 if (autoUpgrade && ret->getProtocol() != dnsdist::Protocol::DoT && ret->getProtocol() != dnsdist::Protocol::DoH) {
09708c45 626 dnsdist::ServiceDiscovery::addUpgradeableServer(ret, upgradeInterval, upgradePool, upgradeDoHKey, keepAfterUpgrade);
8cf75ac2
RG
627 }
628
629 /* this needs to be done _AFTER_ the order has been set,
630 since the server are kept ordered inside the pool */
631 auto localPools = g_pools.getCopy();
632 if (!ret->d_config.pools.empty()) {
633 for (const auto& poolName : ret->d_config.pools) {
69108ebb
RG
634 addServerToPool(localPools, poolName, ret);
635 }
636 }
637 else {
638 addServerToPool(localPools, "", ret);
639 }
640 g_pools.setState(localPools);
641
642 if (ret->connected) {
69108ebb 643 if (g_launchWork) {
09708c45 644 g_launchWork->push_back([ret]() {
8cf75ac2 645 ret->start();
69108ebb
RG
646 });
647 }
648 else {
8cf75ac2 649 ret->start();
69108ebb
RG
650 }
651 }
8c82c02d 652
69108ebb
RG
653 auto states = g_dstates.getCopy();
654 states.push_back(ret);
655 std::stable_sort(states.begin(), states.end(), [](const decltype(ret)& a, const decltype(ret)& b) {
8cf75ac2 656 return a->d_config.order < b->d_config.order;
69108ebb
RG
657 });
658 g_dstates.setState(states);
c29eedcb 659 checkAllParametersConsumed("newServer", vars);
69108ebb
RG
660 return ret;
661 });
df111b53 662
fd51c832 663 luaCtx.writeFunction("rmServer",
69108ebb
RG
664 [](boost::variant<std::shared_ptr<DownstreamState>, int, std::string> var) {
665 setLuaSideEffect();
666 shared_ptr<DownstreamState> server = nullptr;
667 auto states = g_dstates.getCopy();
668 if (auto* rem = boost::get<shared_ptr<DownstreamState>>(&var)) {
669 server = *rem;
670 }
671 else if (auto str = boost::get<std::string>(&var)) {
672 const auto uuid = getUniqueID(*str);
673 for (auto& state : states) {
8cf75ac2 674 if (*state->d_config.id == uuid) {
69108ebb
RG
675 server = state;
676 }
677 }
678 }
679 else {
680 int idx = boost::get<int>(var);
681 server = states.at(idx);
682 }
683 if (!server) {
684 throw std::runtime_error("unable to locate the requested server");
685 }
686 auto localPools = g_pools.getCopy();
8cf75ac2 687 for (const string& poolName : server->d_config.pools) {
69108ebb
RG
688 removeServerFromPool(localPools, poolName, server);
689 }
690 /* the server might also be in the default pool */
691 removeServerFromPool(localPools, "", server);
692 g_pools.setState(localPools);
693 states.erase(remove(states.begin(), states.end(), server), states.end());
694 g_dstates.setState(states);
695 server->stop();
696 });
df111b53 697
fd51c832
RG
698 luaCtx.writeFunction("truncateTC", [](bool tc) { setLuaSideEffect(); g_truncateTC=tc; });
699 luaCtx.writeFunction("fixupCase", [](bool fu) { setLuaSideEffect(); g_fixupCase=fu; });
df111b53 700
fd51c832 701 luaCtx.writeFunction("addACL", [](const std::string& domain) {
69108ebb
RG
702 setLuaSideEffect();
703 g_ACL.modify([domain](NetmaskGroup& nmg) { nmg.addMask(domain); });
704 });
2e72cc0e 705
fd51c832 706 luaCtx.writeFunction("rmACL", [](const std::string& netmask) {
a1d18701 707 setLuaSideEffect();
708 g_ACL.modify([netmask](NetmaskGroup& nmg) { nmg.deleteMask(netmask); });
709 });
710
fd51c832 711 luaCtx.writeFunction("setLocal", [client](const std::string& addr, boost::optional<localbind_t> vars) {
69108ebb 712 setLuaSideEffect();
f727b3c6 713 if (client) {
69108ebb 714 return;
f727b3c6
RG
715 }
716
717 if (!checkConfigurationTime("setLocal")) {
69108ebb
RG
718 return;
719 }
f727b3c6 720
69108ebb
RG
721 bool reusePort = false;
722 int tcpFastOpenQueueSize = 0;
723 int tcpListenQueueSize = 0;
393ed488
RG
724 uint64_t maxInFlightQueriesPerConn = 0;
725 uint64_t tcpMaxConcurrentConnections = 0;
69108ebb
RG
726 std::string interface;
727 std::set<int> cpus;
36800a60 728 bool enableProxyProtocol = true;
efd35aa8 729
36800a60 730 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol);
efd35aa8 731
45f86d73
AT
732 checkAllParametersConsumed("setLocal", vars);
733
69108ebb
RG
734 try {
735 ComboAddress loc(addr, 53);
736 for (auto it = g_frontends.begin(); it != g_frontends.end();) {
737 /* DoH, DoT and DNSCrypt frontends are separate */
738 if ((*it)->tlsFrontend == nullptr && (*it)->dnscryptCtx == nullptr && (*it)->dohFrontend == nullptr) {
739 it = g_frontends.erase(it);
83654fe6 740 }
69108ebb
RG
741 else {
742 ++it;
519a0072 743 }
69108ebb 744 }
519a0072 745
69108ebb 746 // only works pre-startup, so no sync necessary
36800a60
RG
747 g_frontends.push_back(std::make_unique<ClientState>(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol));
748 auto tcpCS = std::make_unique<ClientState>(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol);
69108ebb
RG
749 if (tcpListenQueueSize > 0) {
750 tcpCS->tcpListenQueueSize = tcpListenQueueSize;
5949b95b 751 }
69108ebb
RG
752 if (maxInFlightQueriesPerConn > 0) {
753 tcpCS->d_maxInFlightQueriesPerConn = maxInFlightQueriesPerConn;
5949b95b 754 }
69108ebb
RG
755 if (tcpMaxConcurrentConnections > 0) {
756 tcpCS->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections;
757 }
758
759 g_frontends.push_back(std::move(tcpCS));
760 }
761 catch (const std::exception& e) {
762 g_outputBuffer = "Error: " + string(e.what()) + "\n";
763 }
764 });
5949b95b 765
fd51c832 766 luaCtx.writeFunction("addLocal", [client](const std::string& addr, boost::optional<localbind_t> vars) {
69108ebb
RG
767 setLuaSideEffect();
768 if (client)
769 return;
f727b3c6
RG
770
771 if (!checkConfigurationTime("addLocal")) {
69108ebb
RG
772 return;
773 }
774 bool reusePort = false;
775 int tcpFastOpenQueueSize = 0;
776 int tcpListenQueueSize = 0;
393ed488
RG
777 uint64_t maxInFlightQueriesPerConn = 0;
778 uint64_t tcpMaxConcurrentConnections = 0;
69108ebb
RG
779 std::string interface;
780 std::set<int> cpus;
36800a60 781 bool enableProxyProtocol = true;
efd35aa8 782
36800a60 783 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol);
45f86d73 784 checkAllParametersConsumed("addLocal", vars);
efd35aa8 785
69108ebb
RG
786 try {
787 ComboAddress loc(addr, 53);
788 // only works pre-startup, so no sync necessary
36800a60
RG
789 g_frontends.push_back(std::make_unique<ClientState>(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol));
790 auto tcpCS = std::make_unique<ClientState>(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol);
69108ebb
RG
791 if (tcpListenQueueSize > 0) {
792 tcpCS->tcpListenQueueSize = tcpListenQueueSize;
2e72cc0e 793 }
69108ebb
RG
794 if (maxInFlightQueriesPerConn > 0) {
795 tcpCS->d_maxInFlightQueriesPerConn = maxInFlightQueriesPerConn;
2e72cc0e 796 }
69108ebb
RG
797 if (tcpMaxConcurrentConnections > 0) {
798 tcpCS->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections;
e4944ea0 799 }
69108ebb
RG
800 g_frontends.push_back(std::move(tcpCS));
801 }
802 catch (std::exception& e) {
803 g_outputBuffer = "Error: " + string(e.what()) + "\n";
804 errlog("Error while trying to listen on %s: %s\n", addr, string(e.what()));
805 }
806 });
807
bfb79abc 808 luaCtx.writeFunction("setACL", [](LuaTypeOrArrayOf<std::string> inp) {
69108ebb
RG
809 setLuaSideEffect();
810 NetmaskGroup nmg;
811 if (auto str = boost::get<string>(&inp)) {
812 nmg.addMask(*str);
813 }
814 else
bfb79abc 815 for (const auto& p : boost::get<LuaArray<std::string>>(inp)) {
69108ebb 816 nmg.addMask(p.second);
cffde2fd 817 }
69108ebb 818 g_ACL.setState(nmg);
df111b53 819 });
6bb38cd6 820
cbee37e8 821 luaCtx.writeFunction("setACLFromFile", [](const std::string& file) {
69108ebb
RG
822 setLuaSideEffect();
823 NetmaskGroup nmg;
cbee37e8 824
69108ebb
RG
825 ifstream ifs(file);
826 if (!ifs) {
827 throw std::runtime_error("Could not open '" + file + "': " + stringerror());
828 }
829
830 string::size_type pos;
831 string line;
832 while (getline(ifs, line)) {
833 pos = line.find('#');
834 if (pos != string::npos)
835 line.resize(pos);
836 boost::trim(line);
837 if (line.empty())
838 continue;
839
840 nmg.addMask(line);
841 }
cbee37e8 842
69108ebb 843 g_ACL.setState(nmg);
cbee37e8
MH
844 });
845
fd51c832 846 luaCtx.writeFunction("showACL", []() {
69108ebb 847 setLuaNoSideEffect();
25b3e633 848 auto aclEntries = g_ACL.getLocal()->toStringVector();
cffde2fd 849
25b3e633
RG
850 for (const auto& entry : aclEntries) {
851 g_outputBuffer += entry + "\n";
852 }
69108ebb 853 });
6bb38cd6 854
fd51c832 855 luaCtx.writeFunction("shutdown", []() {
6ab65223 856#ifdef HAVE_SYSTEMD
69108ebb 857 sd_notify(0, "STOPPING=1");
a227f47d
RG
858#endif /* HAVE_SYSTEMD */
859#if 0
5f7151eb
RG
860 // Useful for debugging leaks, but might lead to race under load
861 // since other threads are still running.
862 for (auto& frontend : g_tlslocals) {
863 frontend->cleanup();
864 }
865 g_tlslocals.clear();
866 g_rings.clear();
a227f47d 867#endif /* 0 */
b3356965 868 pdns::coverage::dumpCoverageData();
69108ebb
RG
869 _exit(0);
870 });
df111b53 871
bfb79abc 872 typedef LuaAssociativeTable<boost::variant<bool, std::string>> showserversopts_t;
7f768697 873
fd51c832 874 luaCtx.writeFunction("showServers", [](boost::optional<showserversopts_t> vars) {
69108ebb
RG
875 setLuaNoSideEffect();
876 bool showUUIDs = false;
45f86d73
AT
877 getOptionalValue<bool>(vars, "showUUIDs", showUUIDs);
878 checkAllParametersConsumed("showServers", vars);
879
69108ebb
RG
880 try {
881 ostringstream ret;
882 boost::format fmt;
3619223b 883
cbd17252 884 auto latFmt = boost::format("%5.1f");
69108ebb 885 if (showUUIDs) {
cbd17252
CHB
886 fmt = boost::format("%1$-3d %15$-36s %2$-20.20s %|62t|%3% %|107t|%4$5s %|88t|%5$7.1f %|103t|%6$7d %|106t|%7$10d %|115t|%8$10d %|117t|%9$10d %|123t|%10$7d %|128t|%11$5.1f %|146t|%12$5s %|152t|%16$5s %|158t|%13$11d %14%");
887 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 (tcp latency)
888 ret << (fmt % "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools" % "UUID" % "TCP") << endl;
69108ebb
RG
889 }
890 else {
cbd17252
CHB
891 fmt = boost::format("%1$-3d %2$-20.20s %|25t|%3% %|70t|%4$5s %|51t|%5$7.1f %|66t|%6$7d %|69t|%7$10d %|78t|%8$10d %|80t|%9$10d %|86t|%10$7d %|91t|%11$5.1f %|109t|%12$5s %|115t|%15$5s %|121t|%13$11d %14%");
892 ret << (fmt % "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools" % "TCP") << endl;
df111b53 893 }
df111b53 894
69108ebb
RG
895 uint64_t totQPS{0}, totQueries{0}, totDrops{0};
896 int counter = 0;
897 auto states = g_dstates.getLocal();
898 for (const auto& s : *states) {
899 string status = s->getStatus();
900 string pools;
8cf75ac2
RG
901 for (const auto& p : s->d_config.pools) {
902 if (!pools.empty()) {
69108ebb 903 pools += " ";
8cf75ac2 904 }
69108ebb 905 pools += p;
7f768697 906 }
cbd17252
CHB
907 const std::string latency = (s->latencyUsec == 0.0 ? "-" : boost::str(latFmt % (s->latencyUsec / 1000.0)));
908 const std::string latencytcp = (s->latencyUsecTCP == 0.0 ? "-" : boost::str(latFmt % (s->latencyUsecTCP / 1000.0)));
7f768697 909 if (showUUIDs) {
cbd17252 910 ret << (fmt % counter % s->getName() % s->d_config.remote.toStringWithPort() % status % s->queryLoad % s->qps.getRate() % s->d_config.order % s->d_config.d_weight % s->queries.load() % s->reuseds.load() % (s->dropRate) % latency % s->outstanding.load() % pools % *s->d_config.id % latencytcp) << endl;
7f768697 911 }
69108ebb 912 else {
cbd17252 913 ret << (fmt % counter % s->getName() % s->d_config.remote.toStringWithPort() % status % s->queryLoad % s->qps.getRate() % s->d_config.order % s->d_config.d_weight % s->queries.load() % s->reuseds.load() % (s->dropRate) % latency % s->outstanding.load() % pools % latencytcp) << endl;
69108ebb
RG
914 }
915 totQPS += s->queryLoad;
916 totQueries += s->queries.load();
917 totDrops += s->reuseds.load();
918 ++counter;
7f768697 919 }
69108ebb
RG
920 if (showUUIDs) {
921 ret << (fmt % "All" % "" % "" % ""
cbd17252 922 % (double)totQPS % "" % "" % "" % totQueries % totDrops % "" % "" % "" % "" % "" % "")
69108ebb
RG
923 << endl;
924 }
925 else {
926 ret << (fmt % "All" % "" % "" % ""
cbd17252 927 % (double)totQPS % "" % "" % "" % totQueries % totDrops % "" % "" % "" % "" % "")
69108ebb
RG
928 << endl;
929 }
930
931 g_outputBuffer = ret.str();
932 }
933 catch (std::exception& e) {
934 g_outputBuffer = e.what();
935 throw;
936 }
937 });
df111b53 938
fd51c832 939 luaCtx.writeFunction("getServers", []() {
69108ebb 940 setLuaNoSideEffect();
bfb79abc 941 LuaArray<std::shared_ptr<DownstreamState>> ret;
69108ebb
RG
942 int count = 1;
943 for (const auto& s : g_dstates.getCopy()) {
0b0882f5 944 ret.emplace_back(count++, s);
69108ebb
RG
945 }
946 return ret;
947 });
e27097e4 948
26512612 949 luaCtx.writeFunction("getPoolServers", [](const string& pool) {
69108ebb
RG
950 const auto poolServers = getDownstreamCandidates(g_pools.getCopy(), pool);
951 return *poolServers;
952 });
b1bec9f0 953
23a3bc71 954 luaCtx.writeFunction("getServer", [client](boost::variant<int, std::string> i) {
69108ebb 955 if (client) {
db5fbb86 956 return std::make_shared<DownstreamState>(ComboAddress());
69108ebb
RG
957 }
958 auto states = g_dstates.getCopy();
959 if (auto str = boost::get<std::string>(&i)) {
960 const auto uuid = getUniqueID(*str);
961 for (auto& state : states) {
8cf75ac2 962 if (*state->d_config.id == uuid) {
69108ebb 963 return state;
adfdcc4b
RG
964 }
965 }
69108ebb 966 }
23a3bc71 967 else if (auto pos = boost::get<int>(&i)) {
69108ebb
RG
968 return states.at(*pos);
969 }
adfdcc4b 970
69108ebb
RG
971 g_outputBuffer = "Error: no rule matched\n";
972 return std::shared_ptr<DownstreamState>(nullptr);
973 });
731774a8 974
ac4c3e1c 975#ifndef DISABLE_CARBON
23a3bc71 976 luaCtx.writeFunction("carbonServer", [](const std::string& address, boost::optional<string> ourName, boost::optional<uint64_t> interval, boost::optional<string> namespace_name, boost::optional<string> instance_name) {
69108ebb 977 setLuaSideEffect();
5552745f
RG
978 dnsdist::Carbon::Endpoint endpoint{ComboAddress(address, 2003),
979 (namespace_name && !namespace_name->empty()) ? *namespace_name : "dnsdist",
980 ourName ? *ourName : "",
981 (instance_name && !instance_name->empty()) ? *instance_name : "main",
982 (interval && *interval < std::numeric_limits<unsigned int>::max()) ? static_cast<unsigned int>(*interval) : 30};
983 dnsdist::Carbon::addEndpoint(std::move(endpoint));
69108ebb 984 });
ac4c3e1c 985#endif /* DISABLE_CARBON */
59b364f3 986
e2220927 987 luaCtx.writeFunction("webserver", [client, configCheck](const std::string& address) {
69108ebb
RG
988 setLuaSideEffect();
989 ComboAddress local;
990 try {
991 local = ComboAddress(address);
992 }
993 catch (const PDNSException& e) {
994 throw std::runtime_error(std::string("Error parsing the bind address for the webserver: ") + e.reason);
995 }
59b364f3 996
69108ebb
RG
997 if (client || configCheck) {
998 return;
999 }
fa7e8b5d 1000
69108ebb
RG
1001 try {
1002 int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
1003 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
1004 SBind(sock, local);
1005 SListen(sock, 5);
e2220927 1006 auto launch = [sock, local]() {
69108ebb
RG
1007 thread t(dnsdistWebserverThread, sock, local);
1008 t.detach();
1009 };
1010 if (g_launchWork) {
1011 g_launchWork->push_back(launch);
731774a8 1012 }
69108ebb
RG
1013 else {
1014 launch();
731774a8 1015 }
69108ebb
RG
1016 }
1017 catch (const std::exception& e) {
1018 g_outputBuffer = "Unable to bind to webserver socket on " + local.toStringWithPort() + ": " + e.what();
1019 errlog("Unable to bind to webserver socket on %s: %s", local.toStringWithPort(), e.what());
1020 }
1021 });
731774a8 1022
bfb79abc 1023 typedef LuaAssociativeTable<boost::variant<bool, std::string, LuaAssociativeTable<std::string>>> webserveropts_t;
32c97b56 1024
fd51c832 1025 luaCtx.writeFunction("setWebserverConfig", [](boost::optional<webserveropts_t> vars) {
69108ebb 1026 setLuaSideEffect();
fa7e8b5d 1027
69108ebb
RG
1028 if (!vars) {
1029 return;
1030 }
64c4f83c 1031
69108ebb 1032 bool hashPlaintextCredentials = false;
41366c40 1033 getOptionalValue<bool>(vars, "hashPlaintextCredentials", hashPlaintextCredentials);
32c97b56 1034
45f86d73
AT
1035 std::string password;
1036 std::string apiKey;
1037 std::string acl;
1038 LuaAssociativeTable<std::string> headers;
1039 bool statsRequireAuthentication{true};
1040 bool apiRequiresAuthentication{true};
1041 bool dashboardRequiresAuthentication{true};
41366c40 1042 int maxConcurrentConnections = 0;
45f86d73
AT
1043
1044 if (getOptionalValue<std::string>(vars, "password", password) > 0) {
69108ebb
RG
1045 auto holder = make_unique<CredentialsHolder>(std::move(password), hashPlaintextCredentials);
1046 if (!holder->wasHashed() && holder->isHashingAvailable()) {
1047 infolog("Passing a plain-text password via the 'password' parameter to 'setWebserverConfig()' is not advised, please consider generating a hashed one using 'hashPassword()' instead.");
32c97b56 1048 }
fa7e8b5d 1049
69108ebb
RG
1050 setWebserverPassword(std::move(holder));
1051 }
32c97b56 1052
45f86d73 1053 if (getOptionalValue<std::string>(vars, "apiKey", apiKey) > 0) {
69108ebb
RG
1054 auto holder = make_unique<CredentialsHolder>(std::move(apiKey), hashPlaintextCredentials);
1055 if (!holder->wasHashed() && holder->isHashingAvailable()) {
1056 infolog("Passing a plain-text API key via the 'apiKey' parameter to 'setWebserverConfig()' is not advised, please consider generating a hashed one using 'hashPassword()' instead.");
32c97b56 1057 }
fa7e8b5d 1058
69108ebb
RG
1059 setWebserverAPIKey(std::move(holder));
1060 }
1c90c6bd 1061
45f86d73 1062 if (getOptionalValue<std::string>(vars, "acl", acl) > 0) {
69108ebb
RG
1063 setWebserverACL(acl);
1064 }
32c97b56 1065
45f86d73 1066 if (getOptionalValue<decltype(headers)>(vars, "customHeaders", headers) > 0) {
69108ebb
RG
1067 setWebserverCustomHeaders(headers);
1068 }
5e40d2a5 1069
45f86d73
AT
1070 if (getOptionalValue<bool>(vars, "statsRequireAuthentication", statsRequireAuthentication) > 0) {
1071 setWebserverStatsRequireAuthentication(statsRequireAuthentication);
69108ebb
RG
1072 }
1073
45f86d73
AT
1074 if (getOptionalValue<bool>(vars, "apiRequiresAuthentication", apiRequiresAuthentication) > 0) {
1075 setWebserverAPIRequiresAuthentication(apiRequiresAuthentication);
80af53eb
RG
1076 }
1077
45f86d73
AT
1078 if (getOptionalValue<bool>(vars, "dashboardRequiresAuthentication", dashboardRequiresAuthentication) > 0) {
1079 setWebserverDashboardRequiresAuthentication(dashboardRequiresAuthentication);
4bbed5cf
RG
1080 }
1081
41366c40
RG
1082 if (getOptionalIntegerValue("setWebserverConfig", vars, "maxConcurrentConnections", maxConcurrentConnections) > 0) {
1083 setWebserverMaxConcurrentConnections(maxConcurrentConnections);
69108ebb
RG
1084 }
1085 });
80dbd7d2 1086
e3491311
RG
1087 luaCtx.writeFunction("showWebserverConfig", []() {
1088 setLuaNoSideEffect();
126dd2c8 1089 return getWebserverConfig();
e3491311
RG
1090 });
1091
4ec3ff03
RG
1092 luaCtx.writeFunction("hashPassword", [](const std::string& password, boost::optional<uint64_t> workFactor) {
1093 if (workFactor) {
1094 return hashPassword(password, *workFactor, CredentialsHolder::s_defaultParallelFactor, CredentialsHolder::s_defaultBlockSize);
1095 }
72bf42cd
RG
1096 return hashPassword(password);
1097 });
1098
69108ebb
RG
1099 luaCtx.writeFunction("controlSocket", [client, configCheck](const std::string& str) {
1100 setLuaSideEffect();
1101 ComboAddress local(str, 5199);
832c1792 1102
69108ebb
RG
1103 if (client || configCheck) {
1104 g_serverControl = local;
1105 return;
1106 }
731774a8 1107
69108ebb 1108 g_consoleEnabled = true;
9c9b4998 1109#ifdef HAVE_LIBSODIUM
69108ebb
RG
1110 if (g_configurationDone && g_consoleKey.empty()) {
1111 warnlog("Warning, the console has been enabled via 'controlSocket()' but no key has been set with 'setKey()' so all connections will fail until a key has been set");
1112 }
9c9b4998
RG
1113#endif
1114
69108ebb
RG
1115 try {
1116 int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
1117 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
1118 SBind(sock, local);
1119 SListen(sock, 5);
1120 auto launch = [sock, local]() {
1121 thread t(controlThread, sock, local);
1122 t.detach();
1123 };
1124 if (g_launchWork) {
1125 g_launchWork->push_back(launch);
6bb38cd6 1126 }
69108ebb
RG
1127 else {
1128 launch();
6bb38cd6 1129 }
69108ebb
RG
1130 }
1131 catch (std::exception& e) {
1132 g_outputBuffer = "Unable to bind to control socket on " + local.toStringWithPort() + ": " + e.what();
1133 errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what());
1134 }
1135 });
63beb26d 1136
fd51c832 1137 luaCtx.writeFunction("addConsoleACL", [](const std::string& netmask) {
69108ebb 1138 setLuaSideEffect();
b5521206 1139#ifndef HAVE_LIBSODIUM
69108ebb 1140 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
b5521206
RG
1141#endif
1142
69108ebb
RG
1143 g_consoleACL.modify([netmask](NetmaskGroup& nmg) { nmg.addMask(netmask); });
1144 });
b5521206 1145
bfb79abc 1146 luaCtx.writeFunction("setConsoleACL", [](LuaTypeOrArrayOf<std::string> inp) {
69108ebb 1147 setLuaSideEffect();
b5521206
RG
1148
1149#ifndef HAVE_LIBSODIUM
69108ebb 1150 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
b5521206
RG
1151#endif
1152
69108ebb
RG
1153 NetmaskGroup nmg;
1154 if (auto str = boost::get<string>(&inp)) {
1155 nmg.addMask(*str);
1156 }
1157 else
bfb79abc 1158 for (const auto& p : boost::get<LuaArray<std::string>>(inp)) {
69108ebb 1159 nmg.addMask(p.second);
b5521206 1160 }
69108ebb 1161 g_consoleACL.setState(nmg);
b5521206
RG
1162 });
1163
fd51c832 1164 luaCtx.writeFunction("showConsoleACL", []() {
69108ebb 1165 setLuaNoSideEffect();
b5521206
RG
1166
1167#ifndef HAVE_LIBSODIUM
69108ebb 1168 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
b5521206
RG
1169#endif
1170
25b3e633 1171 auto aclEntries = g_consoleACL.getLocal()->toStringVector();
b5521206 1172
25b3e633
RG
1173 for (const auto& entry : aclEntries) {
1174 g_outputBuffer += entry + "\n";
69108ebb
RG
1175 }
1176 });
b5521206 1177
23a3bc71 1178 luaCtx.writeFunction("setConsoleMaximumConcurrentConnections", [](uint64_t max) {
69108ebb
RG
1179 setLuaSideEffect();
1180 setConsoleMaximumConcurrentConnections(max);
24568984
RG
1181 });
1182
fd51c832 1183 luaCtx.writeFunction("clearQueryCounters", []() {
69108ebb
RG
1184 unsigned int size{0};
1185 {
1186 auto records = g_qcount.records.write_lock();
1187 size = records->size();
1188 records->clear();
1189 }
6eecd4c2 1190
69108ebb
RG
1191 boost::format fmt("%d records cleared from query counter buffer\n");
1192 g_outputBuffer = (fmt % size).str();
1193 });
2332b03c 1194
23a3bc71 1195 luaCtx.writeFunction("getQueryCounters", [](boost::optional<uint64_t> optMax) {
69108ebb
RG
1196 setLuaNoSideEffect();
1197 auto records = g_qcount.records.read_lock();
1198 g_outputBuffer = "query counting is currently: ";
1199 g_outputBuffer += g_qcount.enabled ? "enabled" : "disabled";
1200 g_outputBuffer += (boost::format(" (%d records in buffer)\n") % records->size()).str();
1201
1202 boost::format fmt("%-3d %s: %d request(s)\n");
23a3bc71
RG
1203 uint64_t max = optMax ? *optMax : 10U;
1204 uint64_t index{1};
69108ebb
RG
1205 for (auto it = records->begin(); it != records->end() && index <= max; ++it, ++index) {
1206 g_outputBuffer += (fmt % index % it->first % it->second).str();
1207 }
1208 });
808c5ef7 1209
69108ebb 1210 luaCtx.writeFunction("setQueryCount", [](bool enabled) { g_qcount.enabled = enabled; });
ae3dfa48 1211
fd51c832 1212 luaCtx.writeFunction("setQueryCountFilter", [](QueryCountFilter func) {
c92e6020 1213 g_qcount.filter = std::move(func);
69108ebb 1214 });
886e2cf2 1215
fd51c832 1216 luaCtx.writeFunction("makeKey", []() {
69108ebb
RG
1217 setLuaNoSideEffect();
1218 g_outputBuffer = "setKey(" + newKey() + ")\n";
1219 });
6bba426c 1220
fd51c832 1221 luaCtx.writeFunction("setKey", [](const std::string& key) {
69108ebb
RG
1222 if (!g_configurationDone && !g_consoleKey.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
1223 return; // but later setKeys() trump the -k value again
1224 }
b5521206 1225#ifndef HAVE_LIBSODIUM
69108ebb 1226 warnlog("Calling setKey() while libsodium support has not been enabled is not secure, and will result in cleartext communications");
b5521206 1227#endif
6bba426c 1228
69108ebb
RG
1229 setLuaSideEffect();
1230 string newkey;
1231 if (B64Decode(key, newkey) < 0) {
1232 g_outputBuffer = string("Unable to decode ") + key + " as Base64";
1233 errlog("%s", g_outputBuffer);
1234 }
1235 else
c43570b6 1236 g_consoleKey = std::move(newkey);
69108ebb 1237 });
2332b03c 1238
fd51c832 1239 luaCtx.writeFunction("clearConsoleHistory", []() {
69108ebb
RG
1240 clearConsoleHistory();
1241 });
87e63596 1242
69108ebb
RG
1243 luaCtx.writeFunction("testCrypto", [](boost::optional<string> optTestMsg) {
1244 setLuaNoSideEffect();
6bb38cd6 1245#ifdef HAVE_LIBSODIUM
69108ebb
RG
1246 try {
1247 string testmsg;
1248
1249 if (optTestMsg) {
1250 testmsg = *optTestMsg;
1251 }
1252 else {
1253 testmsg = "testStringForCryptoTests";
1254 }
1255
1256 SodiumNonce sn, sn2;
1257 sn.init();
1258 sn2 = sn;
1259 string encrypted = sodEncryptSym(testmsg, g_consoleKey, sn);
1260 string decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2);
1261
1262 sn.increment();
1263 sn2.increment();
1264
1265 encrypted = sodEncryptSym(testmsg, g_consoleKey, sn);
1266 decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2);
1267
1268 if (testmsg == decrypted)
1269 g_outputBuffer = "Everything is ok!\n";
1270 else
1271 g_outputBuffer = "Crypto failed.. (the decoded value does not match the cleartext one)\n";
1272 }
1273 catch (const std::exception& e) {
1274 g_outputBuffer = "Crypto failed: " + std::string(e.what()) + "\n";
1275 }
1276 catch (...) {
1277 g_outputBuffer = "Crypto failed..\n";
1278 }
6bb38cd6 1279#else
69108ebb 1280 g_outputBuffer = "Crypto not available.\n";
6bb38cd6 1281#endif
69108ebb 1282 });
b7860997 1283
69108ebb 1284 luaCtx.writeFunction("setTCPRecvTimeout", [](int timeout) { g_tcpRecvTimeout = timeout; });
89cb6f9a 1285
69108ebb 1286 luaCtx.writeFunction("setTCPSendTimeout", [](int timeout) { g_tcpSendTimeout = timeout; });
89cb6f9a 1287
7686bce9 1288 luaCtx.writeFunction("setUDPTimeout", [](int timeout) { DownstreamState::s_udpTimeout = timeout; });
d3826006 1289
4f699841 1290 luaCtx.writeFunction("setMaxUDPOutstanding", [](uint64_t max) {
f727b3c6
RG
1291 if (!checkConfigurationTime("setMaxUDPOutstanding")) {
1292 return;
69108ebb 1293 }
f727b3c6
RG
1294
1295 checkParameterBound("setMaxUDPOutstanding", max);
1296 g_maxOutstanding = max;
69108ebb 1297 });
55baa1f2 1298
fd51c832 1299 luaCtx.writeFunction("setMaxTCPClientThreads", [](uint64_t max) {
f727b3c6
RG
1300 if (!checkConfigurationTime("setMaxTCPClientThreads")) {
1301 return;
69108ebb 1302 }
f727b3c6 1303 g_maxTCPClientThreads = max;
69108ebb 1304 });
55baa1f2 1305
fd51c832 1306 luaCtx.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max) {
f727b3c6
RG
1307 if (!checkConfigurationTime("setMaxTCPQueuedConnections")) {
1308 return;
69108ebb 1309 }
f727b3c6 1310 g_maxTCPQueuedConnections = max;
69108ebb 1311 });
55baa1f2 1312
23a3bc71 1313 luaCtx.writeFunction("setMaxTCPQueriesPerConnection", [](uint64_t max) {
f727b3c6
RG
1314 if (!checkConfigurationTime("setMaxTCPQueriesPerConnection")) {
1315 return;
69108ebb 1316 }
f727b3c6 1317 g_maxTCPQueriesPerConn = max;
69108ebb 1318 });
57c61ce9 1319
23a3bc71 1320 luaCtx.writeFunction("setMaxTCPConnectionsPerClient", [](uint64_t max) {
f727b3c6
RG
1321 if (!checkConfigurationTime("setMaxTCPConnectionsPerClient")) {
1322 return;
69108ebb 1323 }
f727b3c6 1324 dnsdist::IncomingConcurrentTCPConnectionsManager::setMaxTCPConnectionsPerClient(max);
69108ebb 1325 });
57c61ce9 1326
23a3bc71 1327 luaCtx.writeFunction("setMaxTCPConnectionDuration", [](uint64_t max) {
f727b3c6
RG
1328 if (!checkConfigurationTime("setMaxTCPConnectionDuration")) {
1329 return;
69108ebb 1330 }
f727b3c6 1331 g_maxTCPConnectionDuration = max;
69108ebb 1332 });
788c3243 1333
23a3bc71 1334 luaCtx.writeFunction("setMaxCachedTCPConnectionsPerDownstream", [](uint64_t max) {
8a2dd7db 1335 setTCPDownstreamMaxIdleConnectionsPerBackend(max);
767f5514
RG
1336 });
1337
cf25b82b 1338#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
23a3bc71 1339 luaCtx.writeFunction("setMaxIdleDoHConnectionsPerDownstream", [](uint64_t max) {
54a9a226 1340 setDoHDownstreamMaxIdleConnectionsPerBackend(max);
69108ebb 1341 });
6d26db15 1342
168ef594 1343 luaCtx.writeFunction("setOutgoingDoHWorkerThreads", [](uint64_t workers) {
f727b3c6
RG
1344 if (!checkConfigurationTime("setOutgoingDoHWorkerThreads")) {
1345 return;
69108ebb 1346 }
f727b3c6 1347 g_outgoingDoHWorkerThreads = workers;
69108ebb 1348 });
cf25b82b 1349#endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
168ef594 1350
4f699841 1351 luaCtx.writeFunction("setOutgoingTLSSessionsCacheMaxTicketsPerBackend", [](uint64_t max) {
f727b3c6 1352 if (!checkConfigurationTime("setOutgoingTLSSessionsCacheMaxTicketsPerBackend")) {
65193dcf
RG
1353 return;
1354 }
1355 TLSSessionCache::setMaxTicketsPerBackend(max);
1356 });
1357
1358 luaCtx.writeFunction("setOutgoingTLSSessionsCacheCleanupDelay", [](time_t delay) {
f727b3c6 1359 if (!checkConfigurationTime("setOutgoingTLSSessionsCacheCleanupDelay")) {
65193dcf
RG
1360 return;
1361 }
1362 TLSSessionCache::setCleanupDelay(delay);
1363 });
1364
1365 luaCtx.writeFunction("setOutgoingTLSSessionsCacheMaxTicketValidity", [](time_t validity) {
f727b3c6 1366 if (!checkConfigurationTime("setOutgoingTLSSessionsCacheMaxTicketValidity")) {
65193dcf
RG
1367 return;
1368 }
1369 TLSSessionCache::setSessionValidity(validity);
1370 });
1371
4a62d2bf
RG
1372 luaCtx.writeFunction("getOutgoingTLSSessionCacheSize", []() {
1373 setLuaNoSideEffect();
1374 return g_sessionCache.getSize();
1375 });
1376
4f699841
RG
1377 luaCtx.writeFunction("setCacheCleaningDelay", [](uint64_t delay) {
1378 checkParameterBound("setCacheCleaningDelay", delay, std::numeric_limits<uint32_t>::max());
1379 g_cacheCleaningDelay = delay;
1380 });
0570f37c 1381
23a3bc71 1382 luaCtx.writeFunction("setCacheCleaningPercentage", [](uint64_t percentage) { if (percentage < 100) g_cacheCleaningPercentage = percentage; else g_cacheCleaningPercentage = 100; });
0570f37c 1383
23a3bc71
RG
1384 luaCtx.writeFunction("setECSSourcePrefixV4", [](uint64_t prefix) {
1385 checkParameterBound("setECSSourcePrefixV4", prefix, std::numeric_limits<uint16_t>::max());
1386 g_ECSSourcePrefixV4 = prefix;
1387 });
f39b7598 1388
23a3bc71
RG
1389 luaCtx.writeFunction("setECSSourcePrefixV6", [](uint64_t prefix) {
1390 checkParameterBound("setECSSourcePrefixV6", prefix, std::numeric_limits<uint16_t>::max());
1391 g_ECSSourcePrefixV6 = prefix;
1392 });
0570f37c 1393
69108ebb 1394 luaCtx.writeFunction("setECSOverride", [](bool override) { g_ECSOverride = override; });
832c1792 1395
3dec2251 1396#ifndef DISABLE_DYNBLOCKS
69108ebb
RG
1397 luaCtx.writeFunction("showDynBlocks", []() {
1398 setLuaNoSideEffect();
1399 auto slow = g_dynblockNMG.getCopy();
1400 struct timespec now;
1401 gettime(&now);
8959c0ad
RG
1402 boost::format fmt("%-24s %8d %8d %-10s %-20s %-10s %s\n");
1403 g_outputBuffer = (fmt % "What" % "Seconds" % "Blocks" % "Warning" % "Action" % "eBPF" % "Reason").str();
69108ebb
RG
1404 for (const auto& e : slow) {
1405 if (now < e.second.until) {
1406 uint64_t counter = e.second.blocks;
1407 if (g_defaultBPFFilter && e.second.bpf) {
1408 counter += g_defaultBPFFilter->getHits(e.first.getNetwork());
1409 }
8959c0ad 1410 g_outputBuffer += (fmt % e.first.toString() % (e.second.until.tv_sec - now.tv_sec) % counter % (e.second.warning ? "true" : "false") % DNSAction::typeToString(e.second.action != DNSAction::Action::None ? e.second.action : g_dynBlockAction) % (g_defaultBPFFilter && e.second.bpf ? "*" : "") % e.second.reason).str();
69108ebb
RG
1411 }
1412 }
1413 auto slow2 = g_dynblockSMT.getCopy();
1414 slow2.visit([&now, &fmt](const SuffixMatchTree<DynBlock>& node) {
1415 if (now < node.d_value.until) {
1416 string dom("empty");
1417 if (!node.d_value.domain.empty())
1418 dom = node.d_value.domain.toString();
8959c0ad 1419 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();
69108ebb 1420 }
520eb5a0 1421 });
69108ebb 1422 });
df111b53 1423
34c9e69c
RG
1424 luaCtx.writeFunction("getDynamicBlocks", []() {
1425 setLuaNoSideEffect();
0bad7da3
RG
1426 struct timespec now
1427 {
1428 };
34c9e69c
RG
1429 gettime(&now);
1430
1431 LuaAssociativeTable<DynBlock> entries;
1432 auto fullCopy = g_dynblockNMG.getCopy();
1433 for (const auto& blockPair : fullCopy) {
1434 const auto& requestor = blockPair.first;
1435 if (!(now < blockPair.second.until)) {
1436 continue;
1437 }
1438 auto entry = blockPair.second;
1439 if (g_defaultBPFFilter && entry.bpf) {
1440 entry.blocks += g_defaultBPFFilter->getHits(requestor.getNetwork());
1441 }
1442 if (entry.action == DNSAction::Action::None) {
1443 entry.action = g_dynBlockAction;
1444 }
1445 entries.emplace(requestor.toString(), std::move(entry));
6ae753a0 1446 }
34c9e69c
RG
1447 return entries;
1448 });
1449
58fc0ef5 1450 luaCtx.writeFunction("getDynamicBlocksSMT", []() {
34c9e69c 1451 setLuaNoSideEffect();
0bad7da3
RG
1452 struct timespec now
1453 {
1454 };
34c9e69c
RG
1455 gettime(&now);
1456
1457 LuaAssociativeTable<DynBlock> entries;
1458 auto fullCopy = g_dynblockSMT.getCopy();
1459 fullCopy.visit([&now, &entries](const SuffixMatchTree<DynBlock>& node) {
6ae753a0 1460 if (!(now < node.d_value.until)) {
34c9e69c
RG
1461 return;
1462 }
1463 auto entry = node.d_value;
6ae753a0 1464 string key("empty");
34c9e69c
RG
1465 if (!entry.domain.empty()) {
1466 key = entry.domain.toString();
1467 }
1468 if (entry.action == DNSAction::Action::None) {
1469 entry.action = g_dynBlockAction;
1470 }
1471 entries.emplace(std::move(key), std::move(entry));
1472 });
1473 return entries;
1474 });
1475
fd51c832 1476 luaCtx.writeFunction("clearDynBlocks", []() {
69108ebb
RG
1477 setLuaSideEffect();
1478 nmts_t nmg;
1479 g_dynblockNMG.setState(nmg);
1480 SuffixMatchTree<DynBlock> smt;
1481 g_dynblockSMT.setState(smt);
1482 });
6bb38cd6 1483
6ed31591 1484#ifndef DISABLE_DEPRECATED_DYNBLOCK
fd51c832 1485 luaCtx.writeFunction("addDynBlocks",
69108ebb
RG
1486 [](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) {
1487 if (m.empty()) {
1488 return;
1489 }
1490 setLuaSideEffect();
1491 auto slow = g_dynblockNMG.getCopy();
1492 struct timespec until, now;
1493 gettime(&now);
1494 until = now;
1495 int actualSeconds = seconds ? *seconds : 10;
1496 until.tv_sec += actualSeconds;
1497 for (const auto& capair : m) {
1498 unsigned int count = 0;
008c061d 1499 /* this legacy interface does not support ranges or ports, use DynBlockRulesGroup instead */
69108ebb
RG
1500 AddressAndPortRange requestor(capair.first, capair.first.isIPv4() ? 32 : 128, 0);
1501 auto got = slow.lookup(requestor);
1502 bool expired = false;
1503 if (got) {
1504 if (until < got->second.until) {
1505 // had a longer policy
1506 continue;
1507 }
1508 if (now < got->second.until) {
1509 // only inherit count on fresh query we are extending
1510 count = got->second.blocks;
c173228a 1511 }
69108ebb
RG
1512 else {
1513 expired = true;
1514 }
1515 }
1516 DynBlock db{msg, until, DNSName(), (action ? *action : DNSAction::Action::None)};
1517 db.blocks = count;
1518 if (!got || expired) {
1519 warnlog("Inserting dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg);
1520 }
c43570b6 1521 slow.insert(requestor).second = std::move(db);
69108ebb
RG
1522 }
1523 g_dynblockNMG.setState(slow);
1524 });
6bb38cd6 1525
fd51c832 1526 luaCtx.writeFunction("setDynBlocksAction", [](DNSAction::Action action) {
f727b3c6
RG
1527 if (!checkConfigurationTime("setDynBlocksAction")) {
1528 return;
1529 }
1530 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) {
1531 g_dynBlockAction = action;
69108ebb
RG
1532 }
1533 else {
f727b3c6
RG
1534 errlog("Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!");
1535 g_outputBuffer = "Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!\n";
69108ebb
RG
1536 }
1537 });
6ed31591 1538#endif /* DISABLE_DEPRECATED_DYNBLOCK */
832c1792 1539
fd51c832 1540 luaCtx.writeFunction("addDynBlockSMT",
bfb79abc 1541 [](const LuaArray<std::string>& names, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
69108ebb
RG
1542 if (names.empty()) {
1543 return;
1544 }
1545 setLuaSideEffect();
0bad7da3
RG
1546 struct timespec now
1547 {
1548 };
69108ebb 1549 gettime(&now);
6ae753a0 1550 unsigned int actualSeconds = seconds ? *seconds : 10;
69108ebb 1551
6ae753a0
RG
1552 bool needUpdate = false;
1553 auto slow = g_dynblockSMT.getCopy();
69108ebb 1554 for (const auto& capair : names) {
69108ebb
RG
1555 DNSName domain(capair.second);
1556 domain.makeUsLowerCase();
c9551e0d 1557
6ae753a0
RG
1558 if (dnsdist::DynamicBlocks::addOrRefreshBlockSMT(slow, now, domain, msg, actualSeconds, action ? *action : DNSAction::Action::None, false)) {
1559 needUpdate = true;
6ddb008a 1560 }
6ae753a0 1561 }
69108ebb 1562
6ae753a0
RG
1563 if (needUpdate) {
1564 g_dynblockSMT.setState(slow);
69108ebb 1565 }
69108ebb 1566 });
6bb38cd6 1567
c9551e0d
RG
1568 luaCtx.writeFunction("addDynamicBlock",
1569 [](const boost::variant<ComboAddress, std::string>& clientIP, const std::string& msg, const boost::optional<DNSAction::Action> action, const boost::optional<int> seconds, boost::optional<uint8_t> clientIPMask, boost::optional<uint8_t> clientIPPortMask) {
6ae753a0 1570 setLuaSideEffect();
c9551e0d 1571
6ae753a0
RG
1572 ComboAddress clientIPCA;
1573 if (clientIP.type() == typeid(ComboAddress)) {
1574 clientIPCA = boost::get<ComboAddress>(clientIP);
1575 }
1576 else {
89f3e9ac 1577 const auto& clientIPStr = boost::get<std::string>(clientIP);
6ae753a0
RG
1578 try {
1579 clientIPCA = ComboAddress(clientIPStr);
1580 }
1581 catch (const std::exception& exp) {
1582 errlog("addDynamicBlock: Unable to parse '%s': %s", clientIPStr, exp.what());
1583 return;
1584 }
1585 catch (const PDNSException& exp) {
1586 errlog("addDynamicBlock: Unable to parse '%s': %s", clientIPStr, exp.reason);
1587 return;
1588 }
1589 }
1590 AddressAndPortRange target(clientIPCA, clientIPMask ? *clientIPMask : (clientIPCA.isIPv4() ? 32 : 128), clientIPPortMask ? *clientIPPortMask : 0);
1591 unsigned int actualSeconds = seconds ? *seconds : 10;
c9551e0d 1592
0bad7da3
RG
1593 struct timespec now
1594 {
1595 };
6ae753a0
RG
1596 gettime(&now);
1597 auto slow = g_dynblockNMG.getCopy();
1598 if (dnsdist::DynamicBlocks::addOrRefreshBlock(slow, now, target, msg, actualSeconds, action ? *action : DNSAction::Action::None, false, false)) {
1599 g_dynblockNMG.setState(slow);
1600 }
1601 });
832c1792 1602
23a3bc71 1603 luaCtx.writeFunction("setDynBlocksPurgeInterval", [](uint64_t interval) {
b03d051c
RG
1604 DynBlockMaintenance::s_expiredDynBlocksPurgeInterval = interval;
1605 });
3dec2251 1606#endif /* DISABLE_DYNBLOCKS */
b03d051c 1607
393ed488 1608#ifdef HAVE_DNSCRYPT
bfb79abc 1609 luaCtx.writeFunction("addDNSCryptBind", [](const std::string& addr, const std::string& providerName, LuaTypeOrArrayOf<std::string> certFiles, LuaTypeOrArrayOf<std::string> keyFiles, boost::optional<localbind_t> vars) {
f727b3c6 1610 if (!checkConfigurationTime("addDNSCryptBind")) {
69108ebb
RG
1611 return;
1612 }
69108ebb
RG
1613 bool reusePort = false;
1614 int tcpFastOpenQueueSize = 0;
1615 int tcpListenQueueSize = 0;
393ed488
RG
1616 uint64_t maxInFlightQueriesPerConn = 0;
1617 uint64_t tcpMaxConcurrentConnections = 0;
69108ebb
RG
1618 std::string interface;
1619 std::set<int> cpus;
1620 std::vector<DNSCryptContext::CertKeyPaths> certKeys;
36800a60 1621 bool enableProxyProtocol = true;
df111b53 1622
36800a60 1623 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol);
45f86d73 1624 checkAllParametersConsumed("addDNSCryptBind", vars);
df111b53 1625
69108ebb
RG
1626 if (certFiles.type() == typeid(std::string) && keyFiles.type() == typeid(std::string)) {
1627 auto certFile = boost::get<std::string>(certFiles);
1628 auto keyFile = boost::get<std::string>(keyFiles);
c92e6020 1629 certKeys.push_back({std::move(certFile), std::move(keyFile)});
69108ebb 1630 }
bfb79abc
RG
1631 else if (certFiles.type() == typeid(LuaArray<std::string>) && keyFiles.type() == typeid(LuaArray<std::string>)) {
1632 auto certFilesVect = boost::get<LuaArray<std::string>>(certFiles);
1633 auto keyFilesVect = boost::get<LuaArray<std::string>>(keyFiles);
69108ebb
RG
1634 if (certFilesVect.size() == keyFilesVect.size()) {
1635 for (size_t idx = 0; idx < certFilesVect.size(); idx++) {
1636 certKeys.push_back({certFilesVect.at(idx).second, keyFilesVect.at(idx).second});
37b6d73d
RG
1637 }
1638 }
1639 else {
69108ebb
RG
1640 errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind!");
1641 g_outputBuffer = "Error, mismatching number of certificates and keys in call to addDNSCryptBind()!";
37b6d73d
RG
1642 return;
1643 }
69108ebb
RG
1644 }
1645 else {
1646 errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind()!");
1647 g_outputBuffer = "Error, mismatching number of certificates and keys in call to addDNSCryptBind()!";
1648 return;
1649 }
37b6d73d 1650
69108ebb
RG
1651 try {
1652 auto ctx = std::make_shared<DNSCryptContext>(providerName, certKeys);
1653
1654 /* UDP */
36800a60 1655 auto clientState = std::make_unique<ClientState>(ComboAddress(addr, 443), false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol);
c114cd18 1656 clientState->dnscryptCtx = ctx;
69108ebb 1657 g_dnsCryptLocals.push_back(ctx);
c114cd18 1658 g_frontends.push_back(std::move(clientState));
c58723d7 1659
69108ebb 1660 /* TCP */
36800a60 1661 clientState = std::make_unique<ClientState>(ComboAddress(addr, 443), true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol);
c114cd18 1662 clientState->dnscryptCtx = std::move(ctx);
69108ebb 1663 if (tcpListenQueueSize > 0) {
c114cd18 1664 clientState->tcpListenQueueSize = tcpListenQueueSize;
df111b53 1665 }
69108ebb 1666 if (maxInFlightQueriesPerConn > 0) {
c114cd18 1667 clientState->d_maxInFlightQueriesPerConn = maxInFlightQueriesPerConn;
6bb38cd6 1668 }
69108ebb 1669 if (tcpMaxConcurrentConnections > 0) {
c114cd18 1670 clientState->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections;
69108ebb
RG
1671 }
1672
c114cd18 1673 g_frontends.push_back(std::move(clientState));
69108ebb 1674 }
f8e1161f
RG
1675 catch (const std::exception& e) {
1676 errlog("Error during addDNSCryptBind() processing: %s", e.what());
1677 g_outputBuffer = "Error during addDNSCryptBind() processing: " + string(e.what()) + "\n";
69108ebb 1678 }
69108ebb 1679 });
df111b53 1680
fd51c832 1681 luaCtx.writeFunction("showDNSCryptBinds", []() {
69108ebb 1682 setLuaNoSideEffect();
69108ebb
RG
1683 ostringstream ret;
1684 boost::format fmt("%1$-3d %2% %|25t|%3$-20.20s");
1685 ret << (fmt % "#" % "Address" % "Provider Name") << endl;
1686 size_t idx = 0;
1687
1688 std::unordered_set<std::shared_ptr<DNSCryptContext>> contexts;
1689 for (const auto& frontend : g_frontends) {
1690 const std::shared_ptr<DNSCryptContext> ctx = frontend->dnscryptCtx;
1691 if (!ctx || contexts.count(ctx) != 0) {
1692 continue;
1693 }
1694 contexts.insert(ctx);
1695 ret << (fmt % idx % frontend->local.toStringWithPort() % ctx->getProviderName()) << endl;
1696 idx++;
1697 }
df111b53 1698
69108ebb 1699 g_outputBuffer = ret.str();
69108ebb 1700 });
df111b53 1701
23a3bc71 1702 luaCtx.writeFunction("getDNSCryptBind", [](uint64_t idx) {
69108ebb 1703 setLuaNoSideEffect();
69108ebb
RG
1704 std::shared_ptr<DNSCryptContext> ret = nullptr;
1705 if (idx < g_dnsCryptLocals.size()) {
1706 ret = g_dnsCryptLocals.at(idx);
1707 }
1708 return ret;
69108ebb 1709 });
d92708ed 1710
fd51c832 1711 luaCtx.writeFunction("getDNSCryptBindCount", []() {
69108ebb
RG
1712 setLuaNoSideEffect();
1713 return g_dnsCryptLocals.size();
1714 });
393ed488 1715#endif /* HAVE_DNSCRYPT */
265260f5 1716
fd51c832 1717 luaCtx.writeFunction("showPools", []() {
69108ebb
RG
1718 setLuaNoSideEffect();
1719 try {
1720 ostringstream ret;
1721 boost::format fmt("%1$-20.20s %|25t|%2$20s %|25t|%3$20s %|50t|%4%");
1722 // 1 2 3 4
1723 ret << (fmt % "Name" % "Cache" % "ServerPolicy" % "Servers") << endl;
1724
1725 const auto localPools = g_pools.getCopy();
1726 for (const auto& entry : localPools) {
1727 const string& name = entry.first;
1728 const std::shared_ptr<ServerPool> pool = entry.second;
1729 string cache = pool->packetCache != nullptr ? pool->packetCache->toString() : "";
1730 string policy = g_policy.getLocal()->getName();
1731 if (pool->policy != nullptr) {
1732 policy = pool->policy->getName();
1733 }
1734 string servers;
1735
1736 const auto poolServers = pool->getServers();
1737 for (const auto& server : *poolServers) {
1738 if (!servers.empty()) {
1739 servers += ", ";
6bb38cd6 1740 }
69108ebb
RG
1741 if (!server.second->getName().empty()) {
1742 servers += server.second->getName();
1743 servers += " ";
6bb38cd6 1744 }
8cf75ac2 1745 servers += server.second->d_config.remote.toStringWithPort();
6bb38cd6 1746 }
6bb38cd6 1747
69108ebb 1748 ret << (fmt % name % cache % policy % servers) << endl;
df111b53 1749 }
69108ebb
RG
1750 g_outputBuffer = ret.str();
1751 }
1752 catch (std::exception& e) {
1753 g_outputBuffer = e.what();
1754 throw;
1755 }
1756 });
1757
377c1200
CC
1758 luaCtx.writeFunction("getPoolNames", []() {
1759 setLuaNoSideEffect();
1760 LuaArray<std::string> ret;
7e4b9d37 1761 int count = 1;
377c1200
CC
1762 const auto localPools = g_pools.getCopy();
1763 for (const auto& entry : localPools) {
1764 const string& name = entry.first;
0b0882f5 1765 ret.emplace_back(count++, name);
377c1200
CC
1766 }
1767 return ret;
1768 });
1769
69108ebb
RG
1770 luaCtx.writeFunction("getPool", [client](const string& poolName) {
1771 if (client) {
1772 return std::make_shared<ServerPool>();
1773 }
1774 auto localPools = g_pools.getCopy();
1775 std::shared_ptr<ServerPool> pool = createPoolIfNotExists(localPools, poolName);
1776 g_pools.setState(localPools);
1777 return pool;
1778 });
df111b53 1779
89d0e8ce 1780 luaCtx.writeFunction("setVerbose", [](bool verbose) { g_verbose = verbose; });
e134f491 1781 luaCtx.writeFunction("getVerbose", []() { return g_verbose; });
69108ebb 1782 luaCtx.writeFunction("setVerboseHealthChecks", [](bool verbose) { g_verboseHealthChecks = verbose; });
4935d3ca 1783 luaCtx.writeFunction("setVerboseLogDestination", [](const std::string& dest) {
f727b3c6 1784 if (!checkConfigurationTime("setVerboseLogDestination")) {
4935d3ca
RG
1785 return;
1786 }
1787 try {
1788 auto stream = std::ofstream(dest.c_str());
86e28d2d 1789 dnsdist::logging::LoggingConfiguration::setVerboseStream(std::move(stream));
4935d3ca
RG
1790 }
1791 catch (const std::exception& e) {
1792 errlog("Error while opening the verbose logging destination file %s: %s", dest, e.what());
1793 }
1794 });
86e28d2d
RG
1795 luaCtx.writeFunction("setStructuredLogging", [](bool enable, boost::optional<LuaAssociativeTable<std::string>> options) {
1796 std::string levelPrefix;
1797 std::string timeFormat;
1798 if (options) {
1799 getOptionalValue<std::string>(options, "levelPrefix", levelPrefix);
1800 if (getOptionalValue<std::string>(options, "timeFormat", timeFormat) == 1) {
1801 if (timeFormat == "numeric") {
1802 dnsdist::logging::LoggingConfiguration::setStructuredTimeFormat(dnsdist::logging::LoggingConfiguration::TimeFormat::Numeric);
1803 }
1804 else if (timeFormat == "ISO8601") {
1805 dnsdist::logging::LoggingConfiguration::setStructuredTimeFormat(dnsdist::logging::LoggingConfiguration::TimeFormat::ISO8601);
1806 }
1807 else {
1808 warnlog("Unknown value '%s' to setStructuredLogging's 'timeFormat' parameter", timeFormat);
1809 }
1810 }
1811 checkAllParametersConsumed("setStructuredLogging", options);
1812 }
1813
1814 dnsdist::logging::LoggingConfiguration::setStructuredLogging(enable, levelPrefix);
1815 });
89d0e8ce 1816
4f699841
RG
1817 luaCtx.writeFunction("setStaleCacheEntriesTTL", [](uint64_t ttl) {
1818 checkParameterBound("setStaleCacheEntriesTTL", ttl, std::numeric_limits<uint32_t>::max());
1819 g_staleCacheEntriesTTL = ttl;
1820 });
03ebf8b2 1821
fd51c832 1822 luaCtx.writeFunction("showBinds", []() {
69108ebb
RG
1823 setLuaNoSideEffect();
1824 try {
1825 ostringstream ret;
1826 boost::format fmt("%1$-3d %2$-20.20s %|35t|%3$-20.20s %|57t|%4%");
1827 // 1 2 3 4
1828 ret << (fmt % "#" % "Address" % "Protocol" % "Queries") << endl;
0e5b3cff 1829
69108ebb
RG
1830 size_t counter = 0;
1831 for (const auto& front : g_frontends) {
1832 ret << (fmt % counter % front->local.toStringWithPort() % front->getType() % front->queries) << endl;
1833 counter++;
df111b53 1834 }
69108ebb
RG
1835 g_outputBuffer = ret.str();
1836 }
1837 catch (std::exception& e) {
1838 g_outputBuffer = e.what();
1839 throw;
1840 }
1841 });
1842
23a3bc71 1843 luaCtx.writeFunction("getBind", [](uint64_t num) {
69108ebb
RG
1844 setLuaNoSideEffect();
1845 ClientState* ret = nullptr;
1846 if (num < g_frontends.size()) {
1847 ret = g_frontends[num].get();
1848 }
1849 return ret;
1850 });
df111b53 1851
fd51c832 1852 luaCtx.writeFunction("getBindCount", []() {
69108ebb
RG
1853 setLuaNoSideEffect();
1854 return g_frontends.size();
1855 });
265260f5 1856
fd51c832 1857 luaCtx.writeFunction("help", [](boost::optional<std::string> command) {
69108ebb
RG
1858 setLuaNoSideEffect();
1859 g_outputBuffer = "";
2ff9703a 1860#ifndef DISABLE_COMPLETION
69108ebb
RG
1861 for (const auto& keyword : g_consoleKeywords) {
1862 if (!command) {
1863 g_outputBuffer += keyword.toString() + "\n";
1864 }
1865 else if (keyword.name == command) {
1866 g_outputBuffer = keyword.toString() + "\n";
1867 return;
786e4d8c 1868 }
69108ebb 1869 }
2ff9703a 1870#endif /* DISABLE_COMPLETION */
69108ebb
RG
1871 if (command) {
1872 g_outputBuffer = "Nothing found for " + *command + "\n";
1873 }
1874 });
520eb5a0 1875
fd51c832 1876 luaCtx.writeFunction("showVersion", []() {
69108ebb
RG
1877 setLuaNoSideEffect();
1878 g_outputBuffer = "dnsdist " + std::string(VERSION) + "\n";
1879 });
786e4d8c 1880
6bb38cd6 1881#ifdef HAVE_EBPF
fd51c832 1882 luaCtx.writeFunction("setDefaultBPFFilter", [](std::shared_ptr<BPFFilter> bpf) {
f727b3c6 1883 if (!checkConfigurationTime("setDefaultBPFFilter")) {
69108ebb
RG
1884 return;
1885 }
c92e6020 1886 g_defaultBPFFilter = std::move(bpf);
69108ebb 1887 });
62edea30 1888
fd51c832 1889 luaCtx.writeFunction("registerDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
69108ebb 1890 if (dbpf) {
c92e6020 1891 g_dynBPFFilters.push_back(std::move(dbpf));
69108ebb
RG
1892 }
1893 });
f5b58807 1894
fd51c832 1895 luaCtx.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
69108ebb
RG
1896 if (dbpf) {
1897 for (auto it = g_dynBPFFilters.begin(); it != g_dynBPFFilters.end(); it++) {
1898 if (*it == dbpf) {
1899 g_dynBPFFilters.erase(it);
1900 break;
6bb38cd6
RG
1901 }
1902 }
69108ebb
RG
1903 }
1904 });
df111b53 1905
3dec2251 1906#ifndef DISABLE_DYNBLOCKS
6ed31591 1907#ifndef DISABLE_DEPRECATED_DYNBLOCK
69108ebb
RG
1908 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) {
1909 if (!dynbpf) {
1910 return;
1911 }
1912 setLuaSideEffect();
1913 struct timespec until, now;
1914 clock_gettime(CLOCK_MONOTONIC, &now);
1915 until = now;
1916 int actualSeconds = seconds ? *seconds : 10;
1917 until.tv_sec += actualSeconds;
1918 for (const auto& capair : m) {
1919 if (dynbpf->block(capair.first, until)) {
1920 warnlog("Inserting eBPF dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg ? *msg : "");
6bb38cd6 1921 }
69108ebb
RG
1922 }
1923 });
6ed31591 1924#endif /* DISABLE_DEPRECATED_DYNBLOCK */
3dec2251 1925#endif /* DISABLE_DYNBLOCKS */
62edea30 1926
6bb38cd6 1927#endif /* HAVE_EBPF */
2d11d1b2 1928
bfb79abc 1929 luaCtx.writeFunction<LuaAssociativeTable<uint64_t>()>("getStatisticsCounters", []() {
69108ebb
RG
1930 setLuaNoSideEffect();
1931 std::unordered_map<string, uint64_t> res;
54c1bc22 1932 {
6ba8d6ca 1933 auto entries = dnsdist::metrics::g_stats.entries.read_lock();
54c1bc22
RG
1934 res.reserve(entries->size());
1935 for (const auto& entry : *entries) {
6ba8d6ca 1936 if (const auto& val = std::get_if<pdns::stat_t*>(&entry.d_value)) {
54c1bc22
RG
1937 res[entry.d_name] = (*val)->load();
1938 }
1939 }
69108ebb
RG
1940 }
1941 return res;
1942 });
df111b53 1943
fd51c832 1944 luaCtx.writeFunction("includeDirectory", [&luaCtx](const std::string& dirname) {
f727b3c6 1945 if (!checkConfigurationTime("includeDirectory")) {
69108ebb
RG
1946 return;
1947 }
69108ebb
RG
1948 if (g_included) {
1949 errlog("includeDirectory() cannot be used recursively!");
1950 g_outputBuffer = "includeDirectory() cannot be used recursively!\n";
1951 return;
1952 }
e12b3374 1953
69108ebb
RG
1954 struct stat st;
1955 if (stat(dirname.c_str(), &st)) {
1956 errlog("The included directory %s does not exist!", dirname.c_str());
1957 g_outputBuffer = "The included directory " + dirname + " does not exist!";
1958 return;
1959 }
df111b53 1960
69108ebb
RG
1961 if (!S_ISDIR(st.st_mode)) {
1962 errlog("The included directory %s is not a directory!", dirname.c_str());
1963 g_outputBuffer = "The included directory " + dirname + " is not a directory!";
1964 return;
1965 }
6f6b4d69 1966
bfb79abc 1967 std::vector<std::string> files;
d3190b7d
RG
1968 auto directoryError = pdns::visit_directory(dirname, [&dirname, &files]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) {
1969 if (boost::starts_with(name, ".")) {
1970 return true;
6f6b4d69 1971 }
d3190b7d
RG
1972 if (boost::ends_with(name, ".conf")) {
1973 std::ostringstream namebuf;
1974 namebuf << dirname << "/" << name;
1975 struct stat fileStat
1976 {
1977 };
1978 if (stat(namebuf.str().c_str(), &fileStat) == 0 && S_ISREG(fileStat.st_mode)) {
694c668c
RG
1979 files.push_back(namebuf.str());
1980 }
e41f8165 1981 }
d3190b7d
RG
1982 return true;
1983 });
1984
1985 if (directoryError) {
1986 errlog("Error opening included directory: %s!", *directoryError);
1987 g_outputBuffer = "Error opening included directory: " + *directoryError + "!";
1988 return;
69108ebb 1989 }
e41f8165 1990
bfb79abc 1991 std::sort(files.begin(), files.end());
6bb38cd6 1992
69108ebb 1993 g_included = true;
b2fd93dc 1994
bfb79abc
RG
1995 for (const auto& file : files) {
1996 std::ifstream ifs(file);
69108ebb 1997 if (!ifs) {
bfb79abc 1998 warnlog("Unable to read configuration from '%s'", file);
69108ebb
RG
1999 }
2000 else {
bfb79abc 2001 vinfolog("Read configuration from '%s'", file);
69108ebb 2002 }
26a6373d 2003
69108ebb
RG
2004 try {
2005 luaCtx.executeCode(ifs);
2006 }
2007 catch (...) {
2008 g_included = false;
2009 throw;
741ebe08 2010 }
26a6373d 2011
69108ebb
RG
2012 luaCtx.executeCode(ifs);
2013 }
2014
2015 g_included = false;
2016 });
26a6373d 2017
fd51c832 2018 luaCtx.writeFunction("setAPIWritable", [](bool writable, boost::optional<std::string> apiConfigDir) {
69108ebb
RG
2019 setLuaSideEffect();
2020 g_apiReadWrite = writable;
2021 if (apiConfigDir) {
2022 if (!(*apiConfigDir).empty()) {
2023 g_apiConfigDirectory = *apiConfigDir;
741ebe08 2024 }
69108ebb
RG
2025 else {
2026 errlog("The API configuration directory value cannot be empty!");
2027 g_outputBuffer = "The API configuration directory value cannot be empty!";
2028 }
2029 }
2030 });
26a6373d 2031
fd51c832 2032 luaCtx.writeFunction("setServFailWhenNoServer", [](bool servfail) {
69108ebb
RG
2033 setLuaSideEffect();
2034 g_servFailOnNoPolicy = servfail;
2035 });
26a6373d 2036
fd51c832 2037 luaCtx.writeFunction("setRoundRobinFailOnNoServer", [](bool fail) {
69108ebb
RG
2038 setLuaSideEffect();
2039 g_roundrobinFailOnNoServer = fail;
2040 });
32b86928 2041
fd51c832 2042 luaCtx.writeFunction("setConsistentHashingBalancingFactor", [](double factor) {
69108ebb
RG
2043 setLuaSideEffect();
2044 if (factor >= 1.0) {
2045 g_consistentHashBalancingFactor = factor;
2046 }
2047 else {
2048 errlog("Invalid value passed to setConsistentHashingBalancingFactor()!");
2049 g_outputBuffer = "Invalid value passed to setConsistentHashingBalancingFactor()!\n";
2050 return;
2051 }
2052 });
2b4287d4 2053
fd51c832 2054 luaCtx.writeFunction("setWeightedBalancingFactor", [](double factor) {
69108ebb
RG
2055 setLuaSideEffect();
2056 if (factor >= 1.0) {
2057 g_weightedBalancingFactor = factor;
2058 }
2059 else {
2060 errlog("Invalid value passed to setWeightedBalancingFactor()!");
2061 g_outputBuffer = "Invalid value passed to setWeightedBalancingFactor()!\n";
2062 return;
2063 }
2064 });
439de524 2065
23a3bc71 2066 luaCtx.writeFunction("setRingBuffersSize", [client](uint64_t capacity, boost::optional<uint64_t> numberOfShards) {
69108ebb 2067 setLuaSideEffect();
f727b3c6 2068 if (!checkConfigurationTime("setRingBuffersSize")) {
69108ebb
RG
2069 return;
2070 }
2071 if (!client) {
2072 g_rings.setCapacity(capacity, numberOfShards ? *numberOfShards : 10);
2073 }
2074 else {
2075 g_rings.setCapacity(0, 1);
2076 }
2077 });
26a6373d 2078
23a3bc71 2079 luaCtx.writeFunction("setRingBuffersLockRetries", [](uint64_t retries) {
69108ebb
RG
2080 setLuaSideEffect();
2081 g_rings.setNumberOfLockRetries(retries);
2082 });
26a6373d 2083
0619a17f
RG
2084 luaCtx.writeFunction("setRingBuffersOptions", [](const LuaAssociativeTable<boost::variant<bool, uint64_t>>& options) {
2085 setLuaSideEffect();
f727b3c6 2086 if (!checkConfigurationTime("setRingBuffersOptions")) {
0619a17f
RG
2087 return;
2088 }
2089 if (options.count("lockRetries") > 0) {
2090 auto retries = boost::get<uint64_t>(options.at("lockRetries"));
2091 g_rings.setNumberOfLockRetries(retries);
2092 }
2093 if (options.count("recordQueries") > 0) {
2094 auto record = boost::get<bool>(options.at("recordQueries"));
2095 g_rings.setRecordQueries(record);
2096 }
2097 if (options.count("recordResponses") > 0) {
2098 auto record = boost::get<bool>(options.at("recordResponses"));
2099 g_rings.setRecordResponses(record);
2100 }
2101 });
2102
4f699841 2103 luaCtx.writeFunction("setWHashedPertubation", [](uint64_t perturb) {
69108ebb 2104 setLuaSideEffect();
4f699841
RG
2105 checkParameterBound("setWHashedPertubation", perturb, std::numeric_limits<uint32_t>::max());
2106 g_hashperturb = perturb;
69108ebb 2107 });
26a6373d 2108
23a3bc71 2109 luaCtx.writeFunction("setTCPInternalPipeBufferSize", [](uint64_t size) { g_tcpInternalPipeBufferSize = size; });
be5e1fca
Y
2110 luaCtx.writeFunction("setTCPFastOpenKey", [](const std::string& keyString) {
2111 setLuaSideEffect();
caaa8dbd 2112 uint32_t key[4] = {};
4b0c0aac 2113 auto ret = sscanf(keyString.c_str(), "%" SCNx32 "-%" SCNx32 "-%" SCNx32 "-%" SCNx32, &key[0], &key[1], &key[2], &key[3]);
be5e1fca
Y
2114 if (ret != 4) {
2115 g_outputBuffer = "Invalid value passed to setTCPFastOpenKey()!\n";
2116 return;
2117 }
caaa8dbd 2118 extern vector<uint32_t> g_TCPFastOpenKey;
be5e1fca
Y
2119 for (const auto i : key) {
2120 g_TCPFastOpenKey.push_back(i);
2121 }
2122 });
4ce513a4 2123
ed57a277 2124#ifdef HAVE_NET_SNMP
69108ebb 2125 luaCtx.writeFunction("snmpAgent", [client, configCheck](bool enableTraps, boost::optional<std::string> daemonSocket) {
f727b3c6 2126 if (client || configCheck) {
69108ebb 2127 return;
f727b3c6
RG
2128 }
2129 if (!checkConfigurationTime("snmpAgent")) {
69108ebb
RG
2130 return;
2131 }
69108ebb
RG
2132 if (g_snmpEnabled) {
2133 errlog("snmpAgent() cannot be used twice!");
2134 g_outputBuffer = "snmpAgent() cannot be used twice!\n";
2135 return;
2136 }
6bb38cd6 2137
69108ebb
RG
2138 g_snmpEnabled = true;
2139 g_snmpTrapsEnabled = enableTraps;
2140 g_snmpAgent = new DNSDistSNMPAgent("dnsdist", daemonSocket ? *daemonSocket : std::string());
69108ebb 2141 });
497a6e3a 2142
fd51c832 2143 luaCtx.writeFunction("sendCustomTrap", [](const std::string& str) {
69108ebb
RG
2144 if (g_snmpAgent && g_snmpTrapsEnabled) {
2145 g_snmpAgent->sendCustomTrap(str);
2146 }
2147 });
ed57a277 2148#endif /* HAVE_NET_SNMP */
6beb5731 2149
393ed488 2150#ifndef DISABLE_POLICIES_BINDINGS
4caba288 2151 luaCtx.writeFunction("setServerPolicy", [](const ServerPolicy& policy) {
69108ebb
RG
2152 setLuaSideEffect();
2153 g_policy.setState(policy);
2154 });
be05aa91 2155
26512612 2156 luaCtx.writeFunction("setServerPolicyLua", [](const string& name, ServerPolicy::policyfunc_t policy) {
69108ebb
RG
2157 setLuaSideEffect();
2158 g_policy.setState(ServerPolicy{name, policy, true});
2159 });
be05aa91 2160
26512612 2161 luaCtx.writeFunction("setServerPolicyLuaFFI", [](const string& name, ServerPolicy::ffipolicyfunc_t policy) {
69108ebb
RG
2162 setLuaSideEffect();
2163 auto pol = ServerPolicy(name, policy);
2164 g_policy.setState(std::move(pol));
2165 });
be05aa91 2166
26512612 2167 luaCtx.writeFunction("setServerPolicyLuaFFIPerThread", [](const string& name, const std::string& policyCode) {
69108ebb
RG
2168 setLuaSideEffect();
2169 auto pol = ServerPolicy(name, policyCode);
2170 g_policy.setState(std::move(pol));
2171 });
be05aa91 2172
fd51c832 2173 luaCtx.writeFunction("showServerPolicy", []() {
69108ebb
RG
2174 setLuaSideEffect();
2175 g_outputBuffer = g_policy.getLocal()->getName() + "\n";
2176 });
fd51c832 2177
26512612 2178 luaCtx.writeFunction("setPoolServerPolicy", [](ServerPolicy policy, const string& pool) {
69108ebb
RG
2179 setLuaSideEffect();
2180 auto localPools = g_pools.getCopy();
c92e6020 2181 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(std::move(policy)));
69108ebb
RG
2182 g_pools.setState(localPools);
2183 });
e41f8165 2184
26512612 2185 luaCtx.writeFunction("setPoolServerPolicyLua", [](const string& name, ServerPolicy::policyfunc_t policy, const string& pool) {
69108ebb
RG
2186 setLuaSideEffect();
2187 auto localPools = g_pools.getCopy();
c92e6020 2188 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, std::move(policy), true}));
69108ebb
RG
2189 g_pools.setState(localPools);
2190 });
9396d955 2191
26512612 2192 luaCtx.writeFunction("setPoolServerPolicyLuaFFI", [](const string& name, ServerPolicy::ffipolicyfunc_t policy, const string& pool) {
69108ebb
RG
2193 setLuaSideEffect();
2194 auto localPools = g_pools.getCopy();
c92e6020 2195 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, std::move(policy)}));
69108ebb
RG
2196 g_pools.setState(localPools);
2197 });
fd51c832 2198
26512612 2199 luaCtx.writeFunction("setPoolServerPolicyLuaFFIPerThread", [](const string& name, const std::string& policyCode, const std::string& pool) {
69108ebb
RG
2200 setLuaSideEffect();
2201 auto localPools = g_pools.getCopy();
2202 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, policyCode}));
2203 g_pools.setState(localPools);
2204 });
fd51c832 2205
26512612 2206 luaCtx.writeFunction("showPoolServerPolicy", [](const std::string& pool) {
69108ebb
RG
2207 setLuaSideEffect();
2208 auto localPools = g_pools.getCopy();
2209 auto poolObj = getPool(localPools, pool);
2210 if (poolObj->policy == nullptr) {
2211 g_outputBuffer = g_policy.getLocal()->getName() + "\n";
2212 }
2213 else {
2214 g_outputBuffer = poolObj->policy->getName() + "\n";
2215 }
2216 });
393ed488 2217#endif /* DISABLE_POLICIES_BINDINGS */
9396d955 2218
4f699841 2219 luaCtx.writeFunction("setTCPDownstreamCleanupInterval", [](uint64_t interval) {
69108ebb 2220 setLuaSideEffect();
4f699841 2221 checkParameterBound("setTCPDownstreamCleanupInterval", interval);
8a2dd7db 2222 setTCPDownstreamCleanupInterval(interval);
69108ebb 2223 });
9396d955 2224
cf25b82b 2225#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
4f699841 2226 luaCtx.writeFunction("setDoHDownstreamCleanupInterval", [](uint64_t interval) {
767f5514 2227 setLuaSideEffect();
4f699841 2228 checkParameterBound("setDoHDownstreamCleanupInterval", interval);
767f5514
RG
2229 setDoHDownstreamCleanupInterval(interval);
2230 });
cf25b82b 2231#endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
767f5514 2232
4f699841 2233 luaCtx.writeFunction("setTCPDownstreamMaxIdleTime", [](uint64_t max) {
767f5514 2234 setLuaSideEffect();
4f699841 2235 checkParameterBound("setTCPDownstreamMaxIdleTime", max);
8a2dd7db 2236 setTCPDownstreamMaxIdleTime(max);
767f5514
RG
2237 });
2238
cf25b82b 2239#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
4f699841 2240 luaCtx.writeFunction("setDoHDownstreamMaxIdleTime", [](uint64_t max) {
767f5514 2241 setLuaSideEffect();
4f699841 2242 checkParameterBound("setDoHDownstreamMaxIdleTime", max);
767f5514
RG
2243 setDoHDownstreamMaxIdleTime(max);
2244 });
cf25b82b 2245#endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
767f5514 2246
fd51c832 2247 luaCtx.writeFunction("setConsoleConnectionsLogging", [](bool enabled) {
69108ebb
RG
2248 g_logConsoleConnections = enabled;
2249 });
e65ae260 2250
4f699841
RG
2251 luaCtx.writeFunction("setConsoleOutputMaxMsgSize", [](uint64_t size) {
2252 checkParameterBound("setConsoleOutputMaxMsgSize", size, std::numeric_limits<uint32_t>::max());
69108ebb
RG
2253 g_consoleOutputMsgMaxSize = size;
2254 });
03c05963 2255
bfb79abc 2256 luaCtx.writeFunction("setProxyProtocolACL", [](LuaTypeOrArrayOf<std::string> inp) {
f727b3c6 2257 if (!checkConfigurationTime("setProxyProtocolACL")) {
448d66d4
RG
2258 return;
2259 }
2260 setLuaSideEffect();
2261 NetmaskGroup nmg;
2262 if (auto str = boost::get<string>(&inp)) {
2263 nmg.addMask(*str);
2264 }
2265 else {
bfb79abc 2266 for (const auto& p : boost::get<LuaArray<std::string>>(inp)) {
69108ebb 2267 nmg.addMask(p.second);
448d66d4
RG
2268 }
2269 }
2270 g_proxyProtocolACL = std::move(nmg);
2271 });
2272
2273 luaCtx.writeFunction("setProxyProtocolApplyACLToProxiedClients", [](bool apply) {
f727b3c6 2274 if (!checkConfigurationTime("setProxyProtocolApplyACLToProxiedClients")) {
448d66d4
RG
2275 return;
2276 }
2277 setLuaSideEffect();
2278 g_applyACLToProxiedClients = apply;
2279 });
2280
23a3bc71 2281 luaCtx.writeFunction("setProxyProtocolMaximumPayloadSize", [](uint64_t size) {
f727b3c6 2282 if (!checkConfigurationTime("setProxyProtocolMaximumPayloadSize")) {
448d66d4
RG
2283 return;
2284 }
2285 setLuaSideEffect();
393ed488 2286 g_proxyProtocolMaximumSize = std::max(static_cast<uint64_t>(16), size);
448d66d4
RG
2287 });
2288
393ed488 2289#ifndef DISABLE_RECVMMSG
23a3bc71 2290 luaCtx.writeFunction("setUDPMultipleMessagesVectorSize", [](uint64_t vSize) {
f727b3c6 2291 if (!checkConfigurationTime("setUDPMultipleMessagesVectorSize")) {
69108ebb
RG
2292 return;
2293 }
6bb38cd6 2294#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
69108ebb
RG
2295 setLuaSideEffect();
2296 g_udpVectorSize = vSize;
6bb38cd6
RG
2297#else
2298 errlog("recvmmsg() support is not available!");
69108ebb 2299 g_outputBuffer = "recvmmsg support is not available!\n";
6bb38cd6 2300#endif
69108ebb 2301 });
393ed488 2302#endif /* DISABLE_RECVMMSG */
a227f47d 2303
fd51c832 2304 luaCtx.writeFunction("setAddEDNSToSelfGeneratedResponses", [](bool add) {
69108ebb 2305 g_addEDNSToSelfGeneratedResponses = add;
e7c732b8
RG
2306 });
2307
23a3bc71 2308 luaCtx.writeFunction("setPayloadSizeOnSelfGeneratedAnswers", [](uint64_t payloadSize) {
69108ebb
RG
2309 if (payloadSize < 512) {
2310 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!");
2311 g_outputBuffer = "setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!";
2312 payloadSize = 512;
2313 }
2314 if (payloadSize > s_udpIncomingBufferSize) {
2315 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to %d instead!", s_udpIncomingBufferSize);
2316 g_outputBuffer = "setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to " + std::to_string(s_udpIncomingBufferSize) + " instead";
2317 payloadSize = s_udpIncomingBufferSize;
2318 }
2319 g_PayloadSizeSelfGenAnswers = payloadSize;
e7c732b8
RG
2320 });
2321
72b54749 2322#ifndef DISABLE_SECPOLL
393ed488
RG
2323 luaCtx.writeFunction("showSecurityStatus", []() {
2324 setLuaNoSideEffect();
6ba8d6ca 2325 g_outputBuffer = std::to_string(dnsdist::metrics::g_stats.securityStatus) + "\n";
393ed488
RG
2326 });
2327
fd51c832 2328 luaCtx.writeFunction("setSecurityPollSuffix", [](const std::string& suffix) {
f727b3c6 2329 if (!checkConfigurationTime("setSecurityPollSuffix")) {
69108ebb
RG
2330 return;
2331 }
69108ebb 2332 g_secPollSuffix = suffix;
5d4e1ef8
RG
2333 });
2334
fd51c832 2335 luaCtx.writeFunction("setSecurityPollInterval", [](time_t newInterval) {
69108ebb
RG
2336 if (newInterval <= 0) {
2337 warnlog("setSecurityPollInterval() should be > 0, skipping");
2338 g_outputBuffer = "setSecurityPollInterval() should be > 0, skipping";
2339 }
5d4e1ef8 2340
69108ebb 2341 g_secPollInterval = newInterval;
5d4e1ef8 2342 });
72b54749 2343#endif /* DISABLE_SECPOLL */
5d4e1ef8 2344
69108ebb 2345 luaCtx.writeFunction("setSyslogFacility", [](boost::variant<int, std::string> facility) {
f727b3c6 2346 if (!checkConfigurationTime("setSyslogFacility")) {
0ca6a67f
RG
2347 return;
2348 }
f727b3c6 2349 setLuaSideEffect();
b6d43610
RG
2350 if (facility.type() == typeid(std::string)) {
2351 static std::map<std::string, int> const facilities = {
69108ebb
RG
2352 {"local0", LOG_LOCAL0},
2353 {"log_local0", LOG_LOCAL0},
2354 {"local1", LOG_LOCAL1},
2355 {"log_local1", LOG_LOCAL1},
2356 {"local2", LOG_LOCAL2},
2357 {"log_local2", LOG_LOCAL2},
2358 {"local3", LOG_LOCAL3},
2359 {"log_local3", LOG_LOCAL3},
2360 {"local4", LOG_LOCAL4},
2361 {"log_local4", LOG_LOCAL4},
2362 {"local5", LOG_LOCAL5},
2363 {"log_local5", LOG_LOCAL5},
2364 {"local6", LOG_LOCAL6},
2365 {"log_local6", LOG_LOCAL6},
2366 {"local7", LOG_LOCAL7},
2367 {"log_local7", LOG_LOCAL7},
b6d43610
RG
2368 /* most of these likely make very little sense
2369 for dnsdist, but why not? */
69108ebb
RG
2370 {"kern", LOG_KERN},
2371 {"log_kern", LOG_KERN},
2372 {"user", LOG_USER},
2373 {"log_user", LOG_USER},
2374 {"mail", LOG_MAIL},
2375 {"log_mail", LOG_MAIL},
2376 {"daemon", LOG_DAEMON},
2377 {"log_daemon", LOG_DAEMON},
2378 {"auth", LOG_AUTH},
2379 {"log_auth", LOG_AUTH},
2380 {"syslog", LOG_SYSLOG},
2381 {"log_syslog", LOG_SYSLOG},
2382 {"lpr", LOG_LPR},
2383 {"log_lpr", LOG_LPR},
2384 {"news", LOG_NEWS},
2385 {"log_news", LOG_NEWS},
2386 {"uucp", LOG_UUCP},
2387 {"log_uucp", LOG_UUCP},
2388 {"cron", LOG_CRON},
2389 {"log_cron", LOG_CRON},
2390 {"authpriv", LOG_AUTHPRIV},
2391 {"log_authpriv", LOG_AUTHPRIV},
2392 {"ftp", LOG_FTP},
2393 {"log_ftp", LOG_FTP}};
b6d43610
RG
2394 auto facilityStr = boost::get<std::string>(facility);
2395 toLowerInPlace(facilityStr);
2396 auto it = facilities.find(facilityStr);
2397 if (it == facilities.end()) {
69108ebb 2398 g_outputBuffer = "Unknown facility '" + facilityStr + "' passed to setSyslogFacility()!\n";
b6d43610
RG
2399 return;
2400 }
2401 setSyslogFacility(it->second);
2402 }
2403 else {
2404 setSyslogFacility(boost::get<int>(facility));
2405 }
0ca6a67f
RG
2406 });
2407
6de19ce2
CHB
2408 typedef std::unordered_map<std::string, std::string> tlscertificateopts_t;
2409 luaCtx.writeFunction("newTLSCertificate", [client](const std::string& cert, boost::optional<tlscertificateopts_t> opts) {
2410 std::shared_ptr<TLSCertKeyPair> result = nullptr;
2411 if (client) {
2412 return result;
2413 }
2414#if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
2415 std::optional<std::string> key, password;
2416 if (opts) {
2417 if (opts->count("key")) {
2418 key = boost::get<const string>((*opts)["key"]);
2419 }
2420 if (opts->count("password")) {
2421 password = boost::get<const string>((*opts)["password"]);
2422 }
2423 }
c43570b6 2424 result = std::make_shared<TLSCertKeyPair>(cert, std::move(key), std::move(password));
6de19ce2
CHB
2425#endif
2426 return result;
2427 });
905dae56 2428
bfb79abc 2429 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) {
fbf14b03
RG
2430 if (client) {
2431 return;
2432 }
b35ad620 2433#ifdef HAVE_DNS_OVER_HTTPS
f727b3c6 2434 if (!checkConfigurationTime("addDOHLocal")) {
fbf14b03
RG
2435 return;
2436 }
f727b3c6 2437 setLuaSideEffect();
bf8cd40d 2438
f727b3c6 2439 auto frontend = std::make_shared<DOHFrontend>();
7e8a05fa
RG
2440 if (getOptionalValue<std::string>(vars, "library", frontend->d_library) == 0) {
2441#ifdef HAVE_NGHTTP2
2442 frontend->d_library = "nghttp2";
2443#else /* HAVE_NGHTTP2 */
2444 frontend->d_library = "h2o";
2445#endif /* HAVE_NGHTTP2 */
2446 }
2447 if (frontend->d_library == "h2o") {
9eb152c4 2448#ifdef HAVE_LIBH2OEVLOOP
7e8a05fa 2449 frontend = std::make_shared<H2ODOHFrontend>();
0cf94587
RG
2450 // we _really_ need to set it again, as we just replaced the generic frontend by a new one
2451 frontend->d_library = "h2o";
9eb152c4 2452#else /* HAVE_LIBH2OEVLOOP */
7e8a05fa
RG
2453 errlog("DOH bind %s is configured to use libh2o but the library is not available", addr);
2454 return;
9eb152c4 2455#endif /* HAVE_LIBH2OEVLOOP */
7e8a05fa
RG
2456 }
2457 else if (frontend->d_library == "nghttp2") {
2458#ifndef HAVE_NGHTTP2
2459 errlog("DOH bind %s is configured to use nghttp2 but the library is not available", addr);
2460 return;
2461#endif /* HAVE_NGHTTP2 */
2462 }
2463 else {
2464 errlog("DOH bind %s is configured to use an unknown library ('%s')", addr, frontend->d_library);
2465 return;
2466 }
9eb152c4 2467
7e8a05fa 2468 bool useTLS = true;
6de19ce2 2469 if (certFiles && !certFiles->empty()) {
9eb152c4 2470 if (!loadTLSCertificateAndKeys("addDOHLocal", frontend->d_tlsContext.d_tlsConfig.d_certKeyPairs, *certFiles, *keyFiles)) {
44947230
RG
2471 return;
2472 }
2473
9eb152c4 2474 frontend->d_tlsContext.d_addr = ComboAddress(addr, 443);
44947230
RG
2475 }
2476 else {
9eb152c4
RG
2477 frontend->d_tlsContext.d_addr = ComboAddress(addr, 80);
2478 infolog("No certificate provided for DoH endpoint %s, running in DNS over HTTP mode instead of DNS over HTTPS", frontend->d_tlsContext.d_addr.toStringWithPort());
7e8a05fa 2479 useTLS = false;
bf8cd40d
RG
2480 }
2481
0718e562
RG
2482 if (urls) {
2483 if (urls->type() == typeid(std::string)) {
9eb152c4 2484 frontend->d_urls.insert(boost::get<std::string>(*urls));
0718e562 2485 }
bfb79abc
RG
2486 else if (urls->type() == typeid(LuaArray<std::string>)) {
2487 auto urlsVect = boost::get<LuaArray<std::string>>(*urls);
69108ebb 2488 for (const auto& p : urlsVect) {
9eb152c4 2489 frontend->d_urls.insert(p.second);
0718e562 2490 }
fbf14b03
RG
2491 }
2492 }
2493 else {
9eb152c4 2494 frontend->d_urls.insert("/dns-query");
fbf14b03
RG
2495 }
2496
2b0cb8f8
RG
2497 bool reusePort = false;
2498 int tcpFastOpenQueueSize = 0;
c58723d7 2499 int tcpListenQueueSize = 0;
393ed488
RG
2500 uint64_t maxInFlightQueriesPerConn = 0;
2501 uint64_t tcpMaxConcurrentConnections = 0;
2b0cb8f8
RG
2502 std::string interface;
2503 std::set<int> cpus;
57845fd8 2504 std::vector<std::pair<ComboAddress, int>> additionalAddresses;
36800a60 2505 bool enableProxyProtocol = true;
2b0cb8f8 2506
c58723d7 2507 if (vars) {
36800a60 2508 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol);
45f86d73
AT
2509 getOptionalValue<int>(vars, "idleTimeout", frontend->d_idleTimeout);
2510 getOptionalValue<std::string>(vars, "serverTokens", frontend->d_serverTokens);
7e8a05fa
RG
2511 getOptionalValue<std::string>(vars, "provider", frontend->d_tlsContext.d_provider);
2512 boost::algorithm::to_lower(frontend->d_tlsContext.d_provider);
9f433af4 2513 getOptionalValue<bool>(vars, "proxyProtocolOutsideTLS", frontend->d_tlsContext.d_proxyProtocolOutsideTLS);
45f86d73
AT
2514
2515 LuaAssociativeTable<std::string> customResponseHeaders;
2516 if (getOptionalValue<decltype(customResponseHeaders)>(vars, "customResponseHeaders", customResponseHeaders) > 0) {
2517 for (auto const& headerMap : customResponseHeaders) {
0b0882f5 2518 auto headerResponse = std::pair(boost::to_lower_copy(headerMap.first), headerMap.second);
45f86d73 2519 frontend->d_customResponseHeaders.insert(headerResponse);
ee01507f
CR
2520 }
2521 }
0ef9ab19 2522
45f86d73
AT
2523 getOptionalValue<bool>(vars, "sendCacheControlHeaders", frontend->d_sendCacheControlHeaders);
2524 getOptionalValue<bool>(vars, "keepIncomingHeaders", frontend->d_keepIncomingHeaders);
2525 getOptionalValue<bool>(vars, "trustForwardedForHeader", frontend->d_trustForwardedForHeader);
7e8a05fa 2526 getOptionalValue<bool>(vars, "earlyACLDrop", frontend->d_earlyACLDrop);
45f86d73
AT
2527 getOptionalValue<int>(vars, "internalPipeBufferSize", frontend->d_internalPipeBufferSize);
2528 getOptionalValue<bool>(vars, "exactPathMatching", frontend->d_exactPathMatching);
0026abf9 2529
45f86d73
AT
2530 LuaArray<std::string> addresses;
2531 if (getOptionalValue<decltype(addresses)>(vars, "additionalAddresses", addresses) > 0) {
57845fd8
RG
2532 for (const auto& [_, add] : addresses) {
2533 try {
2534 ComboAddress address(add);
2535 additionalAddresses.emplace_back(address, -1);
2536 }
2537 catch (const PDNSException& e) {
2538 errlog("Unable to parse additional address %s for DOH bind: %s", add, e.reason);
2539 return;
2540 }
2541 }
2542 }
2543
9eb152c4 2544 parseTLSConfig(frontend->d_tlsContext.d_tlsConfig, "addDOHLocal", vars);
45f86d73
AT
2545
2546 bool ignoreTLSConfigurationErrors = false;
2547 if (getOptionalValue<bool>(vars, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors) > 0 && ignoreTLSConfigurationErrors) {
2548 // we are asked to try to load the certificates so we can return a potential error
2549 // and properly ignore the frontend before actually launching it
2550 try {
2551 std::map<int, std::string> ocspResponses = {};
9eb152c4 2552 auto ctx = libssl_init_server_context(frontend->d_tlsContext.d_tlsConfig, ocspResponses);
45f86d73
AT
2553 }
2554 catch (const std::runtime_error& e) {
2555 errlog("Ignoring DoH frontend: '%s'", e.what());
2556 return;
dcbd10c6
CHB
2557 }
2558 }
45f86d73
AT
2559
2560 checkAllParametersConsumed("addDOHLocal", vars);
fbf14b03 2561 }
7e8a05fa
RG
2562
2563 if (useTLS && frontend->d_library == "nghttp2") {
2564 if (!frontend->d_tlsContext.d_provider.empty()) {
2565 vinfolog("Loading TLS provider '%s'", frontend->d_tlsContext.d_provider);
2566 }
2567 else {
2568#ifdef HAVE_LIBSSL
2569 const std::string provider("openssl");
2570#else
2571 const std::string provider("gnutls");
2572#endif
2573 vinfolog("Loading default TLS provider '%s'", provider);
2574 }
2575 }
2576
fbf14b03 2577 g_dohlocals.push_back(frontend);
36800a60 2578 auto clientState = std::make_unique<ClientState>(frontend->d_tlsContext.d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol);
c114cd18
RG
2579 clientState->dohFrontend = std::move(frontend);
2580 clientState->d_additionalAddresses = std::move(additionalAddresses);
57845fd8 2581
c58723d7 2582 if (tcpListenQueueSize > 0) {
c114cd18 2583 clientState->tcpListenQueueSize = tcpListenQueueSize;
c58723d7 2584 }
519a0072 2585 if (tcpMaxConcurrentConnections > 0) {
c114cd18 2586 clientState->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections;
519a0072 2587 }
c114cd18 2588 g_frontends.push_back(std::move(clientState));
9eb152c4 2589#else /* HAVE_DNS_OVER_HTTPS */
69108ebb 2590 throw std::runtime_error("addDOHLocal() called but DNS over HTTPS support is not present!");
9eb152c4 2591#endif /* HAVE_DNS_OVER_HTTPS */
fbf14b03
RG
2592 });
2593
4cae5603 2594 // NOLINTNEXTLINE(performance-unnecessary-value-param): somehow clang-tidy gets confused about the fact vars could be const while it cannot
e891e0b9
CHB
2595 luaCtx.writeFunction("addDOH3Local", [client](const std::string& addr, const boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>>& certFiles, const boost::variant<std::string, LuaArray<std::string>>& keyFiles, boost::optional<localbind_t> vars) {
2596 if (client) {
2597 return;
2598 }
2599#ifdef HAVE_DNS_OVER_HTTP3
2600 if (!checkConfigurationTime("addDOH3Local")) {
2601 return;
2602 }
2603 setLuaSideEffect();
2604
2605 auto frontend = std::make_shared<DOH3Frontend>();
ed16c81f 2606 if (!loadTLSCertificateAndKeys("addDOH3Local", frontend->d_quicheParams.d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) {
e891e0b9
CHB
2607 return;
2608 }
fd661ab4 2609 frontend->d_local = ComboAddress(addr, 443);
e891e0b9
CHB
2610
2611 bool reusePort = false;
2612 int tcpFastOpenQueueSize = 0;
2613 int tcpListenQueueSize = 0;
2614 uint64_t maxInFlightQueriesPerConn = 0;
2615 uint64_t tcpMaxConcurrentConnections = 0;
2616 std::string interface;
2617 std::set<int> cpus;
2618 std::vector<std::pair<ComboAddress, int>> additionalAddresses;
94ec5e7d 2619 bool enableProxyProtocol = true;
e891e0b9
CHB
2620
2621 if (vars) {
94ec5e7d 2622 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol);
e891e0b9 2623 if (maxInFlightQueriesPerConn > 0) {
ed16c81f 2624 frontend->d_quicheParams.d_maxInFlight = maxInFlightQueriesPerConn;
e891e0b9
CHB
2625 }
2626 getOptionalValue<int>(vars, "internalPipeBufferSize", frontend->d_internalPipeBufferSize);
ed16c81f
CHB
2627 getOptionalValue<int>(vars, "idleTimeout", frontend->d_quicheParams.d_idleTimeout);
2628 getOptionalValue<std::string>(vars, "keyLogFile", frontend->d_quicheParams.d_keyLogFile);
e891e0b9
CHB
2629 {
2630 std::string valueStr;
2631 if (getOptionalValue<std::string>(vars, "congestionControlAlgo", valueStr) > 0) {
ed16c81f
CHB
2632 if (dnsdist::doq::s_available_cc_algorithms.count(valueStr) > 0) {
2633 frontend->d_quicheParams.d_ccAlgo = valueStr;
e891e0b9
CHB
2634 }
2635 else {
2636 warnlog("Ignoring unknown value '%s' for 'congestionControlAlgo' on 'addDOH3Local'", valueStr);
2637 }
2638 }
2639 }
ed16c81f 2640 parseTLSConfig(frontend->d_quicheParams.d_tlsConfig, "addDOH3Local", vars);
e891e0b9
CHB
2641
2642 bool ignoreTLSConfigurationErrors = false;
2643 if (getOptionalValue<bool>(vars, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors) > 0 && ignoreTLSConfigurationErrors) {
2644 // we are asked to try to load the certificates so we can return a potential error
2645 // and properly ignore the frontend before actually launching it
2646 try {
2647 std::map<int, std::string> ocspResponses = {};
ed16c81f 2648 auto ctx = libssl_init_server_context(frontend->d_quicheParams.d_tlsConfig, ocspResponses);
e891e0b9
CHB
2649 }
2650 catch (const std::runtime_error& e) {
2651 errlog("Ignoring DoH3 frontend: '%s'", e.what());
2652 return;
2653 }
2654 }
2655
2656 checkAllParametersConsumed("addDOH3Local", vars);
2657 }
2658 g_doh3locals.push_back(frontend);
b7b61ed5 2659 auto clientState = std::make_unique<ClientState>(frontend->d_local, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol);
c3a86a30
RG
2660 clientState->doh3Frontend = frontend;
2661 clientState->d_additionalAddresses = std::move(additionalAddresses);
e891e0b9 2662
c3a86a30 2663 g_frontends.push_back(std::move(clientState));
e891e0b9
CHB
2664#else
2665 throw std::runtime_error("addDOH3Local() called but DNS over HTTP/3 support is not present!");
2666#endif
2667 });
2668
61e8b855 2669 // NOLINTNEXTLINE(performance-unnecessary-value-param): somehow clang-tidy gets confused about the fact vars could be const while it cannot
a7643425 2670 luaCtx.writeFunction("addDOQLocal", [client](const std::string& addr, const boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>>& certFiles, const boost::variant<std::string, LuaArray<std::string>>& keyFiles, boost::optional<localbind_t> vars) {
57a94421
CHB
2671 if (client) {
2672 return;
2673 }
2674#ifdef HAVE_DNS_OVER_QUIC
2675 if (!checkConfigurationTime("addDOQLocal")) {
2676 return;
2677 }
2678 setLuaSideEffect();
2679
2680 auto frontend = std::make_shared<DOQFrontend>();
ed16c81f 2681 if (!loadTLSCertificateAndKeys("addDOQLocal", frontend->d_quicheParams.d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) {
57a94421
CHB
2682 return;
2683 }
2684 frontend->d_local = ComboAddress(addr, 853);
2685
2686 bool reusePort = false;
2687 int tcpFastOpenQueueSize = 0;
2688 int tcpListenQueueSize = 0;
2689 uint64_t maxInFlightQueriesPerConn = 0;
2690 uint64_t tcpMaxConcurrentConnections = 0;
2691 std::string interface;
2692 std::set<int> cpus;
2693 std::vector<std::pair<ComboAddress, int>> additionalAddresses;
b7b61ed5 2694 bool enableProxyProtocol = true;
57a94421
CHB
2695
2696 if (vars) {
b7b61ed5 2697 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol);
afc459f8 2698 if (maxInFlightQueriesPerConn > 0) {
ed16c81f 2699 frontend->d_quicheParams.d_maxInFlight = maxInFlightQueriesPerConn;
afc459f8 2700 }
f7d508b5 2701 getOptionalValue<int>(vars, "internalPipeBufferSize", frontend->d_internalPipeBufferSize);
ed16c81f
CHB
2702 getOptionalValue<int>(vars, "idleTimeout", frontend->d_quicheParams.d_idleTimeout);
2703 getOptionalValue<std::string>(vars, "keyLogFile", frontend->d_quicheParams.d_keyLogFile);
7b7f5513
CHB
2704 {
2705 std::string valueStr;
2706 if (getOptionalValue<std::string>(vars, "congestionControlAlgo", valueStr) > 0) {
ed16c81f
CHB
2707 if (dnsdist::doq::s_available_cc_algorithms.count(valueStr) > 0) {
2708 frontend->d_quicheParams.d_ccAlgo = valueStr;
7b7f5513
CHB
2709 }
2710 else {
2711 warnlog("Ignoring unknown value '%s' for 'congestionControlAlgo' on 'addDOQLocal'", valueStr);
2712 }
2713 }
2714 }
ed16c81f 2715 parseTLSConfig(frontend->d_quicheParams.d_tlsConfig, "addDOQLocal", vars);
57a94421
CHB
2716
2717 bool ignoreTLSConfigurationErrors = false;
2718 if (getOptionalValue<bool>(vars, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors) > 0 && ignoreTLSConfigurationErrors) {
2719 // we are asked to try to load the certificates so we can return a potential error
2720 // and properly ignore the frontend before actually launching it
2721 try {
2722 std::map<int, std::string> ocspResponses = {};
ed16c81f 2723 auto ctx = libssl_init_server_context(frontend->d_quicheParams.d_tlsConfig, ocspResponses);
57a94421
CHB
2724 }
2725 catch (const std::runtime_error& e) {
2726 errlog("Ignoring DoQ frontend: '%s'", e.what());
2727 return;
2728 }
2729 }
2730
2731 checkAllParametersConsumed("addDOQLocal", vars);
2732 }
2733 g_doqlocals.push_back(frontend);
36800a60 2734 auto clientState = std::make_unique<ClientState>(frontend->d_local, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol);
c114cd18
RG
2735 clientState->doqFrontend = std::move(frontend);
2736 clientState->d_additionalAddresses = std::move(additionalAddresses);
57a94421 2737
c114cd18 2738 g_frontends.push_back(std::move(clientState));
57a94421
CHB
2739#else
2740 throw std::runtime_error("addDOQLocal() called but DNS over QUIC support is not present!");
2741#endif
2742 });
2743
2744 luaCtx.writeFunction("showDOQFrontends", []() {
2745#ifdef HAVE_DNS_OVER_QUIC
2746 setLuaNoSideEffect();
2747 try {
2748 ostringstream ret;
df9cb7f3
CHB
2749 boost::format fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d");
2750 ret << (fmt % "#" % "Address" % "Bad Version" % "Invalid Token" % "Errors" % "Valid") << endl;
57a94421
CHB
2751 size_t counter = 0;
2752 for (const auto& ctx : g_doqlocals) {
df9cb7f3 2753 ret << (fmt % counter % ctx->d_local.toStringWithPort() % ctx->d_doqUnsupportedVersionErrors % ctx->d_doqInvalidTokensReceived % ctx->d_errorResponses % ctx->d_validResponses) << endl;
57a94421
CHB
2754 counter++;
2755 }
2756 g_outputBuffer = ret.str();
2757 }
2758 catch (const std::exception& e) {
2759 g_outputBuffer = e.what();
2760 throw;
2761 }
2762#else
2763 g_outputBuffer = "DNS over QUIC support is not present!\n";
2764#endif
2765 });
2766
fd51c832 2767 luaCtx.writeFunction("showDOHFrontends", []() {
1b51c0f3 2768#ifdef HAVE_DNS_OVER_HTTPS
69108ebb
RG
2769 setLuaNoSideEffect();
2770 try {
2771 ostringstream ret;
2772 boost::format fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d");
2773 ret << (fmt % "#" % "Address" % "HTTP" % "HTTP/1" % "HTTP/2" % "GET" % "POST" % "Bad" % "Errors" % "Redirects" % "Valid" % "# ticket keys" % "Rotation delay" % "Next rotation") << endl;
2774 size_t counter = 0;
2775 for (const auto& ctx : g_dohlocals) {
9eb152c4 2776 ret << (fmt % counter % ctx->d_tlsContext.d_addr.toStringWithPort() % ctx->d_httpconnects % ctx->d_http1Stats.d_nbQueries % ctx->d_http2Stats.d_nbQueries % ctx->d_getqueries % ctx->d_postqueries % ctx->d_badrequests % ctx->d_errorresponses % ctx->d_redirectresponses % ctx->d_validresponses % ctx->getTicketsKeysCount() % ctx->getTicketsKeyRotationDelay() % ctx->getNextTicketsKeyRotation()) << endl;
69108ebb
RG
2777 counter++;
2778 }
2779 g_outputBuffer = ret.str();
2780 }
2781 catch (const std::exception& e) {
2782 g_outputBuffer = e.what();
2783 throw;
2784 }
1b51c0f3 2785#else
69108ebb 2786 g_outputBuffer = "DNS over HTTPS support is not present!\n";
1b51c0f3 2787#endif
69108ebb 2788 });
1b51c0f3 2789
51524d84
RG
2790 luaCtx.writeFunction("showDOH3Frontends", []() {
2791#ifdef HAVE_DNS_OVER_HTTP3
2792 setLuaNoSideEffect();
2793 try {
2794 ostringstream ret;
2795 boost::format fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d");
2796 ret << (fmt % "#" % "Address" % "Bad Version" % "Invalid Token" % "Errors" % "Valid") << endl;
2797 size_t counter = 0;
2798 for (const auto& ctx : g_doh3locals) {
2799 ret << (fmt % counter % ctx->d_local.toStringWithPort() % ctx->d_doh3UnsupportedVersionErrors % ctx->d_doh3InvalidTokensReceived % ctx->d_errorResponses % ctx->d_validResponses) << endl;
2800 counter++;
2801 }
2802 g_outputBuffer = ret.str();
2803 }
2804 catch (const std::exception& e) {
2805 g_outputBuffer = e.what();
2806 throw;
2807 }
2808#else
2809 g_outputBuffer = "DNS over HTTP3 support is not present!\n";
2810#endif
2811 });
2812
69108ebb 2813 luaCtx.writeFunction("showDOHResponseCodes", []() {
5bbcbea0 2814#ifdef HAVE_DNS_OVER_HTTPS
69108ebb
RG
2815 setLuaNoSideEffect();
2816 try {
2817 ostringstream ret;
2818 boost::format fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d %-15d %-15d");
2819 g_outputBuffer = "\n- HTTP/1:\n\n";
2820 ret << (fmt % "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others") << endl;
2821 size_t counter = 0;
2822 for (const auto& ctx : g_dohlocals) {
9eb152c4 2823 ret << (fmt % counter % ctx->d_tlsContext.d_addr.toStringWithPort() % ctx->d_http1Stats.d_nb200Responses % ctx->d_http1Stats.d_nb400Responses % ctx->d_http1Stats.d_nb403Responses % ctx->d_http1Stats.d_nb500Responses % ctx->d_http1Stats.d_nb502Responses % ctx->d_http1Stats.d_nbOtherResponses) << endl;
69108ebb
RG
2824 counter++;
2825 }
2826 g_outputBuffer += ret.str();
2827 ret.str("");
2828
2829 g_outputBuffer += "\n- HTTP/2:\n\n";
2830 ret << (fmt % "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others") << endl;
2831 counter = 0;
2832 for (const auto& ctx : g_dohlocals) {
9eb152c4 2833 ret << (fmt % counter % ctx->d_tlsContext.d_addr.toStringWithPort() % ctx->d_http2Stats.d_nb200Responses % ctx->d_http2Stats.d_nb400Responses % ctx->d_http2Stats.d_nb403Responses % ctx->d_http2Stats.d_nb500Responses % ctx->d_http2Stats.d_nb502Responses % ctx->d_http2Stats.d_nbOtherResponses) << endl;
69108ebb
RG
2834 counter++;
2835 }
2836 g_outputBuffer += ret.str();
2837 }
2838 catch (const std::exception& e) {
2839 g_outputBuffer = e.what();
2840 throw;
2841 }
5bbcbea0 2842#else
69108ebb 2843 g_outputBuffer = "DNS over HTTPS support is not present!\n";
5bbcbea0 2844#endif
69108ebb 2845 });
5bbcbea0 2846
23a3bc71 2847 luaCtx.writeFunction("getDOHFrontend", [client](uint64_t index) {
69108ebb
RG
2848 std::shared_ptr<DOHFrontend> result = nullptr;
2849 if (client) {
2850 return result;
2851 }
1b51c0f3 2852#ifdef HAVE_DNS_OVER_HTTPS
69108ebb
RG
2853 setLuaNoSideEffect();
2854 try {
2855 if (index < g_dohlocals.size()) {
2856 result = g_dohlocals.at(index);
2857 }
2858 else {
f8e1161f 2859 errlog("Error: trying to get DOH frontend with index %d but we only have %d frontend(s)\n", index, g_dohlocals.size());
69108ebb
RG
2860 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";
2861 }
2862 }
2863 catch (const std::exception& e) {
2864 g_outputBuffer = "Error while trying to get DOH frontend with index " + std::to_string(index) + ": " + string(e.what()) + "\n";
f8e1161f 2865 errlog("Error while trying to get DOH frontend with index %d: %s\n", index, string(e.what()));
69108ebb 2866 }
1b51c0f3
RG
2867#else
2868 g_outputBuffer="DNS over HTTPS support is not present!\n";
2869#endif
69108ebb
RG
2870 return result;
2871 });
1b51c0f3 2872
69108ebb
RG
2873 luaCtx.writeFunction("getDOHFrontendCount", []() {
2874 setLuaNoSideEffect();
2875 return g_dohlocals.size();
2876 });
265260f5 2877
69108ebb
RG
2878 luaCtx.registerFunction<void (std::shared_ptr<DOHFrontend>::*)()>("reloadCertificates", [](std::shared_ptr<DOHFrontend> frontend) {
2879 if (frontend != nullptr) {
2880 frontend->reloadCertificates();
2881 }
2882 });
1b51c0f3 2883
bfb79abc 2884 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) {
00a1ad50 2885#ifdef HAVE_DNS_OVER_HTTPS
69108ebb 2886 if (frontend != nullptr) {
9eb152c4 2887 if (loadTLSCertificateAndKeys("DOHFrontend::loadNewCertificatesAndKeys", frontend->d_tlsContext.d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) {
69108ebb
RG
2888 frontend->reloadCertificates();
2889 }
2890 }
00a1ad50 2891#endif
69108ebb 2892 });
00a1ad50 2893
69108ebb
RG
2894 luaCtx.registerFunction<void (std::shared_ptr<DOHFrontend>::*)()>("rotateTicketsKey", [](std::shared_ptr<DOHFrontend> frontend) {
2895 if (frontend != nullptr) {
2896 frontend->rotateTicketsKey(time(nullptr));
2897 }
2898 });
0ef9ab19 2899
69108ebb
RG
2900 luaCtx.registerFunction<void (std::shared_ptr<DOHFrontend>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<DOHFrontend> frontend, const std::string& file) {
2901 if (frontend != nullptr) {
2902 frontend->loadTicketsKeys(file);
2903 }
2904 });
0ef9ab19 2905
bfb79abc 2906 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) {
69108ebb
RG
2907 if (frontend != nullptr) {
2908 auto newMap = std::make_shared<std::vector<std::shared_ptr<DOHResponseMapEntry>>>();
2909 newMap->reserve(map.size());
28b56482 2910
69108ebb
RG
2911 for (const auto& entry : map) {
2912 newMap->push_back(entry.second);
2913 }
28b56482 2914
69108ebb
RG
2915 frontend->d_responsesMap = std::move(newMap);
2916 }
2917 });
28b56482 2918
bfb79abc 2919 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) {
69108ebb
RG
2920 if (client) {
2921 return;
2922 }
b35ad620 2923#ifdef HAVE_DNS_OVER_TLS
f727b3c6 2924 if (!checkConfigurationTime("addTLSLocal")) {
69108ebb
RG
2925 return;
2926 }
f727b3c6 2927 setLuaSideEffect();
fa974ada 2928
9eb152c4 2929 auto frontend = std::make_shared<TLSFrontend>(TLSFrontend::ALPN::DoT);
69108ebb
RG
2930 if (!loadTLSCertificateAndKeys("addTLSLocal", frontend->d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) {
2931 return;
2932 }
a227f47d 2933
69108ebb
RG
2934 bool reusePort = false;
2935 int tcpFastOpenQueueSize = 0;
2936 int tcpListenQueueSize = 0;
393ed488
RG
2937 uint64_t maxInFlightQueriesPerConn = 0;
2938 uint64_t tcpMaxConcurrentConns = 0;
69108ebb
RG
2939 std::string interface;
2940 std::set<int> cpus;
57845fd8 2941 std::vector<std::pair<ComboAddress, int>> additionalAddresses;
36800a60 2942 bool enableProxyProtocol = true;
6e9fd124 2943
69108ebb 2944 if (vars) {
36800a60 2945 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConns, enableProxyProtocol);
a227f47d 2946
45f86d73
AT
2947 getOptionalValue<std::string>(vars, "provider", frontend->d_provider);
2948 boost::algorithm::to_lower(frontend->d_provider);
9f433af4 2949 getOptionalValue<bool>(vars, "proxyProtocolOutsideTLS", frontend->d_proxyProtocolOutsideTLS);
a227f47d 2950
45f86d73
AT
2951 LuaArray<std::string> addresses;
2952 if (getOptionalValue<decltype(addresses)>(vars, "additionalAddresses", addresses) > 0) {
57845fd8
RG
2953 for (const auto& [_, add] : addresses) {
2954 try {
2955 ComboAddress address(add);
2956 additionalAddresses.emplace_back(address, -1);
2957 }
2958 catch (const PDNSException& e) {
2959 errlog("Unable to parse additional address %s for DoT bind: %s", add, e.reason);
2960 return;
2961 }
2962 }
2963 }
2964
69108ebb 2965 parseTLSConfig(frontend->d_tlsConfig, "addTLSLocal", vars);
45f86d73
AT
2966
2967 bool ignoreTLSConfigurationErrors = false;
2968 if (getOptionalValue<bool>(vars, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors) > 0 && ignoreTLSConfigurationErrors) {
2969 // we are asked to try to load the certificates so we can return a potential error
2970 // and properly ignore the frontend before actually launching it
2971 try {
2972 std::map<int, std::string> ocspResponses = {};
2973 auto ctx = libssl_init_server_context(frontend->d_tlsConfig, ocspResponses);
2974 }
2975 catch (const std::runtime_error& e) {
2976 errlog("Ignoring TLS frontend: '%s'", e.what());
2977 return;
dcbd10c6
CHB
2978 }
2979 }
45f86d73
AT
2980
2981 checkAllParametersConsumed("addTLSLocal", vars);
69108ebb 2982 }
a227f47d 2983
69108ebb
RG
2984 try {
2985 frontend->d_addr = ComboAddress(addr, 853);
2986 if (!frontend->d_provider.empty()) {
2987 vinfolog("Loading TLS provider '%s'", frontend->d_provider);
2988 }
2989 else {
b5af510a 2990#ifdef HAVE_LIBSSL
7e8a05fa 2991 const std::string provider("openssl");
b5af510a 2992#else
7e8a05fa 2993 const std::string provider("gnutls");
b5af510a 2994#endif
7e8a05fa 2995 vinfolog("Loading default TLS provider '%s'", provider);
69108ebb
RG
2996 }
2997 // only works pre-startup, so no sync necessary
36800a60 2998 auto clientState = std::make_unique<ClientState>(frontend->d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol);
c114cd18
RG
2999 clientState->tlsFrontend = frontend;
3000 clientState->d_additionalAddresses = std::move(additionalAddresses);
69108ebb 3001 if (tcpListenQueueSize > 0) {
c114cd18 3002 clientState->tcpListenQueueSize = tcpListenQueueSize;
69108ebb
RG
3003 }
3004 if (maxInFlightQueriesPerConn > 0) {
c114cd18 3005 clientState->d_maxInFlightQueriesPerConn = maxInFlightQueriesPerConn;
69108ebb
RG
3006 }
3007 if (tcpMaxConcurrentConns > 0) {
c114cd18 3008 clientState->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConns;
69108ebb 3009 }
83654fe6 3010
c114cd18
RG
3011 g_tlslocals.push_back(clientState->tlsFrontend);
3012 g_frontends.push_back(std::move(clientState));
69108ebb
RG
3013 }
3014 catch (const std::exception& e) {
3015 g_outputBuffer = "Error: " + string(e.what()) + "\n";
3016 }
a227f47d 3017#else
69108ebb 3018 throw std::runtime_error("addTLSLocal() called but DNS over TLS support is not present!");
a227f47d 3019#endif
69108ebb 3020 });
a227f47d 3021
69108ebb 3022 luaCtx.writeFunction("showTLSContexts", []() {
a227f47d 3023#ifdef HAVE_DNS_OVER_TLS
69108ebb
RG
3024 setLuaNoSideEffect();
3025 try {
3026 ostringstream ret;
3027 boost::format fmt("%1$-3d %2$-20.20s %|25t|%3$-14d %|40t|%4$-14d %|54t|%5$-21.21s");
3028 // 1 2 3 4 5
3029 ret << (fmt % "#" % "Address" % "# ticket keys" % "Rotation delay" % "Next rotation") << endl;
3030 size_t counter = 0;
3031 for (const auto& ctx : g_tlslocals) {
3032 ret << (fmt % counter % ctx->d_addr.toStringWithPort() % ctx->getTicketsKeysCount() % ctx->getTicketsKeyRotationDelay() % ctx->getNextTicketsKeyRotation()) << endl;
3033 counter++;
3034 }
3035 g_outputBuffer = ret.str();
3036 }
3037 catch (const std::exception& e) {
3038 g_outputBuffer = e.what();
3039 throw;
3040 }
a227f47d 3041#else
69108ebb 3042 g_outputBuffer = "DNS over TLS support is not present!\n";
a227f47d 3043#endif
69108ebb 3044 });
a227f47d 3045
23a3bc71 3046 luaCtx.writeFunction("getTLSContext", [](uint64_t index) {
69108ebb 3047 std::shared_ptr<TLSCtx> result = nullptr;
a227f47d 3048#ifdef HAVE_DNS_OVER_TLS
69108ebb
RG
3049 setLuaNoSideEffect();
3050 try {
3051 if (index < g_tlslocals.size()) {
3052 result = g_tlslocals.at(index)->getContext();
3053 }
3054 else {
f8e1161f 3055 errlog("Error: trying to get TLS context with index %d but we only have %d context(s)\n", index, g_tlslocals.size());
69108ebb
RG
3056 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";
3057 }
3058 }
3059 catch (const std::exception& e) {
3060 g_outputBuffer = "Error while trying to get TLS context with index " + std::to_string(index) + ": " + string(e.what()) + "\n";
f8e1161f 3061 errlog("Error while trying to get TLS context with index %d: %s\n", index, string(e.what()));
69108ebb 3062 }
a227f47d
RG
3063#else
3064 g_outputBuffer="DNS over TLS support is not present!\n";
3065#endif
69108ebb
RG
3066 return result;
3067 });
a227f47d 3068
23a3bc71 3069 luaCtx.writeFunction("getTLSFrontend", [](uint64_t index) {
69108ebb 3070 std::shared_ptr<TLSFrontend> result = nullptr;
8ef43a02 3071#ifdef HAVE_DNS_OVER_TLS
69108ebb
RG
3072 setLuaNoSideEffect();
3073 try {
3074 if (index < g_tlslocals.size()) {
3075 result = g_tlslocals.at(index);
3076 }
3077 else {
f8e1161f 3078 errlog("Error: trying to get TLS frontend with index %d but we only have %d frontends\n", index, g_tlslocals.size());
69108ebb
RG
3079 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";
3080 }
3081 }
3082 catch (const std::exception& e) {
3083 g_outputBuffer = "Error while trying to get TLS frontend with index " + std::to_string(index) + ": " + string(e.what()) + "\n";
f8e1161f 3084 errlog("Error while trying to get TLS frontend with index %d: %s\n", index, string(e.what()));
69108ebb 3085 }
8ef43a02
RG
3086#else
3087 g_outputBuffer="DNS over TLS support is not present!\n";
3088#endif
69108ebb
RG
3089 return result;
3090 });
8ef43a02 3091
69108ebb
RG
3092 luaCtx.writeFunction("getTLSFrontendCount", []() {
3093 setLuaNoSideEffect();
3094 return g_tlslocals.size();
3095 });
265260f5 3096
69108ebb
RG
3097 luaCtx.registerFunction<void (std::shared_ptr<TLSCtx>::*)()>("rotateTicketsKey", [](std::shared_ptr<TLSCtx>& ctx) {
3098 if (ctx != nullptr) {
3099 ctx->rotateTicketsKey(time(nullptr));
3100 }
3101 });
a227f47d 3102
69108ebb
RG
3103 luaCtx.registerFunction<void (std::shared_ptr<TLSCtx>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<TLSCtx>& ctx, const std::string& file) {
3104 if (ctx != nullptr) {
3105 ctx->loadTicketsKeys(file);
3106 }
3107 });
8ef43a02 3108
9c591809 3109 luaCtx.registerFunction<std::string (std::shared_ptr<TLSFrontend>::*)() const>("getAddressAndPort", [](const std::shared_ptr<TLSFrontend>& frontend) {
59090737
RG
3110 if (frontend == nullptr) {
3111 return std::string();
3112 }
3113 return frontend->d_addr.toStringWithPort();
3114 });
3115
69108ebb
RG
3116 luaCtx.registerFunction<void (std::shared_ptr<TLSFrontend>::*)()>("rotateTicketsKey", [](std::shared_ptr<TLSFrontend>& frontend) {
3117 if (frontend == nullptr) {
3118 return;
3119 }
3120 auto ctx = frontend->getContext();
3121 if (ctx) {
3122 ctx->rotateTicketsKey(time(nullptr));
3123 }
3124 });
86593000 3125
69108ebb
RG
3126 luaCtx.registerFunction<void (std::shared_ptr<TLSFrontend>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<TLSFrontend>& frontend, const std::string& file) {
3127 if (frontend == nullptr) {
3128 return;
3129 }
3130 auto ctx = frontend->getContext();
3131 if (ctx) {
3132 ctx->loadTicketsKeys(file);
3133 }
3134 });
86593000 3135
69108ebb
RG
3136 luaCtx.registerFunction<void (std::shared_ptr<TLSFrontend>::*)()>("reloadCertificates", [](std::shared_ptr<TLSFrontend>& frontend) {
3137 if (frontend == nullptr) {
3138 return;
3139 }
3140 frontend->setupTLS();
3141 });
86593000 3142
bfb79abc 3143 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) {
9ce6a273 3144#ifdef HAVE_DNS_OVER_TLS
69108ebb
RG
3145 if (loadTLSCertificateAndKeys("TLSFrontend::loadNewCertificatesAndKeys", frontend->d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) {
3146 frontend->setupTLS();
3147 }
9ce6a273 3148#endif
69108ebb 3149 });
0dffe9e3 3150
69108ebb
RG
3151 luaCtx.writeFunction("reloadAllCertificates", []() {
3152 for (auto& frontend : g_frontends) {
3153 if (!frontend) {
3154 continue;
3155 }
3156 try {
bcc62bfb 3157#ifdef HAVE_DNSCRYPT
69108ebb
RG
3158 if (frontend->dnscryptCtx) {
3159 frontend->dnscryptCtx->reloadCertificates();
3160 }
bcc62bfb
RG
3161#endif /* HAVE_DNSCRYPT */
3162#ifdef HAVE_DNS_OVER_TLS
69108ebb
RG
3163 if (frontend->tlsFrontend) {
3164 frontend->tlsFrontend->setupTLS();
3165 }
bcc62bfb 3166#endif /* HAVE_DNS_OVER_TLS */
6e3a9fe4 3167#ifdef HAVE_DNS_OVER_HTTPS
69108ebb
RG
3168 if (frontend->dohFrontend) {
3169 frontend->dohFrontend->reloadCertificates();
bcc62bfb 3170 }
69108ebb
RG
3171#endif /* HAVE_DNS_OVER_HTTPS */
3172 }
3173 catch (const std::exception& e) {
3174 errlog("Error reloading certificates for frontend %s: %s", frontend->local.toStringWithPort(), e.what());
3175 }
3176 }
3177 });
bcc62bfb 3178
69108ebb
RG
3179 luaCtx.writeFunction("setAllowEmptyResponse", [](bool allow) { g_allowEmptyResponse = allow; });
3180 luaCtx.writeFunction("setDropEmptyQueries", [](bool drop) { extern bool g_dropEmptyQueries; g_dropEmptyQueries = drop; });
8c15553e 3181
2ba75ea7 3182#if defined(HAVE_LIBSSL) && defined(HAVE_OCSP_BASIC_SIGN) && !defined(DISABLE_OCSP_STAPLING)
69108ebb
RG
3183 luaCtx.writeFunction("generateOCSPResponse", [client](const std::string& certFile, const std::string& caCert, const std::string& caKey, const std::string& outFile, int ndays, int nmin) {
3184 if (client) {
3185 return;
3186 }
203b5348 3187
69108ebb
RG
3188 libssl_generate_ocsp_response(certFile, caCert, caKey, outFile, ndays, nmin);
3189 });
2ba75ea7 3190#endif /* HAVE_LIBSSL && HAVE_OCSP_BASIC_SIGN && !DISABLE_OCSP_STAPLING */
569b249d 3191
bfb79abc 3192 luaCtx.writeFunction("addCapabilitiesToRetain", [](LuaTypeOrArrayOf<std::string> caps) {
f727b3c6 3193 if (!checkConfigurationTime("addCapabilitiesToRetain")) {
569b249d
RG
3194 return;
3195 }
f727b3c6 3196 setLuaSideEffect();
569b249d
RG
3197 if (caps.type() == typeid(std::string)) {
3198 g_capabilitiesToRetain.insert(boost::get<std::string>(caps));
3199 }
bfb79abc
RG
3200 else if (caps.type() == typeid(LuaArray<std::string>)) {
3201 for (const auto& cap : boost::get<LuaArray<std::string>>(caps)) {
569b249d
RG
3202 g_capabilitiesToRetain.insert(cap.second);
3203 }
3204 }
3205 });
2ad23378
RG
3206
3207 luaCtx.writeFunction("setUDPSocketBufferSizes", [client](uint64_t recv, uint64_t snd) {
3208 if (client) {
3209 return;
3210 }
f727b3c6
RG
3211 if (!checkConfigurationTime("setUDPSocketBufferSizes")) {
3212 return;
3213 }
2ad23378
RG
3214 checkParameterBound("setUDPSocketBufferSizes", recv, std::numeric_limits<uint32_t>::max());
3215 checkParameterBound("setUDPSocketBufferSizes", snd, std::numeric_limits<uint32_t>::max());
3216 setLuaSideEffect();
3217
2ad23378
RG
3218 g_socketUDPSendBuffer = snd;
3219 g_socketUDPRecvBuffer = recv;
3220 });
489caa9f 3221
f573af85
RG
3222 luaCtx.writeFunction("setRandomizedOutgoingSockets", [](bool randomized) {
3223 DownstreamState::s_randomizeSockets = randomized;
3224 });
3225
ba958573
RG
3226 luaCtx.writeFunction("setRandomizedIdsOverUDP", [](bool randomized) {
3227 DownstreamState::s_randomizeIDs = randomized;
3228 });
3229
b7664acd 3230#if defined(HAVE_LIBSSL) && !defined(HAVE_TLS_PROVIDERS)
cec2df51
RG
3231 luaCtx.writeFunction("loadTLSEngine", [client](const std::string& engineName, boost::optional<std::string> defaultString) {
3232 if (client) {
3233 return;
3234 }
489caa9f 3235
cec2df51
RG
3236 auto [success, error] = libssl_load_engine(engineName, defaultString ? std::optional<std::string>(*defaultString) : std::nullopt);
3237 if (!success) {
3238 g_outputBuffer = "Error while trying to load TLS engine '" + engineName + "': " + error + "\n";
3239 errlog("Error while trying to load TLS engine '%s': %s", engineName, error);
3240 }
3241 });
b7664acd
FM
3242#endif /* HAVE_LIBSSL && !HAVE_TLS_PROVIDERS */
3243
3244#if defined(HAVE_LIBSSL) && OPENSSL_VERSION_MAJOR >= 3 && defined(HAVE_TLS_PROVIDERS)
3245 luaCtx.writeFunction("loadTLSProvider", [client](const std::string& providerName) {
3246 if (client) {
3247 return;
3248 }
3249
3250 auto [success, error] = libssl_load_provider(providerName);
3251 if (!success) {
3252 g_outputBuffer = "Error while trying to load TLS provider '" + providerName + "': " + error + "\n";
3253 errlog("Error while trying to load TLS provider '%s': %s", providerName, error);
3254 }
3255 });
3256#endif /* HAVE_LIBSSL && OPENSSL_VERSION_MAJOR >= 3 && HAVE_TLS_PROVIDERS */
74a2ea87 3257
271eb117
RG
3258 luaCtx.writeFunction("newThread", [client, configCheck](const std::string& code) {
3259 if (client || configCheck) {
3260 return;
3261 }
74a2ea87 3262 std::thread newThread(LuaThread, code);
055d1cec 3263
74a2ea87
PD
3264 newThread.detach();
3265 });
6211164a 3266
e78fc5bd 3267 luaCtx.writeFunction("declareMetric", [](const std::string& name, const std::string& type, const std::string& description, boost::optional<std::string> customName) {
67cbba12
RG
3268 auto result = dnsdist::metrics::declareCustomMetric(name, type, description, customName ? std::optional<std::string>(*customName) : std::nullopt);
3269 if (result) {
3270 g_outputBuffer += *result + "\n";
f8e1161f 3271 errlog("Error in declareMetric: %s", *result);
6211164a
CHB
3272 return false;
3273 }
3274 return true;
3275 });
54c1bc22 3276 luaCtx.writeFunction("incMetric", [](const std::string& name, boost::optional<uint64_t> step) {
67cbba12
RG
3277 auto result = dnsdist::metrics::incrementCustomCounter(name, step ? *step : 1);
3278 if (const auto* errorStr = std::get_if<dnsdist::metrics::Error>(&result)) {
3279 g_outputBuffer = *errorStr + "'\n";
f8e1161f 3280 errlog("Error in incMetric: %s", *errorStr);
67cbba12 3281 return static_cast<uint64_t>(0);
6211164a 3282 }
67cbba12 3283 return std::get<uint64_t>(result);
6211164a 3284 });
67cbba12
RG
3285 luaCtx.writeFunction("decMetric", [](const std::string& name, boost::optional<uint64_t> step) {
3286 auto result = dnsdist::metrics::decrementCustomCounter(name, step ? *step : 1);
3287 if (const auto* errorStr = std::get_if<dnsdist::metrics::Error>(&result)) {
3288 g_outputBuffer = *errorStr + "'\n";
f8e1161f 3289 errlog("Error in decMetric: %s", *errorStr);
67cbba12 3290 return static_cast<uint64_t>(0);
6211164a 3291 }
67cbba12 3292 return std::get<uint64_t>(result);
6211164a 3293 });
54c1bc22 3294 luaCtx.writeFunction("setMetric", [](const std::string& name, const double value) -> double {
67cbba12
RG
3295 auto result = dnsdist::metrics::setCustomGauge(name, value);
3296 if (const auto* errorStr = std::get_if<dnsdist::metrics::Error>(&result)) {
3297 g_outputBuffer = *errorStr + "'\n";
f8e1161f 3298 errlog("Error in setMetric: %s", *errorStr);
67cbba12 3299 return 0.;
6211164a 3300 }
67cbba12 3301 return std::get<double>(result);
6211164a
CHB
3302 });
3303 luaCtx.writeFunction("getMetric", [](const std::string& name) {
67cbba12
RG
3304 auto result = dnsdist::metrics::getCustomMetric(name);
3305 if (const auto* errorStr = std::get_if<dnsdist::metrics::Error>(&result)) {
3306 g_outputBuffer = *errorStr + "'\n";
f8e1161f 3307 errlog("Error in getMetric: %s", *errorStr);
67cbba12 3308 return 0.;
6211164a 3309 }
67cbba12 3310 return std::get<double>(result);
6211164a 3311 });
6bb38cd6 3312}
2a817e5a 3313
fd51c832 3314vector<std::function<void(void)>> setupLua(LuaContext& luaCtx, bool client, bool configCheck, const std::string& config)
6bb38cd6 3315{
fd51c832
RG
3316 // this needs to exist only during the parsing of the configuration
3317 // and cannot be captured by lambdas
3318 g_launchWork = std::vector<std::function<void(void)>>();
3319
3320 setupLuaActions(luaCtx);
3321 setupLuaConfig(luaCtx, client, configCheck);
b8df8675 3322 setupLuaBindings(luaCtx, client, configCheck);
c0bc108f 3323 setupLuaBindingsDNSCrypt(luaCtx, client);
3669b961 3324 setupLuaBindingsDNSParser(luaCtx);
fd51c832
RG
3325 setupLuaBindingsDNSQuestion(luaCtx);
3326 setupLuaBindingsKVS(luaCtx, client);
67ed7d3e 3327 setupLuaBindingsNetwork(luaCtx, client);
a093b261 3328 setupLuaBindingsPacketCache(luaCtx, client);
fd51c832 3329 setupLuaBindingsProtoBuf(luaCtx, client, configCheck);
b1bea54a 3330 setupLuaBindingsRings(luaCtx, client);
fd51c832
RG
3331 setupLuaInspection(luaCtx);
3332 setupLuaRules(luaCtx);
3333 setupLuaVars(luaCtx);
88d4fe87 3334 setupLuaWeb(luaCtx);
80a216c9 3335
0ed8f0fa 3336#ifdef LUAJIT_VERSION
fd51c832 3337 luaCtx.executeCode(getLuaFFIWrappers());
0ed8f0fa
RG
3338#endif
3339
839f3021 3340 std::ifstream ifs(config);
8b053968
RG
3341 if (!ifs) {
3342 if (configCheck) {
3343 throw std::runtime_error("Unable to read configuration file from " + config);
3344 }
3345 else {
3346 warnlog("Unable to read configuration from '%s'", config);
3347 }
3348 }
3349 else {
cdc04ede 3350 vinfolog("Read configuration from '%s'", config);
8b053968 3351 }
df111b53 3352
fd51c832 3353 luaCtx.executeCode(ifs);
d8c19b98 3354
6bb38cd6 3355 auto ret = *g_launchWork;
fd51c832 3356 g_launchWork = boost::none;
2e72cc0e 3357 return ret;
df111b53 3358}