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