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