]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsdist-lua.cc
Merge pull request #13642 from zeha/auth-make-outoftree
[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-dynblocks.hh"
43 #include "dnsdist-discovery.hh"
44 #include "dnsdist-ecs.hh"
45 #include "dnsdist-healthchecks.hh"
46 #include "dnsdist-lua.hh"
47 #ifdef LUAJIT_VERSION
48 #include "dnsdist-lua-ffi.hh"
49 #endif /* LUAJIT_VERSION */
50 #include "dnsdist-metrics.hh"
51 #include "dnsdist-nghttp2.hh"
52 #include "dnsdist-proxy-protocol.hh"
53 #include "dnsdist-rings.hh"
54 #include "dnsdist-secpoll.hh"
55 #include "dnsdist-session-cache.hh"
56 #include "dnsdist-tcp-downstream.hh"
57 #include "dnsdist-web.hh"
58
59 #include "base64.hh"
60 #include "coverage.hh"
61 #include "doh.hh"
62 #include "doq-common.hh"
63 #include "dolog.hh"
64 #include "sodcrypto.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 #ifdef HAVE_LIBSODIUM
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 int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
1117 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
1118 SBind(sock, local);
1119 SListen(sock, 5);
1120 auto launch = [sock, local]() {
1121 thread t(controlThread, sock, local);
1122 t.detach();
1123 };
1124 if (g_launchWork) {
1125 g_launchWork->push_back(launch);
1126 }
1127 else {
1128 launch();
1129 }
1130 }
1131 catch (std::exception& e) {
1132 g_outputBuffer = "Unable to bind to control socket on " + local.toStringWithPort() + ": " + e.what();
1133 errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what());
1134 }
1135 });
1136
1137 luaCtx.writeFunction("addConsoleACL", [](const std::string& netmask) {
1138 setLuaSideEffect();
1139 #ifndef HAVE_LIBSODIUM
1140 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
1141 #endif
1142
1143 g_consoleACL.modify([netmask](NetmaskGroup& nmg) { nmg.addMask(netmask); });
1144 });
1145
1146 luaCtx.writeFunction("setConsoleACL", [](LuaTypeOrArrayOf<std::string> inp) {
1147 setLuaSideEffect();
1148
1149 #ifndef HAVE_LIBSODIUM
1150 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
1151 #endif
1152
1153 NetmaskGroup nmg;
1154 if (auto str = boost::get<string>(&inp)) {
1155 nmg.addMask(*str);
1156 }
1157 else
1158 for (const auto& p : boost::get<LuaArray<std::string>>(inp)) {
1159 nmg.addMask(p.second);
1160 }
1161 g_consoleACL.setState(nmg);
1162 });
1163
1164 luaCtx.writeFunction("showConsoleACL", []() {
1165 setLuaNoSideEffect();
1166
1167 #ifndef HAVE_LIBSODIUM
1168 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
1169 #endif
1170
1171 auto aclEntries = g_consoleACL.getLocal()->toStringVector();
1172
1173 for (const auto& entry : aclEntries) {
1174 g_outputBuffer += entry + "\n";
1175 }
1176 });
1177
1178 luaCtx.writeFunction("setConsoleMaximumConcurrentConnections", [](uint64_t max) {
1179 setLuaSideEffect();
1180 setConsoleMaximumConcurrentConnections(max);
1181 });
1182
1183 luaCtx.writeFunction("clearQueryCounters", []() {
1184 unsigned int size{0};
1185 {
1186 auto records = g_qcount.records.write_lock();
1187 size = records->size();
1188 records->clear();
1189 }
1190
1191 boost::format fmt("%d records cleared from query counter buffer\n");
1192 g_outputBuffer = (fmt % size).str();
1193 });
1194
1195 luaCtx.writeFunction("getQueryCounters", [](boost::optional<uint64_t> optMax) {
1196 setLuaNoSideEffect();
1197 auto records = g_qcount.records.read_lock();
1198 g_outputBuffer = "query counting is currently: ";
1199 g_outputBuffer += g_qcount.enabled ? "enabled" : "disabled";
1200 g_outputBuffer += (boost::format(" (%d records in buffer)\n") % records->size()).str();
1201
1202 boost::format fmt("%-3d %s: %d request(s)\n");
1203 uint64_t max = optMax ? *optMax : 10U;
1204 uint64_t index{1};
1205 for (auto it = records->begin(); it != records->end() && index <= max; ++it, ++index) {
1206 g_outputBuffer += (fmt % index % it->first % it->second).str();
1207 }
1208 });
1209
1210 luaCtx.writeFunction("setQueryCount", [](bool enabled) { g_qcount.enabled = enabled; });
1211
1212 luaCtx.writeFunction("setQueryCountFilter", [](QueryCountFilter func) {
1213 g_qcount.filter = std::move(func);
1214 });
1215
1216 luaCtx.writeFunction("makeKey", []() {
1217 setLuaNoSideEffect();
1218 g_outputBuffer = "setKey(" + newKey() + ")\n";
1219 });
1220
1221 luaCtx.writeFunction("setKey", [](const std::string& key) {
1222 if (!g_configurationDone && !g_consoleKey.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
1223 return; // but later setKeys() trump the -k value again
1224 }
1225 #ifndef HAVE_LIBSODIUM
1226 warnlog("Calling setKey() while libsodium support has not been enabled is not secure, and will result in cleartext communications");
1227 #endif
1228
1229 setLuaSideEffect();
1230 string newkey;
1231 if (B64Decode(key, newkey) < 0) {
1232 g_outputBuffer = string("Unable to decode ") + key + " as Base64";
1233 errlog("%s", g_outputBuffer);
1234 }
1235 else
1236 g_consoleKey = std::move(newkey);
1237 });
1238
1239 luaCtx.writeFunction("clearConsoleHistory", []() {
1240 clearConsoleHistory();
1241 });
1242
1243 luaCtx.writeFunction("testCrypto", [](boost::optional<string> optTestMsg) {
1244 setLuaNoSideEffect();
1245 #ifdef HAVE_LIBSODIUM
1246 try {
1247 string testmsg;
1248
1249 if (optTestMsg) {
1250 testmsg = *optTestMsg;
1251 }
1252 else {
1253 testmsg = "testStringForCryptoTests";
1254 }
1255
1256 SodiumNonce sn, sn2;
1257 sn.init();
1258 sn2 = sn;
1259 string encrypted = sodEncryptSym(testmsg, g_consoleKey, sn);
1260 string decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2);
1261
1262 sn.increment();
1263 sn2.increment();
1264
1265 encrypted = sodEncryptSym(testmsg, g_consoleKey, sn);
1266 decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2);
1267
1268 if (testmsg == decrypted)
1269 g_outputBuffer = "Everything is ok!\n";
1270 else
1271 g_outputBuffer = "Crypto failed.. (the decoded value does not match the cleartext one)\n";
1272 }
1273 catch (const std::exception& e) {
1274 g_outputBuffer = "Crypto failed: " + std::string(e.what()) + "\n";
1275 }
1276 catch (...) {
1277 g_outputBuffer = "Crypto failed..\n";
1278 }
1279 #else
1280 g_outputBuffer = "Crypto not available.\n";
1281 #endif
1282 });
1283
1284 luaCtx.writeFunction("setTCPRecvTimeout", [](int timeout) { g_tcpRecvTimeout = timeout; });
1285
1286 luaCtx.writeFunction("setTCPSendTimeout", [](int timeout) { g_tcpSendTimeout = timeout; });
1287
1288 luaCtx.writeFunction("setUDPTimeout", [](int timeout) { DownstreamState::s_udpTimeout = timeout; });
1289
1290 luaCtx.writeFunction("setMaxUDPOutstanding", [](uint64_t max) {
1291 if (!checkConfigurationTime("setMaxUDPOutstanding")) {
1292 return;
1293 }
1294
1295 checkParameterBound("setMaxUDPOutstanding", max);
1296 g_maxOutstanding = max;
1297 });
1298
1299 luaCtx.writeFunction("setMaxTCPClientThreads", [](uint64_t max) {
1300 if (!checkConfigurationTime("setMaxTCPClientThreads")) {
1301 return;
1302 }
1303 g_maxTCPClientThreads = max;
1304 });
1305
1306 luaCtx.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max) {
1307 if (!checkConfigurationTime("setMaxTCPQueuedConnections")) {
1308 return;
1309 }
1310 g_maxTCPQueuedConnections = max;
1311 });
1312
1313 luaCtx.writeFunction("setMaxTCPQueriesPerConnection", [](uint64_t max) {
1314 if (!checkConfigurationTime("setMaxTCPQueriesPerConnection")) {
1315 return;
1316 }
1317 g_maxTCPQueriesPerConn = max;
1318 });
1319
1320 luaCtx.writeFunction("setMaxTCPConnectionsPerClient", [](uint64_t max) {
1321 if (!checkConfigurationTime("setMaxTCPConnectionsPerClient")) {
1322 return;
1323 }
1324 dnsdist::IncomingConcurrentTCPConnectionsManager::setMaxTCPConnectionsPerClient(max);
1325 });
1326
1327 luaCtx.writeFunction("setMaxTCPConnectionDuration", [](uint64_t max) {
1328 if (!checkConfigurationTime("setMaxTCPConnectionDuration")) {
1329 return;
1330 }
1331 g_maxTCPConnectionDuration = max;
1332 });
1333
1334 luaCtx.writeFunction("setMaxCachedTCPConnectionsPerDownstream", [](uint64_t max) {
1335 setTCPDownstreamMaxIdleConnectionsPerBackend(max);
1336 });
1337
1338 #if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
1339 luaCtx.writeFunction("setMaxIdleDoHConnectionsPerDownstream", [](uint64_t max) {
1340 setDoHDownstreamMaxIdleConnectionsPerBackend(max);
1341 });
1342
1343 luaCtx.writeFunction("setOutgoingDoHWorkerThreads", [](uint64_t workers) {
1344 if (!checkConfigurationTime("setOutgoingDoHWorkerThreads")) {
1345 return;
1346 }
1347 g_outgoingDoHWorkerThreads = workers;
1348 });
1349 #endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
1350
1351 luaCtx.writeFunction("setOutgoingTLSSessionsCacheMaxTicketsPerBackend", [](uint64_t max) {
1352 if (!checkConfigurationTime("setOutgoingTLSSessionsCacheMaxTicketsPerBackend")) {
1353 return;
1354 }
1355 TLSSessionCache::setMaxTicketsPerBackend(max);
1356 });
1357
1358 luaCtx.writeFunction("setOutgoingTLSSessionsCacheCleanupDelay", [](time_t delay) {
1359 if (!checkConfigurationTime("setOutgoingTLSSessionsCacheCleanupDelay")) {
1360 return;
1361 }
1362 TLSSessionCache::setCleanupDelay(delay);
1363 });
1364
1365 luaCtx.writeFunction("setOutgoingTLSSessionsCacheMaxTicketValidity", [](time_t validity) {
1366 if (!checkConfigurationTime("setOutgoingTLSSessionsCacheMaxTicketValidity")) {
1367 return;
1368 }
1369 TLSSessionCache::setSessionValidity(validity);
1370 });
1371
1372 luaCtx.writeFunction("getOutgoingTLSSessionCacheSize", []() {
1373 setLuaNoSideEffect();
1374 return g_sessionCache.getSize();
1375 });
1376
1377 luaCtx.writeFunction("setCacheCleaningDelay", [](uint64_t delay) {
1378 checkParameterBound("setCacheCleaningDelay", delay, std::numeric_limits<uint32_t>::max());
1379 g_cacheCleaningDelay = delay;
1380 });
1381
1382 luaCtx.writeFunction("setCacheCleaningPercentage", [](uint64_t percentage) { if (percentage < 100) g_cacheCleaningPercentage = percentage; else g_cacheCleaningPercentage = 100; });
1383
1384 luaCtx.writeFunction("setECSSourcePrefixV4", [](uint64_t prefix) {
1385 checkParameterBound("setECSSourcePrefixV4", prefix, std::numeric_limits<uint16_t>::max());
1386 g_ECSSourcePrefixV4 = prefix;
1387 });
1388
1389 luaCtx.writeFunction("setECSSourcePrefixV6", [](uint64_t prefix) {
1390 checkParameterBound("setECSSourcePrefixV6", prefix, std::numeric_limits<uint16_t>::max());
1391 g_ECSSourcePrefixV6 = prefix;
1392 });
1393
1394 luaCtx.writeFunction("setECSOverride", [](bool override) { g_ECSOverride = override; });
1395
1396 #ifndef DISABLE_DYNBLOCKS
1397 luaCtx.writeFunction("showDynBlocks", []() {
1398 setLuaNoSideEffect();
1399 auto slow = g_dynblockNMG.getCopy();
1400 struct timespec now;
1401 gettime(&now);
1402 boost::format fmt("%-24s %8d %8d %-10s %-20s %-10s %s\n");
1403 g_outputBuffer = (fmt % "What" % "Seconds" % "Blocks" % "Warning" % "Action" % "eBPF" % "Reason").str();
1404 for (const auto& e : slow) {
1405 if (now < e.second.until) {
1406 uint64_t counter = e.second.blocks;
1407 if (g_defaultBPFFilter && e.second.bpf) {
1408 counter += g_defaultBPFFilter->getHits(e.first.getNetwork());
1409 }
1410 g_outputBuffer += (fmt % e.first.toString() % (e.second.until.tv_sec - now.tv_sec) % counter % (e.second.warning ? "true" : "false") % DNSAction::typeToString(e.second.action != DNSAction::Action::None ? e.second.action : g_dynBlockAction) % (g_defaultBPFFilter && e.second.bpf ? "*" : "") % e.second.reason).str();
1411 }
1412 }
1413 auto slow2 = g_dynblockSMT.getCopy();
1414 slow2.visit([&now, &fmt](const SuffixMatchTree<DynBlock>& node) {
1415 if (now < node.d_value.until) {
1416 string dom("empty");
1417 if (!node.d_value.domain.empty())
1418 dom = node.d_value.domain.toString();
1419 g_outputBuffer += (fmt % dom % (node.d_value.until.tv_sec - now.tv_sec) % node.d_value.blocks % (node.d_value.warning ? "true" : "false") % DNSAction::typeToString(node.d_value.action != DNSAction::Action::None ? node.d_value.action : g_dynBlockAction) % "" % node.d_value.reason).str();
1420 }
1421 });
1422 });
1423
1424 luaCtx.writeFunction("getDynamicBlocks", []() {
1425 setLuaNoSideEffect();
1426 struct timespec now
1427 {
1428 };
1429 gettime(&now);
1430
1431 LuaAssociativeTable<DynBlock> entries;
1432 auto fullCopy = g_dynblockNMG.getCopy();
1433 for (const auto& blockPair : fullCopy) {
1434 const auto& requestor = blockPair.first;
1435 if (!(now < blockPair.second.until)) {
1436 continue;
1437 }
1438 auto entry = blockPair.second;
1439 if (g_defaultBPFFilter && entry.bpf) {
1440 entry.blocks += g_defaultBPFFilter->getHits(requestor.getNetwork());
1441 }
1442 if (entry.action == DNSAction::Action::None) {
1443 entry.action = g_dynBlockAction;
1444 }
1445 entries.emplace(requestor.toString(), std::move(entry));
1446 }
1447 return entries;
1448 });
1449
1450 luaCtx.writeFunction("getDynamicBlocksSMT", []() {
1451 setLuaNoSideEffect();
1452 struct timespec now
1453 {
1454 };
1455 gettime(&now);
1456
1457 LuaAssociativeTable<DynBlock> entries;
1458 auto fullCopy = g_dynblockSMT.getCopy();
1459 fullCopy.visit([&now, &entries](const SuffixMatchTree<DynBlock>& node) {
1460 if (!(now < node.d_value.until)) {
1461 return;
1462 }
1463 auto entry = node.d_value;
1464 string key("empty");
1465 if (!entry.domain.empty()) {
1466 key = entry.domain.toString();
1467 }
1468 if (entry.action == DNSAction::Action::None) {
1469 entry.action = g_dynBlockAction;
1470 }
1471 entries.emplace(std::move(key), std::move(entry));
1472 });
1473 return entries;
1474 });
1475
1476 luaCtx.writeFunction("clearDynBlocks", []() {
1477 setLuaSideEffect();
1478 nmts_t nmg;
1479 g_dynblockNMG.setState(nmg);
1480 SuffixMatchTree<DynBlock> smt;
1481 g_dynblockSMT.setState(smt);
1482 });
1483
1484 #ifndef DISABLE_DEPRECATED_DYNBLOCK
1485 luaCtx.writeFunction("addDynBlocks",
1486 [](const std::unordered_map<ComboAddress, unsigned int, ComboAddress::addressOnlyHash, ComboAddress::addressOnlyEqual>& m, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
1487 if (m.empty()) {
1488 return;
1489 }
1490 setLuaSideEffect();
1491 auto slow = g_dynblockNMG.getCopy();
1492 struct timespec until, now;
1493 gettime(&now);
1494 until = now;
1495 int actualSeconds = seconds ? *seconds : 10;
1496 until.tv_sec += actualSeconds;
1497 for (const auto& capair : m) {
1498 unsigned int count = 0;
1499 /* this legacy interface does not support ranges or ports, use DynBlockRulesGroup instead */
1500 AddressAndPortRange requestor(capair.first, capair.first.isIPv4() ? 32 : 128, 0);
1501 auto got = slow.lookup(requestor);
1502 bool expired = false;
1503 if (got) {
1504 if (until < got->second.until) {
1505 // had a longer policy
1506 continue;
1507 }
1508 if (now < got->second.until) {
1509 // only inherit count on fresh query we are extending
1510 count = got->second.blocks;
1511 }
1512 else {
1513 expired = true;
1514 }
1515 }
1516 DynBlock db{msg, until, DNSName(), (action ? *action : DNSAction::Action::None)};
1517 db.blocks = count;
1518 if (!got || expired) {
1519 warnlog("Inserting dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg);
1520 }
1521 slow.insert(requestor).second = std::move(db);
1522 }
1523 g_dynblockNMG.setState(slow);
1524 });
1525
1526 luaCtx.writeFunction("setDynBlocksAction", [](DNSAction::Action action) {
1527 if (!checkConfigurationTime("setDynBlocksAction")) {
1528 return;
1529 }
1530 if (action == DNSAction::Action::Drop || action == DNSAction::Action::NoOp || action == DNSAction::Action::Nxdomain || action == DNSAction::Action::Refused || action == DNSAction::Action::Truncate || action == DNSAction::Action::NoRecurse) {
1531 g_dynBlockAction = action;
1532 }
1533 else {
1534 errlog("Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!");
1535 g_outputBuffer = "Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!\n";
1536 }
1537 });
1538 #endif /* DISABLE_DEPRECATED_DYNBLOCK */
1539
1540 luaCtx.writeFunction("addDynBlockSMT",
1541 [](const LuaArray<std::string>& names, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
1542 if (names.empty()) {
1543 return;
1544 }
1545 setLuaSideEffect();
1546 struct timespec now
1547 {
1548 };
1549 gettime(&now);
1550 unsigned int actualSeconds = seconds ? *seconds : 10;
1551
1552 bool needUpdate = false;
1553 auto slow = g_dynblockSMT.getCopy();
1554 for (const auto& capair : names) {
1555 DNSName domain(capair.second);
1556 domain.makeUsLowerCase();
1557
1558 if (dnsdist::DynamicBlocks::addOrRefreshBlockSMT(slow, now, domain, msg, actualSeconds, action ? *action : DNSAction::Action::None, false)) {
1559 needUpdate = true;
1560 }
1561 }
1562
1563 if (needUpdate) {
1564 g_dynblockSMT.setState(slow);
1565 }
1566 });
1567
1568 luaCtx.writeFunction("addDynamicBlock",
1569 [](const boost::variant<ComboAddress, std::string>& clientIP, const std::string& msg, const boost::optional<DNSAction::Action> action, const boost::optional<int> seconds, boost::optional<uint8_t> clientIPMask, boost::optional<uint8_t> clientIPPortMask) {
1570 setLuaSideEffect();
1571
1572 ComboAddress clientIPCA;
1573 if (clientIP.type() == typeid(ComboAddress)) {
1574 clientIPCA = boost::get<ComboAddress>(clientIP);
1575 }
1576 else {
1577 const auto& clientIPStr = boost::get<std::string>(clientIP);
1578 try {
1579 clientIPCA = ComboAddress(clientIPStr);
1580 }
1581 catch (const std::exception& exp) {
1582 errlog("addDynamicBlock: Unable to parse '%s': %s", clientIPStr, exp.what());
1583 return;
1584 }
1585 catch (const PDNSException& exp) {
1586 errlog("addDynamicBlock: Unable to parse '%s': %s", clientIPStr, exp.reason);
1587 return;
1588 }
1589 }
1590 AddressAndPortRange target(clientIPCA, clientIPMask ? *clientIPMask : (clientIPCA.isIPv4() ? 32 : 128), clientIPPortMask ? *clientIPPortMask : 0);
1591 unsigned int actualSeconds = seconds ? *seconds : 10;
1592
1593 struct timespec now
1594 {
1595 };
1596 gettime(&now);
1597 auto slow = g_dynblockNMG.getCopy();
1598 if (dnsdist::DynamicBlocks::addOrRefreshBlock(slow, now, target, msg, actualSeconds, action ? *action : DNSAction::Action::None, false, false)) {
1599 g_dynblockNMG.setState(slow);
1600 }
1601 });
1602
1603 luaCtx.writeFunction("setDynBlocksPurgeInterval", [](uint64_t interval) {
1604 DynBlockMaintenance::s_expiredDynBlocksPurgeInterval = interval;
1605 });
1606 #endif /* DISABLE_DYNBLOCKS */
1607
1608 #ifdef HAVE_DNSCRYPT
1609 luaCtx.writeFunction("addDNSCryptBind", [](const std::string& addr, const std::string& providerName, LuaTypeOrArrayOf<std::string> certFiles, LuaTypeOrArrayOf<std::string> keyFiles, boost::optional<localbind_t> vars) {
1610 if (!checkConfigurationTime("addDNSCryptBind")) {
1611 return;
1612 }
1613 bool reusePort = false;
1614 int tcpFastOpenQueueSize = 0;
1615 int tcpListenQueueSize = 0;
1616 uint64_t maxInFlightQueriesPerConn = 0;
1617 uint64_t tcpMaxConcurrentConnections = 0;
1618 std::string interface;
1619 std::set<int> cpus;
1620 std::vector<DNSCryptContext::CertKeyPaths> certKeys;
1621 bool enableProxyProtocol = true;
1622
1623 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol);
1624 checkAllParametersConsumed("addDNSCryptBind", vars);
1625
1626 if (certFiles.type() == typeid(std::string) && keyFiles.type() == typeid(std::string)) {
1627 auto certFile = boost::get<std::string>(certFiles);
1628 auto keyFile = boost::get<std::string>(keyFiles);
1629 certKeys.push_back({std::move(certFile), std::move(keyFile)});
1630 }
1631 else if (certFiles.type() == typeid(LuaArray<std::string>) && keyFiles.type() == typeid(LuaArray<std::string>)) {
1632 auto certFilesVect = boost::get<LuaArray<std::string>>(certFiles);
1633 auto keyFilesVect = boost::get<LuaArray<std::string>>(keyFiles);
1634 if (certFilesVect.size() == keyFilesVect.size()) {
1635 for (size_t idx = 0; idx < certFilesVect.size(); idx++) {
1636 certKeys.push_back({certFilesVect.at(idx).second, keyFilesVect.at(idx).second});
1637 }
1638 }
1639 else {
1640 errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind!");
1641 g_outputBuffer = "Error, mismatching number of certificates and keys in call to addDNSCryptBind()!";
1642 return;
1643 }
1644 }
1645 else {
1646 errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind()!");
1647 g_outputBuffer = "Error, mismatching number of certificates and keys in call to addDNSCryptBind()!";
1648 return;
1649 }
1650
1651 try {
1652 auto ctx = std::make_shared<DNSCryptContext>(providerName, certKeys);
1653
1654 /* UDP */
1655 auto clientState = std::make_unique<ClientState>(ComboAddress(addr, 443), false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol);
1656 clientState->dnscryptCtx = ctx;
1657 g_dnsCryptLocals.push_back(ctx);
1658 g_frontends.push_back(std::move(clientState));
1659
1660 /* TCP */
1661 clientState = std::make_unique<ClientState>(ComboAddress(addr, 443), true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol);
1662 clientState->dnscryptCtx = std::move(ctx);
1663 if (tcpListenQueueSize > 0) {
1664 clientState->tcpListenQueueSize = tcpListenQueueSize;
1665 }
1666 if (maxInFlightQueriesPerConn > 0) {
1667 clientState->d_maxInFlightQueriesPerConn = maxInFlightQueriesPerConn;
1668 }
1669 if (tcpMaxConcurrentConnections > 0) {
1670 clientState->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections;
1671 }
1672
1673 g_frontends.push_back(std::move(clientState));
1674 }
1675 catch (const std::exception& e) {
1676 errlog("Error during addDNSCryptBind() processing: %s", e.what());
1677 g_outputBuffer = "Error during addDNSCryptBind() processing: " + string(e.what()) + "\n";
1678 }
1679 });
1680
1681 luaCtx.writeFunction("showDNSCryptBinds", []() {
1682 setLuaNoSideEffect();
1683 ostringstream ret;
1684 boost::format fmt("%1$-3d %2% %|25t|%3$-20.20s");
1685 ret << (fmt % "#" % "Address" % "Provider Name") << endl;
1686 size_t idx = 0;
1687
1688 std::unordered_set<std::shared_ptr<DNSCryptContext>> contexts;
1689 for (const auto& frontend : g_frontends) {
1690 const std::shared_ptr<DNSCryptContext> ctx = frontend->dnscryptCtx;
1691 if (!ctx || contexts.count(ctx) != 0) {
1692 continue;
1693 }
1694 contexts.insert(ctx);
1695 ret << (fmt % idx % frontend->local.toStringWithPort() % ctx->getProviderName()) << endl;
1696 idx++;
1697 }
1698
1699 g_outputBuffer = ret.str();
1700 });
1701
1702 luaCtx.writeFunction("getDNSCryptBind", [](uint64_t idx) {
1703 setLuaNoSideEffect();
1704 std::shared_ptr<DNSCryptContext> ret = nullptr;
1705 if (idx < g_dnsCryptLocals.size()) {
1706 ret = g_dnsCryptLocals.at(idx);
1707 }
1708 return ret;
1709 });
1710
1711 luaCtx.writeFunction("getDNSCryptBindCount", []() {
1712 setLuaNoSideEffect();
1713 return g_dnsCryptLocals.size();
1714 });
1715 #endif /* HAVE_DNSCRYPT */
1716
1717 luaCtx.writeFunction("showPools", []() {
1718 setLuaNoSideEffect();
1719 try {
1720 ostringstream ret;
1721 boost::format fmt("%1$-20.20s %|25t|%2$20s %|25t|%3$20s %|50t|%4%");
1722 // 1 2 3 4
1723 ret << (fmt % "Name" % "Cache" % "ServerPolicy" % "Servers") << endl;
1724
1725 const auto localPools = g_pools.getCopy();
1726 for (const auto& entry : localPools) {
1727 const string& name = entry.first;
1728 const std::shared_ptr<ServerPool> pool = entry.second;
1729 string cache = pool->packetCache != nullptr ? pool->packetCache->toString() : "";
1730 string policy = g_policy.getLocal()->getName();
1731 if (pool->policy != nullptr) {
1732 policy = pool->policy->getName();
1733 }
1734 string servers;
1735
1736 const auto poolServers = pool->getServers();
1737 for (const auto& server : *poolServers) {
1738 if (!servers.empty()) {
1739 servers += ", ";
1740 }
1741 if (!server.second->getName().empty()) {
1742 servers += server.second->getName();
1743 servers += " ";
1744 }
1745 servers += server.second->d_config.remote.toStringWithPort();
1746 }
1747
1748 ret << (fmt % name % cache % policy % servers) << endl;
1749 }
1750 g_outputBuffer = ret.str();
1751 }
1752 catch (std::exception& e) {
1753 g_outputBuffer = e.what();
1754 throw;
1755 }
1756 });
1757
1758 luaCtx.writeFunction("getPoolNames", []() {
1759 setLuaNoSideEffect();
1760 LuaArray<std::string> ret;
1761 int count = 1;
1762 const auto localPools = g_pools.getCopy();
1763 for (const auto& entry : localPools) {
1764 const string& name = entry.first;
1765 ret.emplace_back(count++, name);
1766 }
1767 return ret;
1768 });
1769
1770 luaCtx.writeFunction("getPool", [client](const string& poolName) {
1771 if (client) {
1772 return std::make_shared<ServerPool>();
1773 }
1774 auto localPools = g_pools.getCopy();
1775 std::shared_ptr<ServerPool> pool = createPoolIfNotExists(localPools, poolName);
1776 g_pools.setState(localPools);
1777 return pool;
1778 });
1779
1780 luaCtx.writeFunction("setVerbose", [](bool verbose) { g_verbose = verbose; });
1781 luaCtx.writeFunction("getVerbose", []() { return g_verbose; });
1782 luaCtx.writeFunction("setVerboseHealthChecks", [](bool verbose) { g_verboseHealthChecks = verbose; });
1783 luaCtx.writeFunction("setVerboseLogDestination", [](const std::string& dest) {
1784 if (!checkConfigurationTime("setVerboseLogDestination")) {
1785 return;
1786 }
1787 try {
1788 auto stream = std::ofstream(dest.c_str());
1789 dnsdist::logging::LoggingConfiguration::setVerboseStream(std::move(stream));
1790 }
1791 catch (const std::exception& e) {
1792 errlog("Error while opening the verbose logging destination file %s: %s", dest, e.what());
1793 }
1794 });
1795 luaCtx.writeFunction("setStructuredLogging", [](bool enable, boost::optional<LuaAssociativeTable<std::string>> options) {
1796 std::string levelPrefix;
1797 std::string timeFormat;
1798 if (options) {
1799 getOptionalValue<std::string>(options, "levelPrefix", levelPrefix);
1800 if (getOptionalValue<std::string>(options, "timeFormat", timeFormat) == 1) {
1801 if (timeFormat == "numeric") {
1802 dnsdist::logging::LoggingConfiguration::setStructuredTimeFormat(dnsdist::logging::LoggingConfiguration::TimeFormat::Numeric);
1803 }
1804 else if (timeFormat == "ISO8601") {
1805 dnsdist::logging::LoggingConfiguration::setStructuredTimeFormat(dnsdist::logging::LoggingConfiguration::TimeFormat::ISO8601);
1806 }
1807 else {
1808 warnlog("Unknown value '%s' to setStructuredLogging's 'timeFormat' parameter", timeFormat);
1809 }
1810 }
1811 checkAllParametersConsumed("setStructuredLogging", options);
1812 }
1813
1814 dnsdist::logging::LoggingConfiguration::setStructuredLogging(enable, levelPrefix);
1815 });
1816
1817 luaCtx.writeFunction("setStaleCacheEntriesTTL", [](uint64_t ttl) {
1818 checkParameterBound("setStaleCacheEntriesTTL", ttl, std::numeric_limits<uint32_t>::max());
1819 g_staleCacheEntriesTTL = ttl;
1820 });
1821
1822 luaCtx.writeFunction("showBinds", []() {
1823 setLuaNoSideEffect();
1824 try {
1825 ostringstream ret;
1826 boost::format fmt("%1$-3d %2$-20.20s %|35t|%3$-20.20s %|57t|%4%");
1827 // 1 2 3 4
1828 ret << (fmt % "#" % "Address" % "Protocol" % "Queries") << endl;
1829
1830 size_t counter = 0;
1831 for (const auto& front : g_frontends) {
1832 ret << (fmt % counter % front->local.toStringWithPort() % front->getType() % front->queries) << endl;
1833 counter++;
1834 }
1835 g_outputBuffer = ret.str();
1836 }
1837 catch (std::exception& e) {
1838 g_outputBuffer = e.what();
1839 throw;
1840 }
1841 });
1842
1843 luaCtx.writeFunction("getBind", [](uint64_t num) {
1844 setLuaNoSideEffect();
1845 ClientState* ret = nullptr;
1846 if (num < g_frontends.size()) {
1847 ret = g_frontends[num].get();
1848 }
1849 return ret;
1850 });
1851
1852 luaCtx.writeFunction("getBindCount", []() {
1853 setLuaNoSideEffect();
1854 return g_frontends.size();
1855 });
1856
1857 luaCtx.writeFunction("help", [](boost::optional<std::string> command) {
1858 setLuaNoSideEffect();
1859 g_outputBuffer = "";
1860 #ifndef DISABLE_COMPLETION
1861 for (const auto& keyword : g_consoleKeywords) {
1862 if (!command) {
1863 g_outputBuffer += keyword.toString() + "\n";
1864 }
1865 else if (keyword.name == command) {
1866 g_outputBuffer = keyword.toString() + "\n";
1867 return;
1868 }
1869 }
1870 #endif /* DISABLE_COMPLETION */
1871 if (command) {
1872 g_outputBuffer = "Nothing found for " + *command + "\n";
1873 }
1874 });
1875
1876 luaCtx.writeFunction("showVersion", []() {
1877 setLuaNoSideEffect();
1878 g_outputBuffer = "dnsdist " + std::string(VERSION) + "\n";
1879 });
1880
1881 #ifdef HAVE_EBPF
1882 luaCtx.writeFunction("setDefaultBPFFilter", [](std::shared_ptr<BPFFilter> bpf) {
1883 if (!checkConfigurationTime("setDefaultBPFFilter")) {
1884 return;
1885 }
1886 g_defaultBPFFilter = std::move(bpf);
1887 });
1888
1889 luaCtx.writeFunction("registerDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
1890 if (dbpf) {
1891 g_dynBPFFilters.push_back(std::move(dbpf));
1892 }
1893 });
1894
1895 luaCtx.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
1896 if (dbpf) {
1897 for (auto it = g_dynBPFFilters.begin(); it != g_dynBPFFilters.end(); it++) {
1898 if (*it == dbpf) {
1899 g_dynBPFFilters.erase(it);
1900 break;
1901 }
1902 }
1903 }
1904 });
1905
1906 #ifndef DISABLE_DYNBLOCKS
1907 #ifndef DISABLE_DEPRECATED_DYNBLOCK
1908 luaCtx.writeFunction("addBPFFilterDynBlocks", [](const std::unordered_map<ComboAddress, unsigned int, ComboAddress::addressOnlyHash, ComboAddress::addressOnlyEqual>& m, std::shared_ptr<DynBPFFilter> dynbpf, boost::optional<int> seconds, boost::optional<std::string> msg) {
1909 if (!dynbpf) {
1910 return;
1911 }
1912 setLuaSideEffect();
1913 struct timespec until, now;
1914 clock_gettime(CLOCK_MONOTONIC, &now);
1915 until = now;
1916 int actualSeconds = seconds ? *seconds : 10;
1917 until.tv_sec += actualSeconds;
1918 for (const auto& capair : m) {
1919 if (dynbpf->block(capair.first, until)) {
1920 warnlog("Inserting eBPF dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg ? *msg : "");
1921 }
1922 }
1923 });
1924 #endif /* DISABLE_DEPRECATED_DYNBLOCK */
1925 #endif /* DISABLE_DYNBLOCKS */
1926
1927 #endif /* HAVE_EBPF */
1928
1929 luaCtx.writeFunction<LuaAssociativeTable<uint64_t>()>("getStatisticsCounters", []() {
1930 setLuaNoSideEffect();
1931 std::unordered_map<string, uint64_t> res;
1932 {
1933 auto entries = dnsdist::metrics::g_stats.entries.read_lock();
1934 res.reserve(entries->size());
1935 for (const auto& entry : *entries) {
1936 if (const auto& val = std::get_if<pdns::stat_t*>(&entry.d_value)) {
1937 res[entry.d_name] = (*val)->load();
1938 }
1939 }
1940 }
1941 return res;
1942 });
1943
1944 luaCtx.writeFunction("includeDirectory", [&luaCtx](const std::string& dirname) {
1945 if (!checkConfigurationTime("includeDirectory")) {
1946 return;
1947 }
1948 if (g_included) {
1949 errlog("includeDirectory() cannot be used recursively!");
1950 g_outputBuffer = "includeDirectory() cannot be used recursively!\n";
1951 return;
1952 }
1953
1954 struct stat st;
1955 if (stat(dirname.c_str(), &st)) {
1956 errlog("The included directory %s does not exist!", dirname.c_str());
1957 g_outputBuffer = "The included directory " + dirname + " does not exist!";
1958 return;
1959 }
1960
1961 if (!S_ISDIR(st.st_mode)) {
1962 errlog("The included directory %s is not a directory!", dirname.c_str());
1963 g_outputBuffer = "The included directory " + dirname + " is not a directory!";
1964 return;
1965 }
1966
1967 std::vector<std::string> files;
1968 auto directoryError = pdns::visit_directory(dirname, [&dirname, &files]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) {
1969 if (boost::starts_with(name, ".")) {
1970 return true;
1971 }
1972 if (boost::ends_with(name, ".conf")) {
1973 std::ostringstream namebuf;
1974 namebuf << dirname << "/" << name;
1975 struct stat fileStat
1976 {
1977 };
1978 if (stat(namebuf.str().c_str(), &fileStat) == 0 && S_ISREG(fileStat.st_mode)) {
1979 files.push_back(namebuf.str());
1980 }
1981 }
1982 return true;
1983 });
1984
1985 if (directoryError) {
1986 errlog("Error opening included directory: %s!", *directoryError);
1987 g_outputBuffer = "Error opening included directory: " + *directoryError + "!";
1988 return;
1989 }
1990
1991 std::sort(files.begin(), files.end());
1992
1993 g_included = true;
1994
1995 for (const auto& file : files) {
1996 std::ifstream ifs(file);
1997 if (!ifs) {
1998 warnlog("Unable to read configuration from '%s'", file);
1999 }
2000 else {
2001 vinfolog("Read configuration from '%s'", file);
2002 }
2003
2004 try {
2005 luaCtx.executeCode(ifs);
2006 }
2007 catch (...) {
2008 g_included = false;
2009 throw;
2010 }
2011
2012 luaCtx.executeCode(ifs);
2013 }
2014
2015 g_included = false;
2016 });
2017
2018 luaCtx.writeFunction("setAPIWritable", [](bool writable, boost::optional<std::string> apiConfigDir) {
2019 setLuaSideEffect();
2020 g_apiReadWrite = writable;
2021 if (apiConfigDir) {
2022 if (!(*apiConfigDir).empty()) {
2023 g_apiConfigDirectory = *apiConfigDir;
2024 }
2025 else {
2026 errlog("The API configuration directory value cannot be empty!");
2027 g_outputBuffer = "The API configuration directory value cannot be empty!";
2028 }
2029 }
2030 });
2031
2032 luaCtx.writeFunction("setServFailWhenNoServer", [](bool servfail) {
2033 setLuaSideEffect();
2034 g_servFailOnNoPolicy = servfail;
2035 });
2036
2037 luaCtx.writeFunction("setRoundRobinFailOnNoServer", [](bool fail) {
2038 setLuaSideEffect();
2039 g_roundrobinFailOnNoServer = fail;
2040 });
2041
2042 luaCtx.writeFunction("setConsistentHashingBalancingFactor", [](double factor) {
2043 setLuaSideEffect();
2044 if (factor >= 1.0) {
2045 g_consistentHashBalancingFactor = factor;
2046 }
2047 else {
2048 errlog("Invalid value passed to setConsistentHashingBalancingFactor()!");
2049 g_outputBuffer = "Invalid value passed to setConsistentHashingBalancingFactor()!\n";
2050 return;
2051 }
2052 });
2053
2054 luaCtx.writeFunction("setWeightedBalancingFactor", [](double factor) {
2055 setLuaSideEffect();
2056 if (factor >= 1.0) {
2057 g_weightedBalancingFactor = factor;
2058 }
2059 else {
2060 errlog("Invalid value passed to setWeightedBalancingFactor()!");
2061 g_outputBuffer = "Invalid value passed to setWeightedBalancingFactor()!\n";
2062 return;
2063 }
2064 });
2065
2066 luaCtx.writeFunction("setRingBuffersSize", [client](uint64_t capacity, boost::optional<uint64_t> numberOfShards) {
2067 setLuaSideEffect();
2068 if (!checkConfigurationTime("setRingBuffersSize")) {
2069 return;
2070 }
2071 if (!client) {
2072 g_rings.setCapacity(capacity, numberOfShards ? *numberOfShards : 10);
2073 }
2074 else {
2075 g_rings.setCapacity(0, 1);
2076 }
2077 });
2078
2079 luaCtx.writeFunction("setRingBuffersLockRetries", [](uint64_t retries) {
2080 setLuaSideEffect();
2081 g_rings.setNumberOfLockRetries(retries);
2082 });
2083
2084 luaCtx.writeFunction("setRingBuffersOptions", [](const LuaAssociativeTable<boost::variant<bool, uint64_t>>& options) {
2085 setLuaSideEffect();
2086 if (!checkConfigurationTime("setRingBuffersOptions")) {
2087 return;
2088 }
2089 if (options.count("lockRetries") > 0) {
2090 auto retries = boost::get<uint64_t>(options.at("lockRetries"));
2091 g_rings.setNumberOfLockRetries(retries);
2092 }
2093 if (options.count("recordQueries") > 0) {
2094 auto record = boost::get<bool>(options.at("recordQueries"));
2095 g_rings.setRecordQueries(record);
2096 }
2097 if (options.count("recordResponses") > 0) {
2098 auto record = boost::get<bool>(options.at("recordResponses"));
2099 g_rings.setRecordResponses(record);
2100 }
2101 });
2102
2103 luaCtx.writeFunction("setWHashedPertubation", [](uint64_t perturb) {
2104 setLuaSideEffect();
2105 checkParameterBound("setWHashedPertubation", perturb, std::numeric_limits<uint32_t>::max());
2106 g_hashperturb = perturb;
2107 });
2108
2109 luaCtx.writeFunction("setTCPInternalPipeBufferSize", [](uint64_t size) { g_tcpInternalPipeBufferSize = size; });
2110 luaCtx.writeFunction("setTCPFastOpenKey", [](const std::string& keyString) {
2111 setLuaSideEffect();
2112 uint32_t key[4] = {};
2113 auto ret = sscanf(keyString.c_str(), "%" SCNx32 "-%" SCNx32 "-%" SCNx32 "-%" SCNx32, &key[0], &key[1], &key[2], &key[3]);
2114 if (ret != 4) {
2115 g_outputBuffer = "Invalid value passed to setTCPFastOpenKey()!\n";
2116 return;
2117 }
2118 extern vector<uint32_t> g_TCPFastOpenKey;
2119 for (const auto i : key) {
2120 g_TCPFastOpenKey.push_back(i);
2121 }
2122 });
2123
2124 #ifdef HAVE_NET_SNMP
2125 luaCtx.writeFunction("snmpAgent", [client, configCheck](bool enableTraps, boost::optional<std::string> daemonSocket) {
2126 if (client || configCheck) {
2127 return;
2128 }
2129 if (!checkConfigurationTime("snmpAgent")) {
2130 return;
2131 }
2132 if (g_snmpEnabled) {
2133 errlog("snmpAgent() cannot be used twice!");
2134 g_outputBuffer = "snmpAgent() cannot be used twice!\n";
2135 return;
2136 }
2137
2138 g_snmpEnabled = true;
2139 g_snmpTrapsEnabled = enableTraps;
2140 g_snmpAgent = new DNSDistSNMPAgent("dnsdist", daemonSocket ? *daemonSocket : std::string());
2141 });
2142
2143 luaCtx.writeFunction("sendCustomTrap", [](const std::string& str) {
2144 if (g_snmpAgent && g_snmpTrapsEnabled) {
2145 g_snmpAgent->sendCustomTrap(str);
2146 }
2147 });
2148 #endif /* HAVE_NET_SNMP */
2149
2150 #ifndef DISABLE_POLICIES_BINDINGS
2151 luaCtx.writeFunction("setServerPolicy", [](const ServerPolicy& policy) {
2152 setLuaSideEffect();
2153 g_policy.setState(policy);
2154 });
2155
2156 luaCtx.writeFunction("setServerPolicyLua", [](const string& name, ServerPolicy::policyfunc_t policy) {
2157 setLuaSideEffect();
2158 g_policy.setState(ServerPolicy{name, policy, true});
2159 });
2160
2161 luaCtx.writeFunction("setServerPolicyLuaFFI", [](const string& name, ServerPolicy::ffipolicyfunc_t policy) {
2162 setLuaSideEffect();
2163 auto pol = ServerPolicy(name, policy);
2164 g_policy.setState(std::move(pol));
2165 });
2166
2167 luaCtx.writeFunction("setServerPolicyLuaFFIPerThread", [](const string& name, const std::string& policyCode) {
2168 setLuaSideEffect();
2169 auto pol = ServerPolicy(name, policyCode);
2170 g_policy.setState(std::move(pol));
2171 });
2172
2173 luaCtx.writeFunction("showServerPolicy", []() {
2174 setLuaSideEffect();
2175 g_outputBuffer = g_policy.getLocal()->getName() + "\n";
2176 });
2177
2178 luaCtx.writeFunction("setPoolServerPolicy", [](ServerPolicy policy, const string& pool) {
2179 setLuaSideEffect();
2180 auto localPools = g_pools.getCopy();
2181 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(std::move(policy)));
2182 g_pools.setState(localPools);
2183 });
2184
2185 luaCtx.writeFunction("setPoolServerPolicyLua", [](const string& name, ServerPolicy::policyfunc_t policy, const string& pool) {
2186 setLuaSideEffect();
2187 auto localPools = g_pools.getCopy();
2188 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, std::move(policy), true}));
2189 g_pools.setState(localPools);
2190 });
2191
2192 luaCtx.writeFunction("setPoolServerPolicyLuaFFI", [](const string& name, ServerPolicy::ffipolicyfunc_t policy, const string& pool) {
2193 setLuaSideEffect();
2194 auto localPools = g_pools.getCopy();
2195 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, std::move(policy)}));
2196 g_pools.setState(localPools);
2197 });
2198
2199 luaCtx.writeFunction("setPoolServerPolicyLuaFFIPerThread", [](const string& name, const std::string& policyCode, const std::string& pool) {
2200 setLuaSideEffect();
2201 auto localPools = g_pools.getCopy();
2202 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, policyCode}));
2203 g_pools.setState(localPools);
2204 });
2205
2206 luaCtx.writeFunction("showPoolServerPolicy", [](const std::string& pool) {
2207 setLuaSideEffect();
2208 auto localPools = g_pools.getCopy();
2209 auto poolObj = getPool(localPools, pool);
2210 if (poolObj->policy == nullptr) {
2211 g_outputBuffer = g_policy.getLocal()->getName() + "\n";
2212 }
2213 else {
2214 g_outputBuffer = poolObj->policy->getName() + "\n";
2215 }
2216 });
2217 #endif /* DISABLE_POLICIES_BINDINGS */
2218
2219 luaCtx.writeFunction("setTCPDownstreamCleanupInterval", [](uint64_t interval) {
2220 setLuaSideEffect();
2221 checkParameterBound("setTCPDownstreamCleanupInterval", interval);
2222 setTCPDownstreamCleanupInterval(interval);
2223 });
2224
2225 #if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
2226 luaCtx.writeFunction("setDoHDownstreamCleanupInterval", [](uint64_t interval) {
2227 setLuaSideEffect();
2228 checkParameterBound("setDoHDownstreamCleanupInterval", interval);
2229 setDoHDownstreamCleanupInterval(interval);
2230 });
2231 #endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
2232
2233 luaCtx.writeFunction("setTCPDownstreamMaxIdleTime", [](uint64_t max) {
2234 setLuaSideEffect();
2235 checkParameterBound("setTCPDownstreamMaxIdleTime", max);
2236 setTCPDownstreamMaxIdleTime(max);
2237 });
2238
2239 #if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
2240 luaCtx.writeFunction("setDoHDownstreamMaxIdleTime", [](uint64_t max) {
2241 setLuaSideEffect();
2242 checkParameterBound("setDoHDownstreamMaxIdleTime", max);
2243 setDoHDownstreamMaxIdleTime(max);
2244 });
2245 #endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
2246
2247 luaCtx.writeFunction("setConsoleConnectionsLogging", [](bool enabled) {
2248 g_logConsoleConnections = enabled;
2249 });
2250
2251 luaCtx.writeFunction("setConsoleOutputMaxMsgSize", [](uint64_t size) {
2252 checkParameterBound("setConsoleOutputMaxMsgSize", size, std::numeric_limits<uint32_t>::max());
2253 g_consoleOutputMsgMaxSize = size;
2254 });
2255
2256 luaCtx.writeFunction("setProxyProtocolACL", [](LuaTypeOrArrayOf<std::string> inp) {
2257 if (!checkConfigurationTime("setProxyProtocolACL")) {
2258 return;
2259 }
2260 setLuaSideEffect();
2261 NetmaskGroup nmg;
2262 if (auto str = boost::get<string>(&inp)) {
2263 nmg.addMask(*str);
2264 }
2265 else {
2266 for (const auto& p : boost::get<LuaArray<std::string>>(inp)) {
2267 nmg.addMask(p.second);
2268 }
2269 }
2270 g_proxyProtocolACL = std::move(nmg);
2271 });
2272
2273 luaCtx.writeFunction("setProxyProtocolApplyACLToProxiedClients", [](bool apply) {
2274 if (!checkConfigurationTime("setProxyProtocolApplyACLToProxiedClients")) {
2275 return;
2276 }
2277 setLuaSideEffect();
2278 g_applyACLToProxiedClients = apply;
2279 });
2280
2281 luaCtx.writeFunction("setProxyProtocolMaximumPayloadSize", [](uint64_t size) {
2282 if (!checkConfigurationTime("setProxyProtocolMaximumPayloadSize")) {
2283 return;
2284 }
2285 setLuaSideEffect();
2286 g_proxyProtocolMaximumSize = std::max(static_cast<uint64_t>(16), size);
2287 });
2288
2289 #ifndef DISABLE_RECVMMSG
2290 luaCtx.writeFunction("setUDPMultipleMessagesVectorSize", [](uint64_t vSize) {
2291 if (!checkConfigurationTime("setUDPMultipleMessagesVectorSize")) {
2292 return;
2293 }
2294 #if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
2295 setLuaSideEffect();
2296 g_udpVectorSize = vSize;
2297 #else
2298 errlog("recvmmsg() support is not available!");
2299 g_outputBuffer = "recvmmsg support is not available!\n";
2300 #endif
2301 });
2302 #endif /* DISABLE_RECVMMSG */
2303
2304 luaCtx.writeFunction("setAddEDNSToSelfGeneratedResponses", [](bool add) {
2305 g_addEDNSToSelfGeneratedResponses = add;
2306 });
2307
2308 luaCtx.writeFunction("setPayloadSizeOnSelfGeneratedAnswers", [](uint64_t payloadSize) {
2309 if (payloadSize < 512) {
2310 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!");
2311 g_outputBuffer = "setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!";
2312 payloadSize = 512;
2313 }
2314 if (payloadSize > s_udpIncomingBufferSize) {
2315 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to %d instead!", s_udpIncomingBufferSize);
2316 g_outputBuffer = "setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to " + std::to_string(s_udpIncomingBufferSize) + " instead";
2317 payloadSize = s_udpIncomingBufferSize;
2318 }
2319 g_PayloadSizeSelfGenAnswers = payloadSize;
2320 });
2321
2322 #ifndef DISABLE_SECPOLL
2323 luaCtx.writeFunction("showSecurityStatus", []() {
2324 setLuaNoSideEffect();
2325 g_outputBuffer = std::to_string(dnsdist::metrics::g_stats.securityStatus) + "\n";
2326 });
2327
2328 luaCtx.writeFunction("setSecurityPollSuffix", [](const std::string& suffix) {
2329 if (!checkConfigurationTime("setSecurityPollSuffix")) {
2330 return;
2331 }
2332 g_secPollSuffix = suffix;
2333 });
2334
2335 luaCtx.writeFunction("setSecurityPollInterval", [](time_t newInterval) {
2336 if (newInterval <= 0) {
2337 warnlog("setSecurityPollInterval() should be > 0, skipping");
2338 g_outputBuffer = "setSecurityPollInterval() should be > 0, skipping";
2339 }
2340
2341 g_secPollInterval = newInterval;
2342 });
2343 #endif /* DISABLE_SECPOLL */
2344
2345 luaCtx.writeFunction("setSyslogFacility", [](boost::variant<int, std::string> facility) {
2346 if (!checkConfigurationTime("setSyslogFacility")) {
2347 return;
2348 }
2349 setLuaSideEffect();
2350 if (facility.type() == typeid(std::string)) {
2351 static std::map<std::string, int> const facilities = {
2352 {"local0", LOG_LOCAL0},
2353 {"log_local0", LOG_LOCAL0},
2354 {"local1", LOG_LOCAL1},
2355 {"log_local1", LOG_LOCAL1},
2356 {"local2", LOG_LOCAL2},
2357 {"log_local2", LOG_LOCAL2},
2358 {"local3", LOG_LOCAL3},
2359 {"log_local3", LOG_LOCAL3},
2360 {"local4", LOG_LOCAL4},
2361 {"log_local4", LOG_LOCAL4},
2362 {"local5", LOG_LOCAL5},
2363 {"log_local5", LOG_LOCAL5},
2364 {"local6", LOG_LOCAL6},
2365 {"log_local6", LOG_LOCAL6},
2366 {"local7", LOG_LOCAL7},
2367 {"log_local7", LOG_LOCAL7},
2368 /* most of these likely make very little sense
2369 for dnsdist, but why not? */
2370 {"kern", LOG_KERN},
2371 {"log_kern", LOG_KERN},
2372 {"user", LOG_USER},
2373 {"log_user", LOG_USER},
2374 {"mail", LOG_MAIL},
2375 {"log_mail", LOG_MAIL},
2376 {"daemon", LOG_DAEMON},
2377 {"log_daemon", LOG_DAEMON},
2378 {"auth", LOG_AUTH},
2379 {"log_auth", LOG_AUTH},
2380 {"syslog", LOG_SYSLOG},
2381 {"log_syslog", LOG_SYSLOG},
2382 {"lpr", LOG_LPR},
2383 {"log_lpr", LOG_LPR},
2384 {"news", LOG_NEWS},
2385 {"log_news", LOG_NEWS},
2386 {"uucp", LOG_UUCP},
2387 {"log_uucp", LOG_UUCP},
2388 {"cron", LOG_CRON},
2389 {"log_cron", LOG_CRON},
2390 {"authpriv", LOG_AUTHPRIV},
2391 {"log_authpriv", LOG_AUTHPRIV},
2392 {"ftp", LOG_FTP},
2393 {"log_ftp", LOG_FTP}};
2394 auto facilityStr = boost::get<std::string>(facility);
2395 toLowerInPlace(facilityStr);
2396 auto it = facilities.find(facilityStr);
2397 if (it == facilities.end()) {
2398 g_outputBuffer = "Unknown facility '" + facilityStr + "' passed to setSyslogFacility()!\n";
2399 return;
2400 }
2401 setSyslogFacility(it->second);
2402 }
2403 else {
2404 setSyslogFacility(boost::get<int>(facility));
2405 }
2406 });
2407
2408 typedef std::unordered_map<std::string, std::string> tlscertificateopts_t;
2409 luaCtx.writeFunction("newTLSCertificate", [client](const std::string& cert, boost::optional<tlscertificateopts_t> opts) {
2410 std::shared_ptr<TLSCertKeyPair> result = nullptr;
2411 if (client) {
2412 return result;
2413 }
2414 #if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
2415 std::optional<std::string> key, password;
2416 if (opts) {
2417 if (opts->count("key")) {
2418 key = boost::get<const string>((*opts)["key"]);
2419 }
2420 if (opts->count("password")) {
2421 password = boost::get<const string>((*opts)["password"]);
2422 }
2423 }
2424 result = std::make_shared<TLSCertKeyPair>(cert, std::move(key), std::move(password));
2425 #endif
2426 return result;
2427 });
2428
2429 luaCtx.writeFunction("addDOHLocal", [client](const std::string& addr, boost::optional<boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>>> certFiles, boost::optional<boost::variant<std::string, LuaArray<std::string>>> keyFiles, boost::optional<LuaTypeOrArrayOf<std::string>> urls, boost::optional<localbind_t> vars) {
2430 if (client) {
2431 return;
2432 }
2433 #ifdef HAVE_DNS_OVER_HTTPS
2434 if (!checkConfigurationTime("addDOHLocal")) {
2435 return;
2436 }
2437 setLuaSideEffect();
2438
2439 auto frontend = std::make_shared<DOHFrontend>();
2440 if (getOptionalValue<std::string>(vars, "library", frontend->d_library) == 0) {
2441 #ifdef HAVE_NGHTTP2
2442 frontend->d_library = "nghttp2";
2443 #else /* HAVE_NGHTTP2 */
2444 frontend->d_library = "h2o";
2445 #endif /* HAVE_NGHTTP2 */
2446 }
2447 if (frontend->d_library == "h2o") {
2448 #ifdef HAVE_LIBH2OEVLOOP
2449 frontend = std::make_shared<H2ODOHFrontend>();
2450 // we _really_ need to set it again, as we just replaced the generic frontend by a new one
2451 frontend->d_library = "h2o";
2452 #else /* HAVE_LIBH2OEVLOOP */
2453 errlog("DOH bind %s is configured to use libh2o but the library is not available", addr);
2454 return;
2455 #endif /* HAVE_LIBH2OEVLOOP */
2456 }
2457 else if (frontend->d_library == "nghttp2") {
2458 #ifndef HAVE_NGHTTP2
2459 errlog("DOH bind %s is configured to use nghttp2 but the library is not available", addr);
2460 return;
2461 #endif /* HAVE_NGHTTP2 */
2462 }
2463 else {
2464 errlog("DOH bind %s is configured to use an unknown library ('%s')", addr, frontend->d_library);
2465 return;
2466 }
2467
2468 bool useTLS = true;
2469 if (certFiles && !certFiles->empty()) {
2470 if (!loadTLSCertificateAndKeys("addDOHLocal", frontend->d_tlsContext.d_tlsConfig.d_certKeyPairs, *certFiles, *keyFiles)) {
2471 return;
2472 }
2473
2474 frontend->d_tlsContext.d_addr = ComboAddress(addr, 443);
2475 }
2476 else {
2477 frontend->d_tlsContext.d_addr = ComboAddress(addr, 80);
2478 infolog("No certificate provided for DoH endpoint %s, running in DNS over HTTP mode instead of DNS over HTTPS", frontend->d_tlsContext.d_addr.toStringWithPort());
2479 useTLS = false;
2480 }
2481
2482 if (urls) {
2483 if (urls->type() == typeid(std::string)) {
2484 frontend->d_urls.insert(boost::get<std::string>(*urls));
2485 }
2486 else if (urls->type() == typeid(LuaArray<std::string>)) {
2487 auto urlsVect = boost::get<LuaArray<std::string>>(*urls);
2488 for (const auto& p : urlsVect) {
2489 frontend->d_urls.insert(p.second);
2490 }
2491 }
2492 }
2493 else {
2494 frontend->d_urls.insert("/dns-query");
2495 }
2496
2497 bool reusePort = false;
2498 int tcpFastOpenQueueSize = 0;
2499 int tcpListenQueueSize = 0;
2500 uint64_t maxInFlightQueriesPerConn = 0;
2501 uint64_t tcpMaxConcurrentConnections = 0;
2502 std::string interface;
2503 std::set<int> cpus;
2504 std::vector<std::pair<ComboAddress, int>> additionalAddresses;
2505 bool enableProxyProtocol = true;
2506
2507 if (vars) {
2508 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol);
2509 getOptionalValue<int>(vars, "idleTimeout", frontend->d_idleTimeout);
2510 getOptionalValue<std::string>(vars, "serverTokens", frontend->d_serverTokens);
2511 getOptionalValue<std::string>(vars, "provider", frontend->d_tlsContext.d_provider);
2512 boost::algorithm::to_lower(frontend->d_tlsContext.d_provider);
2513 getOptionalValue<bool>(vars, "proxyProtocolOutsideTLS", frontend->d_tlsContext.d_proxyProtocolOutsideTLS);
2514
2515 LuaAssociativeTable<std::string> customResponseHeaders;
2516 if (getOptionalValue<decltype(customResponseHeaders)>(vars, "customResponseHeaders", customResponseHeaders) > 0) {
2517 for (auto const& headerMap : customResponseHeaders) {
2518 auto headerResponse = std::pair(boost::to_lower_copy(headerMap.first), headerMap.second);
2519 frontend->d_customResponseHeaders.insert(headerResponse);
2520 }
2521 }
2522
2523 getOptionalValue<bool>(vars, "sendCacheControlHeaders", frontend->d_sendCacheControlHeaders);
2524 getOptionalValue<bool>(vars, "keepIncomingHeaders", frontend->d_keepIncomingHeaders);
2525 getOptionalValue<bool>(vars, "trustForwardedForHeader", frontend->d_trustForwardedForHeader);
2526 getOptionalValue<bool>(vars, "earlyACLDrop", frontend->d_earlyACLDrop);
2527 getOptionalValue<int>(vars, "internalPipeBufferSize", frontend->d_internalPipeBufferSize);
2528 getOptionalValue<bool>(vars, "exactPathMatching", frontend->d_exactPathMatching);
2529
2530 LuaArray<std::string> addresses;
2531 if (getOptionalValue<decltype(addresses)>(vars, "additionalAddresses", addresses) > 0) {
2532 for (const auto& [_, add] : addresses) {
2533 try {
2534 ComboAddress address(add);
2535 additionalAddresses.emplace_back(address, -1);
2536 }
2537 catch (const PDNSException& e) {
2538 errlog("Unable to parse additional address %s for DOH bind: %s", add, e.reason);
2539 return;
2540 }
2541 }
2542 }
2543
2544 parseTLSConfig(frontend->d_tlsContext.d_tlsConfig, "addDOHLocal", vars);
2545
2546 bool ignoreTLSConfigurationErrors = false;
2547 if (getOptionalValue<bool>(vars, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors) > 0 && ignoreTLSConfigurationErrors) {
2548 // we are asked to try to load the certificates so we can return a potential error
2549 // and properly ignore the frontend before actually launching it
2550 try {
2551 std::map<int, std::string> ocspResponses = {};
2552 auto ctx = libssl_init_server_context(frontend->d_tlsContext.d_tlsConfig, ocspResponses);
2553 }
2554 catch (const std::runtime_error& e) {
2555 errlog("Ignoring DoH frontend: '%s'", e.what());
2556 return;
2557 }
2558 }
2559
2560 checkAllParametersConsumed("addDOHLocal", vars);
2561 }
2562
2563 if (useTLS && frontend->d_library == "nghttp2") {
2564 if (!frontend->d_tlsContext.d_provider.empty()) {
2565 vinfolog("Loading TLS provider '%s'", frontend->d_tlsContext.d_provider);
2566 }
2567 else {
2568 #ifdef HAVE_LIBSSL
2569 const std::string provider("openssl");
2570 #else
2571 const std::string provider("gnutls");
2572 #endif
2573 vinfolog("Loading default TLS provider '%s'", provider);
2574 }
2575 }
2576
2577 g_dohlocals.push_back(frontend);
2578 auto clientState = std::make_unique<ClientState>(frontend->d_tlsContext.d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol);
2579 clientState->dohFrontend = std::move(frontend);
2580 clientState->d_additionalAddresses = std::move(additionalAddresses);
2581
2582 if (tcpListenQueueSize > 0) {
2583 clientState->tcpListenQueueSize = tcpListenQueueSize;
2584 }
2585 if (tcpMaxConcurrentConnections > 0) {
2586 clientState->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConnections;
2587 }
2588 g_frontends.push_back(std::move(clientState));
2589 #else /* HAVE_DNS_OVER_HTTPS */
2590 throw std::runtime_error("addDOHLocal() called but DNS over HTTPS support is not present!");
2591 #endif /* HAVE_DNS_OVER_HTTPS */
2592 });
2593
2594 // NOLINTNEXTLINE(performance-unnecessary-value-param): somehow clang-tidy gets confused about the fact vars could be const while it cannot
2595 luaCtx.writeFunction("addDOH3Local", [client](const std::string& addr, const boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>>& certFiles, const boost::variant<std::string, LuaArray<std::string>>& keyFiles, boost::optional<localbind_t> vars) {
2596 if (client) {
2597 return;
2598 }
2599 #ifdef HAVE_DNS_OVER_HTTP3
2600 if (!checkConfigurationTime("addDOH3Local")) {
2601 return;
2602 }
2603 setLuaSideEffect();
2604
2605 auto frontend = std::make_shared<DOH3Frontend>();
2606 if (!loadTLSCertificateAndKeys("addDOH3Local", frontend->d_quicheParams.d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) {
2607 return;
2608 }
2609 frontend->d_local = ComboAddress(addr, 443);
2610
2611 bool reusePort = false;
2612 int tcpFastOpenQueueSize = 0;
2613 int tcpListenQueueSize = 0;
2614 uint64_t maxInFlightQueriesPerConn = 0;
2615 uint64_t tcpMaxConcurrentConnections = 0;
2616 std::string interface;
2617 std::set<int> cpus;
2618 std::vector<std::pair<ComboAddress, int>> additionalAddresses;
2619 bool enableProxyProtocol = true;
2620
2621 if (vars) {
2622 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol);
2623 if (maxInFlightQueriesPerConn > 0) {
2624 frontend->d_quicheParams.d_maxInFlight = maxInFlightQueriesPerConn;
2625 }
2626 getOptionalValue<int>(vars, "internalPipeBufferSize", frontend->d_internalPipeBufferSize);
2627 getOptionalValue<int>(vars, "idleTimeout", frontend->d_quicheParams.d_idleTimeout);
2628 getOptionalValue<std::string>(vars, "keyLogFile", frontend->d_quicheParams.d_keyLogFile);
2629 {
2630 std::string valueStr;
2631 if (getOptionalValue<std::string>(vars, "congestionControlAlgo", valueStr) > 0) {
2632 if (dnsdist::doq::s_available_cc_algorithms.count(valueStr) > 0) {
2633 frontend->d_quicheParams.d_ccAlgo = valueStr;
2634 }
2635 else {
2636 warnlog("Ignoring unknown value '%s' for 'congestionControlAlgo' on 'addDOH3Local'", valueStr);
2637 }
2638 }
2639 }
2640 parseTLSConfig(frontend->d_quicheParams.d_tlsConfig, "addDOH3Local", vars);
2641
2642 bool ignoreTLSConfigurationErrors = false;
2643 if (getOptionalValue<bool>(vars, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors) > 0 && ignoreTLSConfigurationErrors) {
2644 // we are asked to try to load the certificates so we can return a potential error
2645 // and properly ignore the frontend before actually launching it
2646 try {
2647 std::map<int, std::string> ocspResponses = {};
2648 auto ctx = libssl_init_server_context(frontend->d_quicheParams.d_tlsConfig, ocspResponses);
2649 }
2650 catch (const std::runtime_error& e) {
2651 errlog("Ignoring DoH3 frontend: '%s'", e.what());
2652 return;
2653 }
2654 }
2655
2656 checkAllParametersConsumed("addDOH3Local", vars);
2657 }
2658 g_doh3locals.push_back(frontend);
2659 auto clientState = std::make_unique<ClientState>(frontend->d_local, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol);
2660 clientState->doh3Frontend = frontend;
2661 clientState->d_additionalAddresses = std::move(additionalAddresses);
2662
2663 g_frontends.push_back(std::move(clientState));
2664 #else
2665 throw std::runtime_error("addDOH3Local() called but DNS over HTTP/3 support is not present!");
2666 #endif
2667 });
2668
2669 // NOLINTNEXTLINE(performance-unnecessary-value-param): somehow clang-tidy gets confused about the fact vars could be const while it cannot
2670 luaCtx.writeFunction("addDOQLocal", [client](const std::string& addr, const boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>>& certFiles, const boost::variant<std::string, LuaArray<std::string>>& keyFiles, boost::optional<localbind_t> vars) {
2671 if (client) {
2672 return;
2673 }
2674 #ifdef HAVE_DNS_OVER_QUIC
2675 if (!checkConfigurationTime("addDOQLocal")) {
2676 return;
2677 }
2678 setLuaSideEffect();
2679
2680 auto frontend = std::make_shared<DOQFrontend>();
2681 if (!loadTLSCertificateAndKeys("addDOQLocal", frontend->d_quicheParams.d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) {
2682 return;
2683 }
2684 frontend->d_local = ComboAddress(addr, 853);
2685
2686 bool reusePort = false;
2687 int tcpFastOpenQueueSize = 0;
2688 int tcpListenQueueSize = 0;
2689 uint64_t maxInFlightQueriesPerConn = 0;
2690 uint64_t tcpMaxConcurrentConnections = 0;
2691 std::string interface;
2692 std::set<int> cpus;
2693 std::vector<std::pair<ComboAddress, int>> additionalAddresses;
2694 bool enableProxyProtocol = true;
2695
2696 if (vars) {
2697 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol);
2698 if (maxInFlightQueriesPerConn > 0) {
2699 frontend->d_quicheParams.d_maxInFlight = maxInFlightQueriesPerConn;
2700 }
2701 getOptionalValue<int>(vars, "internalPipeBufferSize", frontend->d_internalPipeBufferSize);
2702 getOptionalValue<int>(vars, "idleTimeout", frontend->d_quicheParams.d_idleTimeout);
2703 getOptionalValue<std::string>(vars, "keyLogFile", frontend->d_quicheParams.d_keyLogFile);
2704 {
2705 std::string valueStr;
2706 if (getOptionalValue<std::string>(vars, "congestionControlAlgo", valueStr) > 0) {
2707 if (dnsdist::doq::s_available_cc_algorithms.count(valueStr) > 0) {
2708 frontend->d_quicheParams.d_ccAlgo = valueStr;
2709 }
2710 else {
2711 warnlog("Ignoring unknown value '%s' for 'congestionControlAlgo' on 'addDOQLocal'", valueStr);
2712 }
2713 }
2714 }
2715 parseTLSConfig(frontend->d_quicheParams.d_tlsConfig, "addDOQLocal", vars);
2716
2717 bool ignoreTLSConfigurationErrors = false;
2718 if (getOptionalValue<bool>(vars, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors) > 0 && ignoreTLSConfigurationErrors) {
2719 // we are asked to try to load the certificates so we can return a potential error
2720 // and properly ignore the frontend before actually launching it
2721 try {
2722 std::map<int, std::string> ocspResponses = {};
2723 auto ctx = libssl_init_server_context(frontend->d_quicheParams.d_tlsConfig, ocspResponses);
2724 }
2725 catch (const std::runtime_error& e) {
2726 errlog("Ignoring DoQ frontend: '%s'", e.what());
2727 return;
2728 }
2729 }
2730
2731 checkAllParametersConsumed("addDOQLocal", vars);
2732 }
2733 g_doqlocals.push_back(frontend);
2734 auto clientState = std::make_unique<ClientState>(frontend->d_local, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol);
2735 clientState->doqFrontend = std::move(frontend);
2736 clientState->d_additionalAddresses = std::move(additionalAddresses);
2737
2738 g_frontends.push_back(std::move(clientState));
2739 #else
2740 throw std::runtime_error("addDOQLocal() called but DNS over QUIC support is not present!");
2741 #endif
2742 });
2743
2744 luaCtx.writeFunction("showDOQFrontends", []() {
2745 #ifdef HAVE_DNS_OVER_QUIC
2746 setLuaNoSideEffect();
2747 try {
2748 ostringstream ret;
2749 boost::format fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d");
2750 ret << (fmt % "#" % "Address" % "Bad Version" % "Invalid Token" % "Errors" % "Valid") << endl;
2751 size_t counter = 0;
2752 for (const auto& ctx : g_doqlocals) {
2753 ret << (fmt % counter % ctx->d_local.toStringWithPort() % ctx->d_doqUnsupportedVersionErrors % ctx->d_doqInvalidTokensReceived % ctx->d_errorResponses % ctx->d_validResponses) << endl;
2754 counter++;
2755 }
2756 g_outputBuffer = ret.str();
2757 }
2758 catch (const std::exception& e) {
2759 g_outputBuffer = e.what();
2760 throw;
2761 }
2762 #else
2763 g_outputBuffer = "DNS over QUIC support is not present!\n";
2764 #endif
2765 });
2766
2767 luaCtx.writeFunction("showDOHFrontends", []() {
2768 #ifdef HAVE_DNS_OVER_HTTPS
2769 setLuaNoSideEffect();
2770 try {
2771 ostringstream ret;
2772 boost::format fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d");
2773 ret << (fmt % "#" % "Address" % "HTTP" % "HTTP/1" % "HTTP/2" % "GET" % "POST" % "Bad" % "Errors" % "Redirects" % "Valid" % "# ticket keys" % "Rotation delay" % "Next rotation") << endl;
2774 size_t counter = 0;
2775 for (const auto& ctx : g_dohlocals) {
2776 ret << (fmt % counter % ctx->d_tlsContext.d_addr.toStringWithPort() % ctx->d_httpconnects % ctx->d_http1Stats.d_nbQueries % ctx->d_http2Stats.d_nbQueries % ctx->d_getqueries % ctx->d_postqueries % ctx->d_badrequests % ctx->d_errorresponses % ctx->d_redirectresponses % ctx->d_validresponses % ctx->getTicketsKeysCount() % ctx->getTicketsKeyRotationDelay() % ctx->getNextTicketsKeyRotation()) << endl;
2777 counter++;
2778 }
2779 g_outputBuffer = ret.str();
2780 }
2781 catch (const std::exception& e) {
2782 g_outputBuffer = e.what();
2783 throw;
2784 }
2785 #else
2786 g_outputBuffer = "DNS over HTTPS support is not present!\n";
2787 #endif
2788 });
2789
2790 luaCtx.writeFunction("showDOH3Frontends", []() {
2791 #ifdef HAVE_DNS_OVER_HTTP3
2792 setLuaNoSideEffect();
2793 try {
2794 ostringstream ret;
2795 boost::format fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d");
2796 ret << (fmt % "#" % "Address" % "Bad Version" % "Invalid Token" % "Errors" % "Valid") << endl;
2797 size_t counter = 0;
2798 for (const auto& ctx : g_doh3locals) {
2799 ret << (fmt % counter % ctx->d_local.toStringWithPort() % ctx->d_doh3UnsupportedVersionErrors % ctx->d_doh3InvalidTokensReceived % ctx->d_errorResponses % ctx->d_validResponses) << endl;
2800 counter++;
2801 }
2802 g_outputBuffer = ret.str();
2803 }
2804 catch (const std::exception& e) {
2805 g_outputBuffer = e.what();
2806 throw;
2807 }
2808 #else
2809 g_outputBuffer = "DNS over HTTP3 support is not present!\n";
2810 #endif
2811 });
2812
2813 luaCtx.writeFunction("showDOHResponseCodes", []() {
2814 #ifdef HAVE_DNS_OVER_HTTPS
2815 setLuaNoSideEffect();
2816 try {
2817 ostringstream ret;
2818 boost::format fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d %-15d %-15d");
2819 g_outputBuffer = "\n- HTTP/1:\n\n";
2820 ret << (fmt % "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others") << endl;
2821 size_t counter = 0;
2822 for (const auto& ctx : g_dohlocals) {
2823 ret << (fmt % counter % ctx->d_tlsContext.d_addr.toStringWithPort() % ctx->d_http1Stats.d_nb200Responses % ctx->d_http1Stats.d_nb400Responses % ctx->d_http1Stats.d_nb403Responses % ctx->d_http1Stats.d_nb500Responses % ctx->d_http1Stats.d_nb502Responses % ctx->d_http1Stats.d_nbOtherResponses) << endl;
2824 counter++;
2825 }
2826 g_outputBuffer += ret.str();
2827 ret.str("");
2828
2829 g_outputBuffer += "\n- HTTP/2:\n\n";
2830 ret << (fmt % "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others") << endl;
2831 counter = 0;
2832 for (const auto& ctx : g_dohlocals) {
2833 ret << (fmt % counter % ctx->d_tlsContext.d_addr.toStringWithPort() % ctx->d_http2Stats.d_nb200Responses % ctx->d_http2Stats.d_nb400Responses % ctx->d_http2Stats.d_nb403Responses % ctx->d_http2Stats.d_nb500Responses % ctx->d_http2Stats.d_nb502Responses % ctx->d_http2Stats.d_nbOtherResponses) << endl;
2834 counter++;
2835 }
2836 g_outputBuffer += ret.str();
2837 }
2838 catch (const std::exception& e) {
2839 g_outputBuffer = e.what();
2840 throw;
2841 }
2842 #else
2843 g_outputBuffer = "DNS over HTTPS support is not present!\n";
2844 #endif
2845 });
2846
2847 luaCtx.writeFunction("getDOHFrontend", [client](uint64_t index) {
2848 std::shared_ptr<DOHFrontend> result = nullptr;
2849 if (client) {
2850 return result;
2851 }
2852 #ifdef HAVE_DNS_OVER_HTTPS
2853 setLuaNoSideEffect();
2854 try {
2855 if (index < g_dohlocals.size()) {
2856 result = g_dohlocals.at(index);
2857 }
2858 else {
2859 errlog("Error: trying to get DOH frontend with index %d but we only have %d frontend(s)\n", index, g_dohlocals.size());
2860 g_outputBuffer = "Error: trying to get DOH frontend with index " + std::to_string(index) + " but we only have " + std::to_string(g_dohlocals.size()) + " frontend(s)\n";
2861 }
2862 }
2863 catch (const std::exception& e) {
2864 g_outputBuffer = "Error while trying to get DOH frontend with index " + std::to_string(index) + ": " + string(e.what()) + "\n";
2865 errlog("Error while trying to get DOH frontend with index %d: %s\n", index, string(e.what()));
2866 }
2867 #else
2868 g_outputBuffer="DNS over HTTPS support is not present!\n";
2869 #endif
2870 return result;
2871 });
2872
2873 luaCtx.writeFunction("getDOHFrontendCount", []() {
2874 setLuaNoSideEffect();
2875 return g_dohlocals.size();
2876 });
2877
2878 luaCtx.registerFunction<void (std::shared_ptr<DOHFrontend>::*)()>("reloadCertificates", [](std::shared_ptr<DOHFrontend> frontend) {
2879 if (frontend != nullptr) {
2880 frontend->reloadCertificates();
2881 }
2882 });
2883
2884 luaCtx.registerFunction<void (std::shared_ptr<DOHFrontend>::*)(boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>> certFiles, boost::variant<std::string, LuaArray<std::string>> keyFiles)>("loadNewCertificatesAndKeys", [](std::shared_ptr<DOHFrontend> frontend, boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>> certFiles, boost::variant<std::string, LuaArray<std::string>> keyFiles) {
2885 #ifdef HAVE_DNS_OVER_HTTPS
2886 if (frontend != nullptr) {
2887 if (loadTLSCertificateAndKeys("DOHFrontend::loadNewCertificatesAndKeys", frontend->d_tlsContext.d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) {
2888 frontend->reloadCertificates();
2889 }
2890 }
2891 #endif
2892 });
2893
2894 luaCtx.registerFunction<void (std::shared_ptr<DOHFrontend>::*)()>("rotateTicketsKey", [](std::shared_ptr<DOHFrontend> frontend) {
2895 if (frontend != nullptr) {
2896 frontend->rotateTicketsKey(time(nullptr));
2897 }
2898 });
2899
2900 luaCtx.registerFunction<void (std::shared_ptr<DOHFrontend>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<DOHFrontend> frontend, const std::string& file) {
2901 if (frontend != nullptr) {
2902 frontend->loadTicketsKeys(file);
2903 }
2904 });
2905
2906 luaCtx.registerFunction<void (std::shared_ptr<DOHFrontend>::*)(const LuaArray<std::shared_ptr<DOHResponseMapEntry>>&)>("setResponsesMap", [](std::shared_ptr<DOHFrontend> frontend, const LuaArray<std::shared_ptr<DOHResponseMapEntry>>& map) {
2907 if (frontend != nullptr) {
2908 auto newMap = std::make_shared<std::vector<std::shared_ptr<DOHResponseMapEntry>>>();
2909 newMap->reserve(map.size());
2910
2911 for (const auto& entry : map) {
2912 newMap->push_back(entry.second);
2913 }
2914
2915 frontend->d_responsesMap = std::move(newMap);
2916 }
2917 });
2918
2919 luaCtx.writeFunction("addTLSLocal", [client](const std::string& addr, boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>> certFiles, LuaTypeOrArrayOf<std::string> keyFiles, boost::optional<localbind_t> vars) {
2920 if (client) {
2921 return;
2922 }
2923 #ifdef HAVE_DNS_OVER_TLS
2924 if (!checkConfigurationTime("addTLSLocal")) {
2925 return;
2926 }
2927 setLuaSideEffect();
2928
2929 auto frontend = std::make_shared<TLSFrontend>(TLSFrontend::ALPN::DoT);
2930 if (!loadTLSCertificateAndKeys("addTLSLocal", frontend->d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) {
2931 return;
2932 }
2933
2934 bool reusePort = false;
2935 int tcpFastOpenQueueSize = 0;
2936 int tcpListenQueueSize = 0;
2937 uint64_t maxInFlightQueriesPerConn = 0;
2938 uint64_t tcpMaxConcurrentConns = 0;
2939 std::string interface;
2940 std::set<int> cpus;
2941 std::vector<std::pair<ComboAddress, int>> additionalAddresses;
2942 bool enableProxyProtocol = true;
2943
2944 if (vars) {
2945 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConns, enableProxyProtocol);
2946
2947 getOptionalValue<std::string>(vars, "provider", frontend->d_provider);
2948 boost::algorithm::to_lower(frontend->d_provider);
2949 getOptionalValue<bool>(vars, "proxyProtocolOutsideTLS", frontend->d_proxyProtocolOutsideTLS);
2950
2951 LuaArray<std::string> addresses;
2952 if (getOptionalValue<decltype(addresses)>(vars, "additionalAddresses", addresses) > 0) {
2953 for (const auto& [_, add] : addresses) {
2954 try {
2955 ComboAddress address(add);
2956 additionalAddresses.emplace_back(address, -1);
2957 }
2958 catch (const PDNSException& e) {
2959 errlog("Unable to parse additional address %s for DoT bind: %s", add, e.reason);
2960 return;
2961 }
2962 }
2963 }
2964
2965 parseTLSConfig(frontend->d_tlsConfig, "addTLSLocal", vars);
2966
2967 bool ignoreTLSConfigurationErrors = false;
2968 if (getOptionalValue<bool>(vars, "ignoreTLSConfigurationErrors", ignoreTLSConfigurationErrors) > 0 && ignoreTLSConfigurationErrors) {
2969 // we are asked to try to load the certificates so we can return a potential error
2970 // and properly ignore the frontend before actually launching it
2971 try {
2972 std::map<int, std::string> ocspResponses = {};
2973 auto ctx = libssl_init_server_context(frontend->d_tlsConfig, ocspResponses);
2974 }
2975 catch (const std::runtime_error& e) {
2976 errlog("Ignoring TLS frontend: '%s'", e.what());
2977 return;
2978 }
2979 }
2980
2981 checkAllParametersConsumed("addTLSLocal", vars);
2982 }
2983
2984 try {
2985 frontend->d_addr = ComboAddress(addr, 853);
2986 if (!frontend->d_provider.empty()) {
2987 vinfolog("Loading TLS provider '%s'", frontend->d_provider);
2988 }
2989 else {
2990 #ifdef HAVE_LIBSSL
2991 const std::string provider("openssl");
2992 #else
2993 const std::string provider("gnutls");
2994 #endif
2995 vinfolog("Loading default TLS provider '%s'", provider);
2996 }
2997 // only works pre-startup, so no sync necessary
2998 auto clientState = std::make_unique<ClientState>(frontend->d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol);
2999 clientState->tlsFrontend = frontend;
3000 clientState->d_additionalAddresses = std::move(additionalAddresses);
3001 if (tcpListenQueueSize > 0) {
3002 clientState->tcpListenQueueSize = tcpListenQueueSize;
3003 }
3004 if (maxInFlightQueriesPerConn > 0) {
3005 clientState->d_maxInFlightQueriesPerConn = maxInFlightQueriesPerConn;
3006 }
3007 if (tcpMaxConcurrentConns > 0) {
3008 clientState->d_tcpConcurrentConnectionsLimit = tcpMaxConcurrentConns;
3009 }
3010
3011 g_tlslocals.push_back(clientState->tlsFrontend);
3012 g_frontends.push_back(std::move(clientState));
3013 }
3014 catch (const std::exception& e) {
3015 g_outputBuffer = "Error: " + string(e.what()) + "\n";
3016 }
3017 #else
3018 throw std::runtime_error("addTLSLocal() called but DNS over TLS support is not present!");
3019 #endif
3020 });
3021
3022 luaCtx.writeFunction("showTLSContexts", []() {
3023 #ifdef HAVE_DNS_OVER_TLS
3024 setLuaNoSideEffect();
3025 try {
3026 ostringstream ret;
3027 boost::format fmt("%1$-3d %2$-20.20s %|25t|%3$-14d %|40t|%4$-14d %|54t|%5$-21.21s");
3028 // 1 2 3 4 5
3029 ret << (fmt % "#" % "Address" % "# ticket keys" % "Rotation delay" % "Next rotation") << endl;
3030 size_t counter = 0;
3031 for (const auto& ctx : g_tlslocals) {
3032 ret << (fmt % counter % ctx->d_addr.toStringWithPort() % ctx->getTicketsKeysCount() % ctx->getTicketsKeyRotationDelay() % ctx->getNextTicketsKeyRotation()) << endl;
3033 counter++;
3034 }
3035 g_outputBuffer = ret.str();
3036 }
3037 catch (const std::exception& e) {
3038 g_outputBuffer = e.what();
3039 throw;
3040 }
3041 #else
3042 g_outputBuffer = "DNS over TLS support is not present!\n";
3043 #endif
3044 });
3045
3046 luaCtx.writeFunction("getTLSContext", [](uint64_t index) {
3047 std::shared_ptr<TLSCtx> result = nullptr;
3048 #ifdef HAVE_DNS_OVER_TLS
3049 setLuaNoSideEffect();
3050 try {
3051 if (index < g_tlslocals.size()) {
3052 result = g_tlslocals.at(index)->getContext();
3053 }
3054 else {
3055 errlog("Error: trying to get TLS context with index %d but we only have %d context(s)\n", index, g_tlslocals.size());
3056 g_outputBuffer = "Error: trying to get TLS context with index " + std::to_string(index) + " but we only have " + std::to_string(g_tlslocals.size()) + " context(s)\n";
3057 }
3058 }
3059 catch (const std::exception& e) {
3060 g_outputBuffer = "Error while trying to get TLS context with index " + std::to_string(index) + ": " + string(e.what()) + "\n";
3061 errlog("Error while trying to get TLS context with index %d: %s\n", index, string(e.what()));
3062 }
3063 #else
3064 g_outputBuffer="DNS over TLS support is not present!\n";
3065 #endif
3066 return result;
3067 });
3068
3069 luaCtx.writeFunction("getTLSFrontend", [](uint64_t index) {
3070 std::shared_ptr<TLSFrontend> result = nullptr;
3071 #ifdef HAVE_DNS_OVER_TLS
3072 setLuaNoSideEffect();
3073 try {
3074 if (index < g_tlslocals.size()) {
3075 result = g_tlslocals.at(index);
3076 }
3077 else {
3078 errlog("Error: trying to get TLS frontend with index %d but we only have %d frontends\n", index, g_tlslocals.size());
3079 g_outputBuffer = "Error: trying to get TLS frontend with index " + std::to_string(index) + " but we only have " + std::to_string(g_tlslocals.size()) + " frontend(s)\n";
3080 }
3081 }
3082 catch (const std::exception& e) {
3083 g_outputBuffer = "Error while trying to get TLS frontend with index " + std::to_string(index) + ": " + string(e.what()) + "\n";
3084 errlog("Error while trying to get TLS frontend with index %d: %s\n", index, string(e.what()));
3085 }
3086 #else
3087 g_outputBuffer="DNS over TLS support is not present!\n";
3088 #endif
3089 return result;
3090 });
3091
3092 luaCtx.writeFunction("getTLSFrontendCount", []() {
3093 setLuaNoSideEffect();
3094 return g_tlslocals.size();
3095 });
3096
3097 luaCtx.registerFunction<void (std::shared_ptr<TLSCtx>::*)()>("rotateTicketsKey", [](std::shared_ptr<TLSCtx>& ctx) {
3098 if (ctx != nullptr) {
3099 ctx->rotateTicketsKey(time(nullptr));
3100 }
3101 });
3102
3103 luaCtx.registerFunction<void (std::shared_ptr<TLSCtx>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<TLSCtx>& ctx, const std::string& file) {
3104 if (ctx != nullptr) {
3105 ctx->loadTicketsKeys(file);
3106 }
3107 });
3108
3109 luaCtx.registerFunction<std::string (std::shared_ptr<TLSFrontend>::*)() const>("getAddressAndPort", [](const std::shared_ptr<TLSFrontend>& frontend) {
3110 if (frontend == nullptr) {
3111 return std::string();
3112 }
3113 return frontend->d_addr.toStringWithPort();
3114 });
3115
3116 luaCtx.registerFunction<void (std::shared_ptr<TLSFrontend>::*)()>("rotateTicketsKey", [](std::shared_ptr<TLSFrontend>& frontend) {
3117 if (frontend == nullptr) {
3118 return;
3119 }
3120 auto ctx = frontend->getContext();
3121 if (ctx) {
3122 ctx->rotateTicketsKey(time(nullptr));
3123 }
3124 });
3125
3126 luaCtx.registerFunction<void (std::shared_ptr<TLSFrontend>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<TLSFrontend>& frontend, const std::string& file) {
3127 if (frontend == nullptr) {
3128 return;
3129 }
3130 auto ctx = frontend->getContext();
3131 if (ctx) {
3132 ctx->loadTicketsKeys(file);
3133 }
3134 });
3135
3136 luaCtx.registerFunction<void (std::shared_ptr<TLSFrontend>::*)()>("reloadCertificates", [](std::shared_ptr<TLSFrontend>& frontend) {
3137 if (frontend == nullptr) {
3138 return;
3139 }
3140 frontend->setupTLS();
3141 });
3142
3143 luaCtx.registerFunction<void (std::shared_ptr<TLSFrontend>::*)(boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>> certFiles, LuaTypeOrArrayOf<std::string> keyFiles)>("loadNewCertificatesAndKeys", [](std::shared_ptr<TLSFrontend>& frontend, boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, LuaArray<std::string>, LuaArray<std::shared_ptr<TLSCertKeyPair>>> certFiles, LuaTypeOrArrayOf<std::string> keyFiles) {
3144 #ifdef HAVE_DNS_OVER_TLS
3145 if (loadTLSCertificateAndKeys("TLSFrontend::loadNewCertificatesAndKeys", frontend->d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) {
3146 frontend->setupTLS();
3147 }
3148 #endif
3149 });
3150
3151 luaCtx.writeFunction("reloadAllCertificates", []() {
3152 for (auto& frontend : g_frontends) {
3153 if (!frontend) {
3154 continue;
3155 }
3156 try {
3157 #ifdef HAVE_DNSCRYPT
3158 if (frontend->dnscryptCtx) {
3159 frontend->dnscryptCtx->reloadCertificates();
3160 }
3161 #endif /* HAVE_DNSCRYPT */
3162 #ifdef HAVE_DNS_OVER_TLS
3163 if (frontend->tlsFrontend) {
3164 frontend->tlsFrontend->setupTLS();
3165 }
3166 #endif /* HAVE_DNS_OVER_TLS */
3167 #ifdef HAVE_DNS_OVER_HTTPS
3168 if (frontend->dohFrontend) {
3169 frontend->dohFrontend->reloadCertificates();
3170 }
3171 #endif /* HAVE_DNS_OVER_HTTPS */
3172 }
3173 catch (const std::exception& e) {
3174 errlog("Error reloading certificates for frontend %s: %s", frontend->local.toStringWithPort(), e.what());
3175 }
3176 }
3177 });
3178
3179 luaCtx.writeFunction("setAllowEmptyResponse", [](bool allow) { g_allowEmptyResponse = allow; });
3180 luaCtx.writeFunction("setDropEmptyQueries", [](bool drop) { extern bool g_dropEmptyQueries; g_dropEmptyQueries = drop; });
3181
3182 #if defined(HAVE_LIBSSL) && defined(HAVE_OCSP_BASIC_SIGN) && !defined(DISABLE_OCSP_STAPLING)
3183 luaCtx.writeFunction("generateOCSPResponse", [client](const std::string& certFile, const std::string& caCert, const std::string& caKey, const std::string& outFile, int ndays, int nmin) {
3184 if (client) {
3185 return;
3186 }
3187
3188 libssl_generate_ocsp_response(certFile, caCert, caKey, outFile, ndays, nmin);
3189 });
3190 #endif /* HAVE_LIBSSL && HAVE_OCSP_BASIC_SIGN && !DISABLE_OCSP_STAPLING */
3191
3192 luaCtx.writeFunction("addCapabilitiesToRetain", [](LuaTypeOrArrayOf<std::string> caps) {
3193 if (!checkConfigurationTime("addCapabilitiesToRetain")) {
3194 return;
3195 }
3196 setLuaSideEffect();
3197 if (caps.type() == typeid(std::string)) {
3198 g_capabilitiesToRetain.insert(boost::get<std::string>(caps));
3199 }
3200 else if (caps.type() == typeid(LuaArray<std::string>)) {
3201 for (const auto& cap : boost::get<LuaArray<std::string>>(caps)) {
3202 g_capabilitiesToRetain.insert(cap.second);
3203 }
3204 }
3205 });
3206
3207 luaCtx.writeFunction("setUDPSocketBufferSizes", [client](uint64_t recv, uint64_t snd) {
3208 if (client) {
3209 return;
3210 }
3211 if (!checkConfigurationTime("setUDPSocketBufferSizes")) {
3212 return;
3213 }
3214 checkParameterBound("setUDPSocketBufferSizes", recv, std::numeric_limits<uint32_t>::max());
3215 checkParameterBound("setUDPSocketBufferSizes", snd, std::numeric_limits<uint32_t>::max());
3216 setLuaSideEffect();
3217
3218 g_socketUDPSendBuffer = snd;
3219 g_socketUDPRecvBuffer = recv;
3220 });
3221
3222 luaCtx.writeFunction("setRandomizedOutgoingSockets", [](bool randomized) {
3223 DownstreamState::s_randomizeSockets = randomized;
3224 });
3225
3226 luaCtx.writeFunction("setRandomizedIdsOverUDP", [](bool randomized) {
3227 DownstreamState::s_randomizeIDs = randomized;
3228 });
3229
3230 #if defined(HAVE_LIBSSL) && !defined(HAVE_TLS_PROVIDERS)
3231 luaCtx.writeFunction("loadTLSEngine", [client](const std::string& engineName, boost::optional<std::string> defaultString) {
3232 if (client) {
3233 return;
3234 }
3235
3236 auto [success, error] = libssl_load_engine(engineName, defaultString ? std::optional<std::string>(*defaultString) : std::nullopt);
3237 if (!success) {
3238 g_outputBuffer = "Error while trying to load TLS engine '" + engineName + "': " + error + "\n";
3239 errlog("Error while trying to load TLS engine '%s': %s", engineName, error);
3240 }
3241 });
3242 #endif /* HAVE_LIBSSL && !HAVE_TLS_PROVIDERS */
3243
3244 #if defined(HAVE_LIBSSL) && OPENSSL_VERSION_MAJOR >= 3 && defined(HAVE_TLS_PROVIDERS)
3245 luaCtx.writeFunction("loadTLSProvider", [client](const std::string& providerName) {
3246 if (client) {
3247 return;
3248 }
3249
3250 auto [success, error] = libssl_load_provider(providerName);
3251 if (!success) {
3252 g_outputBuffer = "Error while trying to load TLS provider '" + providerName + "': " + error + "\n";
3253 errlog("Error while trying to load TLS provider '%s': %s", providerName, error);
3254 }
3255 });
3256 #endif /* HAVE_LIBSSL && OPENSSL_VERSION_MAJOR >= 3 && HAVE_TLS_PROVIDERS */
3257
3258 luaCtx.writeFunction("newThread", [client, configCheck](const std::string& code) {
3259 if (client || configCheck) {
3260 return;
3261 }
3262 std::thread newThread(LuaThread, code);
3263
3264 newThread.detach();
3265 });
3266
3267 luaCtx.writeFunction("declareMetric", [](const std::string& name, const std::string& type, const std::string& description, boost::optional<std::string> customName) {
3268 auto result = dnsdist::metrics::declareCustomMetric(name, type, description, customName ? std::optional<std::string>(*customName) : std::nullopt);
3269 if (result) {
3270 g_outputBuffer += *result + "\n";
3271 errlog("Error in declareMetric: %s", *result);
3272 return false;
3273 }
3274 return true;
3275 });
3276 luaCtx.writeFunction("incMetric", [](const std::string& name, boost::optional<uint64_t> step) {
3277 auto result = dnsdist::metrics::incrementCustomCounter(name, step ? *step : 1);
3278 if (const auto* errorStr = std::get_if<dnsdist::metrics::Error>(&result)) {
3279 g_outputBuffer = *errorStr + "'\n";
3280 errlog("Error in incMetric: %s", *errorStr);
3281 return static_cast<uint64_t>(0);
3282 }
3283 return std::get<uint64_t>(result);
3284 });
3285 luaCtx.writeFunction("decMetric", [](const std::string& name, boost::optional<uint64_t> step) {
3286 auto result = dnsdist::metrics::decrementCustomCounter(name, step ? *step : 1);
3287 if (const auto* errorStr = std::get_if<dnsdist::metrics::Error>(&result)) {
3288 g_outputBuffer = *errorStr + "'\n";
3289 errlog("Error in decMetric: %s", *errorStr);
3290 return static_cast<uint64_t>(0);
3291 }
3292 return std::get<uint64_t>(result);
3293 });
3294 luaCtx.writeFunction("setMetric", [](const std::string& name, const double value) -> double {
3295 auto result = dnsdist::metrics::setCustomGauge(name, value);
3296 if (const auto* errorStr = std::get_if<dnsdist::metrics::Error>(&result)) {
3297 g_outputBuffer = *errorStr + "'\n";
3298 errlog("Error in setMetric: %s", *errorStr);
3299 return 0.;
3300 }
3301 return std::get<double>(result);
3302 });
3303 luaCtx.writeFunction("getMetric", [](const std::string& name) {
3304 auto result = dnsdist::metrics::getCustomMetric(name);
3305 if (const auto* errorStr = std::get_if<dnsdist::metrics::Error>(&result)) {
3306 g_outputBuffer = *errorStr + "'\n";
3307 errlog("Error in getMetric: %s", *errorStr);
3308 return 0.;
3309 }
3310 return std::get<double>(result);
3311 });
3312 }
3313
3314 vector<std::function<void(void)>> setupLua(LuaContext& luaCtx, bool client, bool configCheck, const std::string& config)
3315 {
3316 // this needs to exist only during the parsing of the configuration
3317 // and cannot be captured by lambdas
3318 g_launchWork = std::vector<std::function<void(void)>>();
3319
3320 setupLuaActions(luaCtx);
3321 setupLuaConfig(luaCtx, client, configCheck);
3322 setupLuaBindings(luaCtx, client, configCheck);
3323 setupLuaBindingsDNSCrypt(luaCtx, client);
3324 setupLuaBindingsDNSParser(luaCtx);
3325 setupLuaBindingsDNSQuestion(luaCtx);
3326 setupLuaBindingsKVS(luaCtx, client);
3327 setupLuaBindingsNetwork(luaCtx, client);
3328 setupLuaBindingsPacketCache(luaCtx, client);
3329 setupLuaBindingsProtoBuf(luaCtx, client, configCheck);
3330 setupLuaBindingsRings(luaCtx, client);
3331 setupLuaInspection(luaCtx);
3332 setupLuaRules(luaCtx);
3333 setupLuaVars(luaCtx);
3334 setupLuaWeb(luaCtx);
3335
3336 #ifdef LUAJIT_VERSION
3337 luaCtx.executeCode(getLuaFFIWrappers());
3338 #endif
3339
3340 std::ifstream ifs(config);
3341 if (!ifs) {
3342 if (configCheck) {
3343 throw std::runtime_error("Unable to read configuration file from " + config);
3344 }
3345 else {
3346 warnlog("Unable to read configuration from '%s'", config);
3347 }
3348 }
3349 else {
3350 vinfolog("Read configuration from '%s'", config);
3351 }
3352
3353 luaCtx.executeCode(ifs);
3354
3355 auto ret = *g_launchWork;
3356 g_launchWork = boost::none;
3357 return ret;
3358 }