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