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