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