]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-lua.cc
dnsdist: Fix duplicated HTTP/1 counter in 'showDOHFrontends()'
[thirdparty/pdns.git] / pdns / dnsdist-lua.cc
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
6bb38cd6
RG
22
23#include <dirent.h>
24#include <fstream>
aff9ca62
PD
25
26// for OpenBSD, sys/socket.h needs to come before net/if.h
27#include <sys/socket.h>
6bb38cd6 28#include <net/if.h>
aff9ca62 29
9ef9b494 30#include <sys/types.h>
6bb38cd6 31#include <sys/stat.h>
df111b53 32#include <thread>
6bb38cd6
RG
33
34#include "dnsdist.hh"
b5521206 35#include "dnsdist-console.hh"
e7c732b8 36#include "dnsdist-ecs.hh"
dd9c8246 37#include "dnsdist-healthchecks.hh"
6bb38cd6 38#include "dnsdist-lua.hh"
0ed8f0fa
RG
39#ifdef LUAJIT_VERSION
40#include "dnsdist-lua-ffi.hh"
41#endif /* LUAJIT_VERSION */
03b00917 42#include "dnsdist-rings.hh"
5d4e1ef8 43#include "dnsdist-secpoll.hh"
6bb38cd6 44
df111b53 45#include "base64.hh"
b7860997 46#include "dnswriter.hh"
6bb38cd6 47#include "dolog.hh"
0e41337b 48#include "lock.hh"
a227f47d 49#include "protobuf.hh"
6bb38cd6
RG
50#include "sodcrypto.hh"
51
8c15553e
RG
52#ifdef HAVE_LIBSSL
53#include "libssl.hh"
54#endif
55
6bb38cd6 56#include <boost/logic/tribool.hpp>
1720247e 57#include <boost/lexical_cast.hpp>
df111b53 58
6ab65223
PL
59#ifdef HAVE_SYSTEMD
60#include <systemd/sd-daemon.h>
61#endif
62
df111b53 63using std::thread;
64
6bb38cd6 65static vector<std::function<void(void)>>* g_launchWork = nullptr;
2e72cc0e 66
6bb38cd6
RG
67boost::tribool g_noLuaSideEffect;
68static bool g_included{false};
d8d85a30 69
6bb38cd6
RG
70/* this is a best effort way to prevent logging calls with no side-effects in the output of delta()
71 Functions can declare setLuaNoSideEffect() and if nothing else does declare a side effect, or nothing
72 has done so before on this invocation, this call won't be part of delta() output */
73void setLuaNoSideEffect()
153d5065 74{
6bb38cd6
RG
75 if(g_noLuaSideEffect==false) // there has been a side effect already
76 return;
77 g_noLuaSideEffect=true;
78}
153d5065 79
6bb38cd6 80void setLuaSideEffect()
d8d85a30 81{
6bb38cd6
RG
82 g_noLuaSideEffect=false;
83}
f850b032 84
6bb38cd6
RG
85bool getLuaNoSideEffect()
86{
d7a26377
RG
87 if (g_noLuaSideEffect) {
88 return true;
89 }
90 return false;
d8d85a30 91}
92
6bb38cd6 93void resetLuaSideEffect()
2d11d1b2 94{
6bb38cd6 95 g_noLuaSideEffect = boost::logic::indeterminate;
2d11d1b2 96}
97
37b9ba5a 98typedef std::unordered_map<std::string, boost::variant<bool, int, std::string, std::vector<std::pair<int,int> >, std::vector<std::pair<int, std::string> >, std::map<std::string,std::string> > > localbind_t;
6bb38cd6 99
c58723d7 100static void parseLocalBindVars(boost::optional<localbind_t> vars, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface, std::set<int>& cpus, int& tcpListenQueueSize)
efd35aa8
RG
101{
102 if (vars) {
efd35aa8
RG
103 if (vars->count("reusePort")) {
104 reusePort = boost::get<bool>((*vars)["reusePort"]);
105 }
106 if (vars->count("tcpFastOpenQueueSize")) {
107 tcpFastOpenQueueSize = boost::get<int>((*vars)["tcpFastOpenQueueSize"]);
108 }
c58723d7
RG
109 if (vars->count("tcpListenQueueSize")) {
110 tcpListenQueueSize = boost::get<int>((*vars)["tcpListenQueueSize"]);
111 }
efd35aa8
RG
112 if (vars->count("interface")) {
113 interface = boost::get<std::string>((*vars)["interface"]);
114 }
f0e4dcba 115 if (vars->count("cpus")) {
ba8ade8e 116 for (const auto& cpu : boost::get<std::vector<std::pair<int,int>>>((*vars)["cpus"])) {
f0e4dcba
RG
117 cpus.insert(cpu.second);
118 }
119 }
efd35aa8
RG
120 }
121}
122
39006144 123#if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
bf8cd40d 124static bool loadTLSCertificateAndKeys(const std::string& context, std::vector<std::pair<std::string, std::string>>& pairs, boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> keyFiles)
8ef43a02
RG
125{
126 if (certFiles.type() == typeid(std::string) && keyFiles.type() == typeid(std::string)) {
127 auto certFile = boost::get<std::string>(certFiles);
128 auto keyFile = boost::get<std::string>(keyFiles);
bf8cd40d
RG
129 pairs.clear();
130 pairs.push_back({certFile, keyFile});
8ef43a02
RG
131 }
132 else if (certFiles.type() == typeid(std::vector<std::pair<int,std::string>>) && keyFiles.type() == typeid(std::vector<std::pair<int,std::string>>))
133 {
134 auto certFilesVect = boost::get<std::vector<std::pair<int,std::string>>>(certFiles);
135 auto keyFilesVect = boost::get<std::vector<std::pair<int,std::string>>>(keyFiles);
136 if (certFilesVect.size() == keyFilesVect.size()) {
bf8cd40d 137 pairs.clear();
8ef43a02 138 for (size_t idx = 0; idx < certFilesVect.size(); idx++) {
bf8cd40d 139 pairs.push_back({certFilesVect.at(idx).second, keyFilesVect.at(idx).second});
8ef43a02
RG
140 }
141 }
142 else {
bf8cd40d
RG
143 errlog("Error, mismatching number of certificates and keys in call to %s()!", context);
144 g_outputBuffer="Error, mismatching number of certificates and keys in call to " + context + "()!";
8ef43a02
RG
145 return false;
146 }
147 }
148 else {
bf8cd40d
RG
149 errlog("Error, mismatching number of certificates and keys in call to %s()!", context);
150 g_outputBuffer="Error, mismatching number of certificates and keys in call to " + context + "()!";
8ef43a02
RG
151 return false;
152 }
153
154 return true;
155}
b54e94dc
RG
156
157static void parseTLSConfig(TLSConfig& config, const std::string& context, boost::optional<localbind_t> vars)
158{
159 if (vars->count("ciphers")) {
160 config.d_ciphers = boost::get<const string>((*vars)["ciphers"]);
161 }
162
163 if (vars->count("ciphersTLS13")) {
164 config.d_ciphers13 = boost::get<const string>((*vars)["ciphersTLS13"]);
165 }
166
167#ifdef HAVE_LIBSSL
168 if (vars->count("minTLSVersion")) {
169 config.d_minTLSVersion = libssl_tls_version_from_string(boost::get<const string>((*vars)["minTLSVersion"]));
170 }
171#endif /* HAVE_LIBSSL */
172
173 if (vars->count("ticketKeyFile")) {
174 config.d_ticketKeyFile = boost::get<const string>((*vars)["ticketKeyFile"]);
175 }
176
177 if (vars->count("ticketsKeysRotationDelay")) {
178 config.d_ticketsKeyRotationDelay = boost::get<int>((*vars)["ticketsKeysRotationDelay"]);
179 }
180
181 if (vars->count("numberOfTicketsKeys")) {
182 config.d_numberOfTicketsKeys = boost::get<int>((*vars)["numberOfTicketsKeys"]);
183 }
184
185 if (vars->count("preferServerCiphers")) {
186 config.d_preferServerCiphers = boost::get<bool>((*vars)["preferServerCiphers"]);
187 }
188
25426675
MH
189 if (vars->count("sessionTimeout")) {
190 config.d_sessionTimeout = boost::get<int>((*vars)["sessionTimeout"]);
191 }
192
b54e94dc
RG
193 if (vars->count("sessionTickets")) {
194 config.d_enableTickets = boost::get<bool>((*vars)["sessionTickets"]);
195 }
196
197 if (vars->count("numberOfStoredSessions")) {
198 auto value = boost::get<int>((*vars)["numberOfStoredSessions"]);
199 if (value < 0) {
200 errlog("Invalid value '%d' for %s() parameter 'numberOfStoredSessions', should be >= 0, dismissing", value, context);
201 g_outputBuffer="Invalid value '" + std::to_string(value) + "' for " + context + "() parameter 'numberOfStoredSessions', should be >= 0, dimissing";
202 }
203 config.d_maxStoredSessions = value;
204 }
205
206 if (vars->count("ocspResponses")) {
207 auto files = boost::get<std::vector<std::pair<int, std::string>>>((*vars)["ocspResponses"]);
208 for (const auto& file : files) {
209 config.d_ocspFiles.push_back(file.second);
210 }
211 }
7f8a5a32
RG
212
213 if (vars->count("keyLogFile")) {
f8bbe49b 214#ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
7f8a5a32 215 config.d_keyLogFile = boost::get<const string>((*vars)["keyLogFile"]);
f8bbe49b
RG
216#else
217 errlog("TLS Key logging has been enabled using the 'keyLogFile' parameter to %s(), but this version of OpenSSL does not support it", context);
218 g_outputBuffer = "TLS Key logging has been enabled using the 'keyLogFile' parameter to " + context + "(), but this version of OpenSSL does not support it";
219#endif
7f8a5a32 220 }
b54e94dc
RG
221}
222
39006144 223#endif // defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
8ef43a02 224
a9599e73 225static void setupLuaConfig(bool client, bool configCheck)
df111b53 226{
98650fde 227 typedef std::unordered_map<std::string, boost::variant<bool, std::string, vector<pair<int, std::string> >, DownstreamState::checkfunc_t > > newserver_t;
6f2a4580 228 g_lua.writeFunction("inClientStartup", [client]() {
541dd48f 229 return client && !g_configurationDone;
6f2a4580
PD
230 });
231
8a059611 232 g_lua.writeFunction("inConfigCheck", [configCheck]() {
203b5348
RG
233 return !configCheck;
234 });
235
6bb38cd6 236 g_lua.writeFunction("newServer",
203b5348 237 [client, configCheck](boost::variant<string,newserver_t> pvars, boost::optional<int> qps) {
a5526b57
CH
238 setLuaSideEffect();
239
240 std::shared_ptr<DownstreamState> ret = std::make_shared<DownstreamState>(ComboAddress());
241 newserver_t vars;
242
243 ComboAddress serverAddr;
244 std::string serverAddressStr;
245 if(auto addrStr = boost::get<string>(&pvars)) {
246 serverAddressStr = *addrStr;
247 if(qps) {
248 vars["qps"] = std::to_string(*qps);
249 }
250 } else {
251 vars = boost::get<newserver_t>(pvars);
252 serverAddressStr = boost::get<string>(vars["address"]);
253 }
2e72cc0e 254
a5526b57
CH
255 try {
256 serverAddr = ComboAddress(serverAddressStr, 53);
257 }
258 catch(const PDNSException& e) {
259 g_outputBuffer="Error creating new server: "+string(e.reason);
260 errlog("Error creating new server with address %s: %s", serverAddressStr, e.reason);
261 return ret;
262 }
263 catch(std::exception& e) {
264 g_outputBuffer="Error creating new server: "+string(e.what());
265 errlog("Error creating new server with address %s: %s", serverAddressStr, e.what());
266 return ret;
267 }
2e72cc0e 268
a5526b57
CH
269 if(IsAnyAddress(serverAddr)) {
270 g_outputBuffer="Error creating new server: invalid address for a downstream server.";
271 errlog("Error creating new server: %s is not a valid address for a downstream server", serverAddressStr);
272 return ret;
273 }
274
275 ComboAddress sourceAddr;
70b0d0e2 276 std::string sourceItfName;
a5526b57
CH
277 unsigned int sourceItf = 0;
278 size_t numberOfSockets = 1;
279 std::set<int> cpus;
fbe2a2e0 280
8592b46e
RG
281 if(vars.count("source")) {
282 /* handle source in the following forms:
283 - v4 address ("192.0.2.1")
284 - v6 address ("2001:DB8::1")
285 - interface name ("eth0")
286 - v4 address and interface name ("192.0.2.1@eth0")
287 - v6 address and interface name ("2001:DB8::1@eth0")
288 */
289 const string source = boost::get<string>(vars["source"]);
290 bool parsed = false;
291 std::string::size_type pos = source.find("@");
292 if (pos == std::string::npos) {
293 /* no '@', try to parse that as a valid v4/v6 address */
294 try {
295 sourceAddr = ComboAddress(source);
296 parsed = true;
297 }
298 catch(...)
299 {
300 }
301 }
302
303 if (parsed == false)
304 {
305 /* try to parse as interface name, or v4/v6@itf */
306 sourceItfName = source.substr(pos == std::string::npos ? 0 : pos + 1);
307 unsigned int itfIdx = if_nametoindex(sourceItfName.c_str());
308
309 if (itfIdx != 0) {
310 if (pos == 0 || pos == std::string::npos) {
311 /* "eth0" or "@eth0" */
312 sourceItf = itfIdx;
313 }
314 else {
315 /* "192.0.2.1@eth0" */
316 sourceAddr = ComboAddress(source.substr(0, pos));
317 sourceItf = itfIdx;
318 }
83fe2c55 319#ifdef SO_BINDTODEVICE
8592b46e
RG
320 /* we need to retain CAP_NET_RAW to be able to set SO_BINDTODEVICE in the health checks */
321 g_capabilitiesToRetain.insert("CAP_NET_RAW");
83fe2c55 322#endif
8592b46e
RG
323 }
324 else
325 {
326 warnlog("Dismissing source %s because '%s' is not a valid interface name", source, sourceItfName);
327 }
328 }
329 }
330
331 if (vars.count("sockets")) {
332 numberOfSockets = std::stoul(boost::get<string>(vars["sockets"]));
333 if (numberOfSockets == 0) {
334 warnlog("Dismissing invalid number of sockets '%s', using 1 instead", boost::get<string>(vars["sockets"]));
335 numberOfSockets = 1;
336 }
337 }
150105a2 338
203b5348
RG
339 // create but don't connect the socket in client or check-config modes
340 ret=std::make_shared<DownstreamState>(serverAddr, sourceAddr, sourceItf, sourceItfName, numberOfSockets, !(client || configCheck));
b6e26895
RG
341 if (!(client || configCheck)) {
342 infolog("Added downstream server %s", serverAddr.toStringWithPort());
343 }
fbe2a2e0 344
8592b46e
RG
345 if(vars.count("qps")) {
346 int qpsVal=std::stoi(boost::get<string>(vars["qps"]));
347 ret->qps=QPSLimiter(qpsVal, qpsVal);
348 }
349
350 if(vars.count("order")) {
351 ret->order=std::stoi(boost::get<string>(vars["order"]));
352 }
353
354 if(vars.count("weight")) {
355 try {
356 int weightVal=std::stoi(boost::get<string>(vars["weight"]));
357
358 if(weightVal < 1) {
359 errlog("Error creating new server: downstream weight value must be greater than 0.");
360 return ret;
361 }
362
363 ret->setWeight(weightVal);
364 }
365 catch(std::exception& e) {
366 // std::stoi will throw an exception if the string isn't in a value int range
367 errlog("Error creating new server: downstream weight value must be between %s and %s", 1, std::numeric_limits<int>::max());
368 return ret;
369 }
370 }
371
372 if(vars.count("retries")) {
373 ret->retries=std::stoi(boost::get<string>(vars["retries"]));
374 }
375
376 if(vars.count("checkInterval")) {
377 ret->checkInterval=static_cast<unsigned int>(std::stoul(boost::get<string>(vars["checkInterval"])));
378 }
379
380 if(vars.count("tcpConnectTimeout")) {
381 ret->tcpConnectTimeout=std::stoi(boost::get<string>(vars["tcpConnectTimeout"]));
382 }
383
384 if(vars.count("tcpSendTimeout")) {
385 ret->tcpSendTimeout=std::stoi(boost::get<string>(vars["tcpSendTimeout"]));
386 }
387
388 if(vars.count("tcpRecvTimeout")) {
389 ret->tcpRecvTimeout=std::stoi(boost::get<string>(vars["tcpRecvTimeout"]));
390 }
391
392 if(vars.count("tcpFastOpen")) {
393 bool fastOpen = boost::get<bool>(vars["tcpFastOpen"]);
394 if (fastOpen) {
d987f632 395#ifdef MSG_FASTOPEN
8592b46e 396 ret->tcpFastOpen=true;
d987f632 397#else
8592b46e 398 warnlog("TCP Fast Open has been configured on downstream server %s but is not supported", boost::get<string>(vars["address"]));
d987f632 399#endif
8592b46e
RG
400 }
401 }
284d460c 402
8592b46e 403 if(vars.count("name")) {
be05aa91 404 ret->setName(boost::get<string>(vars["name"]));
8592b46e 405 }
18eeccc9 406
8592b46e
RG
407 if (vars.count("id")) {
408 ret->setId(boost::lexical_cast<boost::uuids::uuid>(boost::get<string>(vars["id"])));
409 }
1720247e 410
8592b46e
RG
411 if(vars.count("checkName")) {
412 ret->checkName=DNSName(boost::get<string>(vars["checkName"]));
413 }
ad485896 414
8592b46e
RG
415 if(vars.count("checkType")) {
416 ret->checkType=boost::get<string>(vars["checkType"]);
417 }
ad485896 418
8592b46e
RG
419 if(vars.count("checkClass")) {
420 ret->checkClass=std::stoi(boost::get<string>(vars["checkClass"]));
421 }
de9f7157 422
8592b46e
RG
423 if(vars.count("checkFunction")) {
424 ret->checkFunction= boost::get<DownstreamState::checkfunc_t>(vars["checkFunction"]);
425 }
98650fde 426
8592b46e
RG
427 if(vars.count("checkTimeout")) {
428 ret->checkTimeout = std::stoi(boost::get<string>(vars["checkTimeout"]));
429 }
b7e6f4a1 430
8592b46e
RG
431 if(vars.count("setCD")) {
432 ret->setCD=boost::get<bool>(vars["setCD"]);
433 }
21830638 434
8592b46e
RG
435 if(vars.count("mustResolve")) {
436 ret->mustResolve=boost::get<bool>(vars["mustResolve"]);
437 }
a6e02424 438
8592b46e
RG
439 if(vars.count("useClientSubnet")) {
440 ret->useECS=boost::get<bool>(vars["useClientSubnet"]);
441 }
ca404e94 442
bde73d5b
RG
443 if(vars.count("useProxyProtocol")) {
444 ret->useProxyProtocol = boost::get<bool>(vars["useProxyProtocol"]);
445 }
446
8592b46e
RG
447 if(vars.count("disableZeroScope")) {
448 ret->disableZeroScope=boost::get<bool>(vars["disableZeroScope"]);
449 }
996db8e9 450
8592b46e
RG
451 if(vars.count("ipBindAddrNoPort")) {
452 ret->ipBindAddrNoPort=boost::get<bool>(vars["ipBindAddrNoPort"]);
453 }
5cc8371b 454
8592b46e
RG
455 if(vars.count("addXPF")) {
456 ret->xpfRRCode=std::stoi(boost::get<string>(vars["addXPF"]));
457 }
5602f131 458
8592b46e
RG
459 if(vars.count("maxCheckFailures")) {
460 ret->maxCheckFailures=std::stoi(boost::get<string>(vars["maxCheckFailures"]));
461 }
9e87dcb8 462
8592b46e
RG
463 if(vars.count("rise")) {
464 ret->minRiseSuccesses=std::stoi(boost::get<string>(vars["rise"]));
465 }
1b633bec 466
8592b46e 467 if(vars.count("cpus")) {
ba8ade8e 468 for (const auto& cpu : boost::get<vector<pair<int,string>>>(vars["cpus"])) {
8592b46e
RG
469 cpus.insert(std::stoi(cpu.second));
470 }
471 }
3a8ee8bf 472
8592b46e
RG
473 /* this needs to be done _AFTER_ the order has been set,
474 since the server are kept ordered inside the pool */
475 auto localPools = g_pools.getCopy();
476 if(vars.count("pool")) {
477 if(auto* pool = boost::get<string>(&vars["pool"])) {
478 ret->pools.insert(*pool);
479 }
480 else {
481 auto pools = boost::get<vector<pair<int, string> > >(vars["pool"]);
482 for(auto& p : pools) {
483 ret->pools.insert(p.second);
484 }
485 }
486 for(const auto& poolName: ret->pools) {
487 addServerToPool(localPools, poolName, ret);
488 }
489 }
490 else {
491 addServerToPool(localPools, "", ret);
492 }
493 g_pools.setState(localPools);
494
495 if (ret->connected) {
496 ret->threadStarted.test_and_set();
497
498 if(g_launchWork) {
499 g_launchWork->push_back([ret,cpus]() {
500 ret->tid = thread(responderThread, ret);
501 if (!cpus.empty()) {
502 mapThreadToCPUList(ret->tid.native_handle(), cpus);
503 }
504 });
505 }
506 else {
507 ret->tid = thread(responderThread, ret);
508 if (!cpus.empty()) {
509 mapThreadToCPUList(ret->tid.native_handle(), cpus);
510 }
511 }
512 }
8c82c02d 513
8592b46e
RG
514 auto states = g_dstates.getCopy();
515 states.push_back(ret);
516 std::stable_sort(states.begin(), states.end(), [](const decltype(ret)& a, const decltype(ret)& b) {
517 return a->order < b->order;
518 });
519 g_dstates.setState(states);
520 return ret;
521 } );
df111b53 522
6bb38cd6 523 g_lua.writeFunction("rmServer",
adfdcc4b 524 [](boost::variant<std::shared_ptr<DownstreamState>, int, std::string> var)
8592b46e 525 {
f758857a 526 setLuaSideEffect();
adfdcc4b 527 shared_ptr<DownstreamState> server = nullptr;
886e2cf2 528 auto states = g_dstates.getCopy();
adfdcc4b 529 if (auto* rem = boost::get<shared_ptr<DownstreamState>>(&var)) {
886e2cf2
RG
530 server = *rem;
531 }
adfdcc4b
RG
532 else if (auto str = boost::get<std::string>(&var)) {
533 const auto uuid = getUniqueID(*str);
534 for (auto& state : states) {
535 if (state->id == uuid) {
536 server = state;
537 }
538 }
539 }
886e2cf2
RG
540 else {
541 int idx = boost::get<int>(var);
0f06cd4c 542 server = states.at(idx);
886e2cf2 543 }
adfdcc4b
RG
544 if (!server) {
545 throw std::runtime_error("unable to locate the requested server");
546 }
886e2cf2
RG
547 auto localPools = g_pools.getCopy();
548 for (const string& poolName : server->pools) {
549 removeServerFromPool(localPools, poolName, server);
550 }
0f06cd4c
RG
551 /* the server might also be in the default pool */
552 removeServerFromPool(localPools, "", server);
886e2cf2
RG
553 g_pools.setState(localPools);
554 states.erase(remove(states.begin(), states.end(), server), states.end());
555 g_dstates.setState(states);
8592b46e 556 } );
df111b53 557
f758857a 558 g_lua.writeFunction("truncateTC", [](bool tc) { setLuaSideEffect(); g_truncateTC=tc; });
559 g_lua.writeFunction("fixupCase", [](bool fu) { setLuaSideEffect(); g_fixupCase=fu; });
df111b53 560
df111b53 561 g_lua.writeFunction("addACL", [](const std::string& domain) {
f758857a 562 setLuaSideEffect();
e5a14b2b 563 g_ACL.modify([domain](NetmaskGroup& nmg) { nmg.addMask(domain); });
df111b53 564 });
2e72cc0e 565
a1d18701 566 g_lua.writeFunction("rmACL", [](const std::string& netmask) {
567 setLuaSideEffect();
568 g_ACL.modify([netmask](NetmaskGroup& nmg) { nmg.deleteMask(netmask); });
569 });
570
efd35aa8 571 g_lua.writeFunction("setLocal", [client](const std::string& addr, boost::optional<localbind_t> vars) {
f758857a 572 setLuaSideEffect();
5949b95b 573 if(client)
574 return;
85e4ce52
RG
575 if (g_configurationDone) {
576 g_outputBuffer="setLocal cannot be used at runtime!\n";
577 return;
578 }
efd35aa8
RG
579 bool reusePort = false;
580 int tcpFastOpenQueueSize = 0;
c58723d7 581 int tcpListenQueueSize = 0;
efd35aa8 582 std::string interface;
f0e4dcba 583 std::set<int> cpus;
efd35aa8 584
c58723d7 585 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize);
efd35aa8 586
5949b95b 587 try {
588 ComboAddress loc(addr, 53);
6e9fd124 589 for (auto it = g_frontends.begin(); it != g_frontends.end(); ) {
265260f5 590 /* DoH, DoT and DNSCrypt frontends are separate */
e52d278a 591 if ((*it)->tlsFrontend == nullptr && (*it)->dnscryptCtx == nullptr && (*it)->dohFrontend == nullptr) {
6e9fd124
RG
592 it = g_frontends.erase(it);
593 }
594 else {
595 ++it;
596 }
597 }
598
599 // only works pre-startup, so no sync necessary
600 g_frontends.push_back(std::unique_ptr<ClientState>(new ClientState(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus)));
c58723d7
RG
601 auto tcpCS = std::unique_ptr<ClientState>(new ClientState(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus));
602 if (tcpListenQueueSize > 0) {
603 tcpCS->tcpListenQueueSize = tcpListenQueueSize;
604 }
605 g_frontends.push_back(std::move(tcpCS));
5949b95b 606 }
6e9fd124 607 catch(const std::exception& e) {
5949b95b 608 g_outputBuffer="Error: "+string(e.what())+"\n";
609 }
610 });
611
efd35aa8 612 g_lua.writeFunction("addLocal", [client](const std::string& addr, boost::optional<localbind_t> vars) {
f758857a 613 setLuaSideEffect();
2e72cc0e 614 if(client)
615 return;
85e4ce52
RG
616 if (g_configurationDone) {
617 g_outputBuffer="addLocal cannot be used at runtime!\n";
618 return;
619 }
efd35aa8
RG
620 bool reusePort = false;
621 int tcpFastOpenQueueSize = 0;
c58723d7 622 int tcpListenQueueSize = 0;
efd35aa8 623 std::string interface;
f0e4dcba 624 std::set<int> cpus;
efd35aa8 625
c58723d7 626 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize);
efd35aa8 627
2e72cc0e 628 try {
629 ComboAddress loc(addr, 53);
6e9fd124
RG
630 // only works pre-startup, so no sync necessary
631 g_frontends.push_back(std::unique_ptr<ClientState>(new ClientState(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus)));
c58723d7
RG
632 auto tcpCS = std::unique_ptr<ClientState>(new ClientState(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus));
633 if (tcpListenQueueSize > 0) {
634 tcpCS->tcpListenQueueSize = tcpListenQueueSize;
635 }
636 g_frontends.push_back(std::move(tcpCS));
2e72cc0e 637 }
638 catch(std::exception& e) {
56d44226
RG
639 g_outputBuffer="Error: "+string(e.what())+"\n";
640 errlog("Error while trying to listen on %s: %s\n", addr, string(e.what()));
2e72cc0e 641 }
642 });
6bb38cd6 643
e4944ea0 644 g_lua.writeFunction("setACL", [](boost::variant<string,vector<pair<int, string>>> inp) {
f758857a 645 setLuaSideEffect();
e5a14b2b 646 NetmaskGroup nmg;
e4944ea0 647 if(auto str = boost::get<string>(&inp)) {
648 nmg.addMask(*str);
649 }
650 else for(const auto& p : boost::get<vector<pair<int,string>>>(inp)) {
e5a14b2b 651 nmg.addMask(p.second);
cffde2fd 652 }
653 g_ACL.setState(nmg);
df111b53 654 });
6bb38cd6 655
df111b53 656 g_lua.writeFunction("showACL", []() {
f758857a 657 setLuaNoSideEffect();
df111b53 658 vector<string> vec;
cffde2fd 659
a9c2e4ab 660 g_ACL.getLocal()->toStringVector(&vec);
cffde2fd 661
df111b53 662 for(const auto& s : vec)
cffde2fd 663 g_outputBuffer+=s+"\n";
664
df111b53 665 });
6bb38cd6 666
6ab65223
PL
667 g_lua.writeFunction("shutdown", []() {
668#ifdef HAVE_SYSTEMD
669 sd_notify(0, "STOPPING=1");
a227f47d
RG
670#endif /* HAVE_SYSTEMD */
671#if 0
672 // Useful for debugging leaks, but might lead to race under load
673 // since other threads are still runing.
674 for(auto& frontend : g_tlslocals) {
675 frontend->cleanup();
676 }
677 g_tlslocals.clear();
678#ifdef HAVE_PROTOBUF
679 google::protobuf::ShutdownProtobufLibrary();
680#endif /* HAVE_PROTOBUF */
681#endif /* 0 */
6ab65223
PL
682 _exit(0);
683 } );
df111b53 684
7f768697
CHB
685 typedef std::unordered_map<std::string, boost::variant<bool, std::string> > showserversopts_t;
686
687 g_lua.writeFunction("showServers", [](boost::optional<showserversopts_t> vars) {
f758857a 688 setLuaNoSideEffect();
7f768697
CHB
689 bool showUUIDs = false;
690 if (vars) {
691 if (vars->count("showUUIDs")) {
692 showUUIDs = boost::get<bool>((*vars)["showUUIDs"]);
693 }
df111b53 694 }
7f768697
CHB
695 try {
696 ostringstream ret;
697 boost::format fmt;
698 if (showUUIDs) {
699 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%" );
700 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
701 ret << (fmt % "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools" % "UUID") << endl;
702 } else {
703 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%" );
704 ret << (fmt % "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools") << endl;
705 }
df111b53 706
7f768697
CHB
707 uint64_t totQPS{0}, totQueries{0}, totDrops{0};
708 int counter=0;
709 auto states = g_dstates.getLocal();
710 for(const auto& s : *states) {
711 string status = s->getStatus();
712 string pools;
713 for(auto& p : s->pools) {
714 if(!pools.empty())
715 pools+=" ";
716 pools+=p;
717 }
718 if (showUUIDs) {
be05aa91 719 ret << (fmt % counter % s->getName() % s->remote.toStringWithPort() %
7f768697
CHB
720 status %
721 s->queryLoad % s->qps.getRate() % s->order % s->weight % s->queries.load() % s->reuseds.load() % (s->dropRate) % (s->latencyUsec/1000.0) % s->outstanding.load() % pools % s->id) << endl;
722 } else {
be05aa91 723 ret << (fmt % counter % s->getName() % s->remote.toStringWithPort() %
7f768697
CHB
724 status %
725 s->queryLoad % s->qps.getRate() % s->order % s->weight % s->queries.load() % s->reuseds.load() % (s->dropRate) % (s->latencyUsec/1000.0) % s->outstanding.load() % pools) << endl;
726 }
727 totQPS += s->queryLoad;
728 totQueries += s->queries.load();
729 totDrops += s->reuseds.load();
730 ++counter;
731 }
732 if (showUUIDs) {
733 ret<< (fmt % "All" % "" % "" % ""
734 %
735 (double)totQPS % "" % "" % "" % totQueries % totDrops % "" % "" % "" % "" % "" ) << endl;
736 } else {
737 ret<< (fmt % "All" % "" % "" % ""
738 %
739 (double)totQPS % "" % "" % "" % totQueries % totDrops % "" % "" % "" % "" ) << endl;
740 }
741
742 g_outputBuffer=ret.str();
743 } catch(std::exception& e) {
744 g_outputBuffer=e.what();
745 throw;
746 }
df111b53 747 });
748
6bb38cd6
RG
749 g_lua.writeFunction("getServers", []() {
750 setLuaNoSideEffect();
751 vector<pair<int, std::shared_ptr<DownstreamState> > > ret;
752 int count=1;
753 for(const auto& s : g_dstates.getCopy()) {
754 ret.push_back(make_pair(count++, s));
755 }
756 return ret;
8499caaf 757 });
e27097e4 758
6bb38cd6
RG
759 g_lua.writeFunction("getPoolServers", [](string pool) {
760 return getDownstreamCandidates(g_pools.getCopy(), pool);
b1bec9f0
RG
761 });
762
adfdcc4b
RG
763 g_lua.writeFunction("getServer", [client](boost::variant<unsigned int, std::string> i) {
764 if (client) {
6bb38cd6 765 return std::make_shared<DownstreamState>(ComboAddress());
adfdcc4b
RG
766 }
767 auto states = g_dstates.getCopy();
768 if (auto str = boost::get<std::string>(&i)) {
769 const auto uuid = getUniqueID(*str);
770 for (auto& state : states) {
771 if (state->id == uuid) {
772 return state;
773 }
774 }
775 }
776 else if (auto pos = boost::get<unsigned int>(&i)) {
777 return states.at(*pos);
778 }
779
780 g_outputBuffer = "Error: no rule matched\n";
781 return std::shared_ptr<DownstreamState>(nullptr);
731774a8 782 });
783
6bb38cd6 784 g_lua.writeFunction("carbonServer", [](const std::string& address, boost::optional<string> ourName,
48341d8f 785 boost::optional<unsigned int> interval, boost::optional<string> namespace_name,
f7a645ec
RG
786 boost::optional<string> instance_name) {
787 setLuaSideEffect();
788 auto ours = g_carbon.getCopy();
789 ours.push_back({
790 ComboAddress(address, 2003),
791 (namespace_name && !namespace_name->empty()) ? *namespace_name : "dnsdist",
792 ourName ? *ourName : "",
793 (instance_name && !instance_name->empty()) ? *instance_name : "main" ,
794 interval ? *interval : 30
795 });
796 g_carbon.setState(ours);
797 });
87c605c4 798
203b5348 799 g_lua.writeFunction("webserver", [client,configCheck](const std::string& address, const std::string& password, const boost::optional<std::string> apiKey, const boost::optional<std::map<std::string, std::string> > customHeaders) {
f758857a 800 setLuaSideEffect();
59b364f3
RG
801 ComboAddress local;
802 try {
803 local = ComboAddress(address);
804 }
805 catch (const PDNSException& e) {
806 throw std::runtime_error(std::string("Error parsing the bind address for the webserver: ") + e.reason);
807 }
808
203b5348 809 if (client || configCheck) {
59b364f3
RG
810 return;
811 }
812
6bb38cd6
RG
813 try {
814 int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
815 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
816 SBind(sock, local);
817 SListen(sock, 5);
818 auto launch=[sock, local, password, apiKey, customHeaders]() {
32c97b56
CHB
819 setWebserverPassword(password);
820 setWebserverAPIKey(apiKey);
821 setWebserverCustomHeaders(customHeaders);
80dbd7d2 822 thread t(dnsdistWebserverThread, sock, local);
6bb38cd6
RG
823 t.detach();
824 };
825 if(g_launchWork)
826 g_launchWork->push_back(launch);
827 else
828 launch();
731774a8 829 }
830 catch(std::exception& e) {
6bb38cd6
RG
831 g_outputBuffer="Unable to bind to webserver socket on " + local.toStringWithPort() + ": " + e.what();
832 errlog("Unable to bind to webserver socket on %s: %s", local.toStringWithPort(), e.what());
731774a8 833 }
731774a8 834
835 });
836
32c97b56
CHB
837 typedef std::unordered_map<std::string, boost::variant<std::string, std::map<std::string, std::string>> > webserveropts_t;
838
839 g_lua.writeFunction("setWebserverConfig", [](boost::optional<webserveropts_t> vars) {
80dbd7d2 840 setLuaSideEffect();
32c97b56
CHB
841
842 if (!vars) {
843 return ;
844 }
845 if(vars->count("password")) {
846 const std::string password = boost::get<std::string>(vars->at("password"));
847
848 setWebserverPassword(password);
849 }
850 if(vars->count("apiKey")) {
32c97b56
CHB
851 const std::string apiKey = boost::get<std::string>(vars->at("apiKey"));
852
853 setWebserverAPIKey(apiKey);
854 }
855 if(vars->count("customHeaders")) {
856 const boost::optional<std::map<std::string, std::string> > headers = boost::get<std::map<std::string, std::string> >(vars->at("customHeaders"));
857
858 setWebserverCustomHeaders(headers);
859 }
80dbd7d2
CHB
860 });
861
203b5348 862 g_lua.writeFunction("controlSocket", [client,configCheck](const std::string& str) {
87c605c4 863 setLuaSideEffect();
6bb38cd6 864 ComboAddress local(str, 5199);
832c1792 865
203b5348 866 if(client || configCheck) {
6bb38cd6 867 g_serverControl = local;
87c605c4
RG
868 return;
869 }
731774a8 870
9c9b4998
RG
871 g_consoleEnabled = true;
872#ifdef HAVE_LIBSODIUM
873 if (g_configurationDone && g_consoleKey.empty()) {
874 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");
875 }
876#endif
877
6bb38cd6
RG
878 try {
879 int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
880 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
881 SBind(sock, local);
882 SListen(sock, 5);
883 auto launch=[sock, local]() {
884 thread t(controlThread, sock, local);
885 t.detach();
886 };
887 if(g_launchWork)
888 g_launchWork->push_back(launch);
889 else
890 launch();
ae3dfa48 891
6bb38cd6
RG
892 }
893 catch(std::exception& e) {
894 g_outputBuffer="Unable to bind to control socket on " + local.toStringWithPort() + ": " + e.what();
895 errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what());
896 }
63beb26d
G
897 });
898
b5521206
RG
899 g_lua.writeFunction("addConsoleACL", [](const std::string& netmask) {
900 setLuaSideEffect();
901#ifndef HAVE_LIBSODIUM
902 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
903#endif
904
905 g_consoleACL.modify([netmask](NetmaskGroup& nmg) { nmg.addMask(netmask); });
906 });
907
908 g_lua.writeFunction("setConsoleACL", [](boost::variant<string,vector<pair<int, string>>> inp) {
909 setLuaSideEffect();
910
911#ifndef HAVE_LIBSODIUM
912 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
913#endif
914
915 NetmaskGroup nmg;
916 if(auto str = boost::get<string>(&inp)) {
917 nmg.addMask(*str);
918 }
919 else for(const auto& p : boost::get<vector<pair<int,string>>>(inp)) {
920 nmg.addMask(p.second);
921 }
922 g_consoleACL.setState(nmg);
923 });
924
925 g_lua.writeFunction("showConsoleACL", []() {
926 setLuaNoSideEffect();
927
928#ifndef HAVE_LIBSODIUM
929 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
930#endif
931
932 vector<string> vec;
933 g_consoleACL.getLocal()->toStringVector(&vec);
934
935 for(const auto& s : vec) {
936 g_outputBuffer += s + "\n";
937 }
938 });
939
6bb38cd6
RG
940 g_lua.writeFunction("clearQueryCounters", []() {
941 unsigned int size{0};
942 {
943 WriteLock wl(&g_qcount.queryLock);
944 size = g_qcount.records.size();
945 g_qcount.records.clear();
946 }
6eecd4c2 947
6bb38cd6
RG
948 boost::format fmt("%d records cleared from query counter buffer\n");
949 g_outputBuffer = (fmt % size).str();
2332b03c 950 });
951
6bb38cd6
RG
952 g_lua.writeFunction("getQueryCounters", [](boost::optional<unsigned int> optMax) {
953 setLuaNoSideEffect();
954 ReadLock rl(&g_qcount.queryLock);
955 g_outputBuffer = "query counting is currently: ";
956 g_outputBuffer+= g_qcount.enabled ? "enabled" : "disabled";
957 g_outputBuffer+= (boost::format(" (%d records in buffer)\n") % g_qcount.records.size()).str();
f39b7598 958
6bb38cd6
RG
959 boost::format fmt("%-3d %s: %d request(s)\n");
960 QueryCountRecords::iterator it;
961 unsigned int max = optMax ? *optMax : 10;
962 unsigned int index{1};
963 for(it = g_qcount.records.begin(); it != g_qcount.records.end() && index <= max; ++it, ++index) {
964 g_outputBuffer += (fmt % index % it->first % it->second).str();
965 }
808c5ef7 966 });
967
6bb38cd6 968 g_lua.writeFunction("setQueryCount", [](bool enabled) { g_qcount.enabled=enabled; });
ae3dfa48 969
6bb38cd6
RG
970 g_lua.writeFunction("setQueryCountFilter", [](QueryCountFilter func) {
971 g_qcount.filter = func;
886e2cf2
RG
972 });
973
6bb38cd6
RG
974 g_lua.writeFunction("makeKey", []() {
975 setLuaNoSideEffect();
976 g_outputBuffer="setKey("+newKey()+")\n";
6bba426c 977 });
978
6bb38cd6 979 g_lua.writeFunction("setKey", [](const std::string& key) {
b5521206 980 if(!g_configurationDone && ! g_consoleKey.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
6bb38cd6
RG
981 return; // but later setKeys() trump the -k value again
982 }
b5521206
RG
983#ifndef HAVE_LIBSODIUM
984 warnlog("Calling setKey() while libsodium support has not been enabled is not secure, and will result in cleartext communications");
985#endif
6bba426c 986
6bb38cd6
RG
987 setLuaSideEffect();
988 string newkey;
989 if(B64Decode(key, newkey) < 0) {
990 g_outputBuffer=string("Unable to decode ")+key+" as Base64";
991 errlog("%s", g_outputBuffer);
992 }
2332b03c 993 else
b5521206 994 g_consoleKey=newkey;
2332b03c 995 });
996
6bb38cd6
RG
997 g_lua.writeFunction("testCrypto", [](boost::optional<string> optTestMsg)
998 {
999 setLuaNoSideEffect();
1000#ifdef HAVE_LIBSODIUM
1001 try {
1002 string testmsg;
2332b03c 1003
6bb38cd6
RG
1004 if (optTestMsg) {
1005 testmsg = *optTestMsg;
1006 }
1007 else {
1008 testmsg = "testStringForCryptoTests";
1009 }
6eecd4c2 1010
6bb38cd6
RG
1011 SodiumNonce sn, sn2;
1012 sn.init();
1013 sn2=sn;
b5521206
RG
1014 string encrypted = sodEncryptSym(testmsg, g_consoleKey, sn);
1015 string decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2);
4ed8dfeb 1016
6bb38cd6
RG
1017 sn.increment();
1018 sn2.increment();
b7860997 1019
b5521206
RG
1020 encrypted = sodEncryptSym(testmsg, g_consoleKey, sn);
1021 decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2);
36da3ecd 1022
6bb38cd6
RG
1023 if(testmsg == decrypted)
1024 g_outputBuffer="Everything is ok!\n";
1025 else
c29bf3df
RG
1026 g_outputBuffer="Crypto failed.. (the decoded value does not match the cleartext one)\n";
1027 }
1028 catch(const std::exception& e) {
1029 g_outputBuffer="Crypto failed: "+std::string(e.what())+"\n";
6bb38cd6
RG
1030 }
1031 catch(...) {
1032 g_outputBuffer="Crypto failed..\n";
1033 }
1034#else
1035 g_outputBuffer="Crypto not available.\n";
1036#endif
1037 });
b7860997 1038
6bb38cd6 1039 g_lua.writeFunction("setTCPRecvTimeout", [](int timeout) { g_tcpRecvTimeout=timeout; });
89cb6f9a 1040
6bb38cd6 1041 g_lua.writeFunction("setTCPSendTimeout", [](int timeout) { g_tcpSendTimeout=timeout; });
89cb6f9a 1042
6bb38cd6 1043 g_lua.writeFunction("setUDPTimeout", [](int timeout) { g_udpTimeout=timeout; });
d3826006 1044
6bb38cd6
RG
1045 g_lua.writeFunction("setMaxUDPOutstanding", [](uint16_t max) {
1046 if (!g_configurationDone) {
1047 g_maxOutstanding = max;
1048 } else {
1049 g_outputBuffer="Max UDP outstanding cannot be altered at runtime!\n";
b7860997 1050 }
55baa1f2
RG
1051 });
1052
6bb38cd6
RG
1053 g_lua.writeFunction("setMaxTCPClientThreads", [](uint64_t max) {
1054 if (!g_configurationDone) {
1055 g_maxTCPClientThreads = max;
1056 } else {
1057 g_outputBuffer="Maximum TCP client threads count cannot be altered at runtime!\n";
1058 }
55baa1f2
RG
1059 });
1060
6bb38cd6
RG
1061 g_lua.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max) {
1062 if (!g_configurationDone) {
1063 g_maxTCPQueuedConnections = max;
1064 } else {
1065 g_outputBuffer="The maximum number of queued TCP connections cannot be altered at runtime!\n";
1066 }
55baa1f2
RG
1067 });
1068
6bb38cd6
RG
1069 g_lua.writeFunction("setMaxTCPQueriesPerConnection", [](size_t max) {
1070 if (!g_configurationDone) {
1071 g_maxTCPQueriesPerConn = max;
1072 } else {
1073 g_outputBuffer="The maximum number of queries per TCP connection cannot be altered at runtime!\n";
1074 }
57c61ce9
RG
1075 });
1076
6bb38cd6
RG
1077 g_lua.writeFunction("setMaxTCPConnectionsPerClient", [](size_t max) {
1078 if (!g_configurationDone) {
1079 g_maxTCPConnectionsPerClient = max;
1080 } else {
1081 g_outputBuffer="The maximum number of TCP connection per client cannot be altered at runtime!\n";
1082 }
57c61ce9
RG
1083 });
1084
6bb38cd6
RG
1085 g_lua.writeFunction("setMaxTCPConnectionDuration", [](size_t max) {
1086 if (!g_configurationDone) {
1087 g_maxTCPConnectionDuration = max;
1088 } else {
1089 g_outputBuffer="The maximum duration of a TCP connection cannot be altered at runtime!\n";
1090 }
788c3243
RG
1091 });
1092
6bb38cd6 1093 g_lua.writeFunction("setCacheCleaningDelay", [](uint32_t delay) { g_cacheCleaningDelay = delay; });
0570f37c 1094
6bb38cd6 1095 g_lua.writeFunction("setCacheCleaningPercentage", [](uint16_t percentage) { if (percentage < 100) g_cacheCleaningPercentage = percentage; else g_cacheCleaningPercentage = 100; });
0570f37c 1096
6bb38cd6 1097 g_lua.writeFunction("setECSSourcePrefixV4", [](uint16_t prefix) { g_ECSSourcePrefixV4=prefix; });
f39b7598 1098
6bb38cd6 1099 g_lua.writeFunction("setECSSourcePrefixV6", [](uint16_t prefix) { g_ECSSourcePrefixV6=prefix; });
0570f37c 1100
6bb38cd6 1101 g_lua.writeFunction("setECSOverride", [](bool override) { g_ECSOverride=override; });
df111b53 1102
53c57da7
RG
1103 g_lua.writeFunction("setPreserveTrailingData", [](bool preserve) { g_preserveTrailingData = preserve; });
1104
6bb38cd6
RG
1105 g_lua.writeFunction("showDynBlocks", []() {
1106 setLuaNoSideEffect();
1107 auto slow = g_dynblockNMG.getCopy();
1108 struct timespec now;
1109 gettime(&now);
1d3ba133
RG
1110 boost::format fmt("%-24s %8d %8d %-10s %-20s %s\n");
1111 g_outputBuffer = (fmt % "What" % "Seconds" % "Blocks" % "Warning" % "Action" % "Reason").str();
6bb38cd6 1112 for(const auto& e: slow) {
7c888097
SB
1113 if(now < e.second.until)
1114 g_outputBuffer+= (fmt % e.first.toString() % (e.second.until.tv_sec - now.tv_sec) % e.second.blocks % (e.second.warning ? "true" : "false") % DNSAction::typeToString(e.second.action != DNSAction::Action::None ? e.second.action : g_dynBlockAction) % e.second.reason).str();
6bb38cd6
RG
1115 }
1116 auto slow2 = g_dynblockSMT.getCopy();
1117 slow2.visit([&now, &fmt](const SuffixMatchTree<DynBlock>& node) {
1118 if(now <node.d_value.until) {
1119 string dom("empty");
1120 if(!node.d_value.domain.empty())
1121 dom = node.d_value.domain.toString();
1d3ba133 1122 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();
6bb38cd6
RG
1123 }
1124 });
832c1792 1125
520eb5a0 1126 });
df111b53 1127
6bb38cd6 1128 g_lua.writeFunction("clearDynBlocks", []() {
f758857a 1129 setLuaSideEffect();
6bb38cd6
RG
1130 nmts_t nmg;
1131 g_dynblockNMG.setState(nmg);
1132 SuffixMatchTree<DynBlock> smt;
1133 g_dynblockSMT.setState(smt);
1134 });
1135
1136 g_lua.writeFunction("addDynBlocks",
f8327125 1137 [](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) {
6ddb008a
RG
1138 if (m.empty()) {
1139 return;
1140 }
6bb38cd6
RG
1141 setLuaSideEffect();
1142 auto slow = g_dynblockNMG.getCopy();
1143 struct timespec until, now;
1144 gettime(&now);
1145 until=now;
1146 int actualSeconds = seconds ? *seconds : 10;
1147 until.tv_sec += actualSeconds;
1148 for(const auto& capair : m) {
1149 unsigned int count = 0;
1150 auto got = slow.lookup(Netmask(capair.first));
1151 bool expired=false;
1152 if(got) {
1153 if(until < got->second.until) // had a longer policy
1154 continue;
1155 if(now < got->second.until) // only inherit count on fresh query we are extending
1156 count=got->second.blocks;
1157 else
1158 expired=true;
1159 }
1160 DynBlock db{msg,until,DNSName(),(action ? *action : DNSAction::Action::None)};
1161 db.blocks=count;
1162 if(!got || expired)
1163 warnlog("Inserting dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg);
1164 slow.insert(Netmask(capair.first)).second=db;
1165 }
1166 g_dynblockNMG.setState(slow);
1167 });
1168
1169 g_lua.writeFunction("addDynBlockSMT",
1170 [](const vector<pair<unsigned int, string> >&names, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
6ddb008a
RG
1171 if (names.empty()) {
1172 return;
1173 }
6bb38cd6
RG
1174 setLuaSideEffect();
1175 auto slow = g_dynblockSMT.getCopy();
1176 struct timespec until, now;
1177 gettime(&now);
1178 until=now;
1179 int actualSeconds = seconds ? *seconds : 10;
1180 until.tv_sec += actualSeconds;
1181
1182 for(const auto& capair : names) {
1183 unsigned int count = 0;
1184 DNSName domain(capair.second);
86df840d 1185 domain.makeUsLowerCase();
6bb38cd6
RG
1186 auto got = slow.lookup(domain);
1187 bool expired=false;
1188 if(got) {
1189 if(until < got->until) // had a longer policy
1190 continue;
1191 if(now < got->until) // only inherit count on fresh query we are extending
1192 count=got->blocks;
1193 else
1194 expired=true;
1195 }
1196
1197 DynBlock db{msg,until,domain,(action ? *action : DNSAction::Action::None)};
1198 db.blocks=count;
1199 if(!got || expired)
1200 warnlog("Inserting dynamic block for %s for %d seconds: %s", domain, actualSeconds, msg);
1201 slow.add(domain, db);
1202 }
1203 g_dynblockSMT.setState(slow);
1204 });
1205
1206 g_lua.writeFunction("setDynBlocksAction", [](DNSAction::Action action) {
1207 if (!g_configurationDone) {
3d60b39a 1208 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) {
6bb38cd6
RG
1209 g_dynBlockAction = action;
1210 }
1211 else {
3d60b39a 1212 errlog("Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!");
1213 g_outputBuffer="Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!\n";
6bb38cd6
RG
1214 }
1215 } else {
1216 g_outputBuffer="Dynamic blocks action cannot be altered at runtime!\n";
1217 }
df111b53 1218 });
832c1792 1219
37b6d73d 1220 g_lua.writeFunction("addDNSCryptBind", [](const std::string& addr, const std::string& providerName, boost::variant<std::string, std::vector<std::pair<int, std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int, std::string>>> keyFiles, boost::optional<localbind_t> vars) {
6bb38cd6
RG
1221 if (g_configurationDone) {
1222 g_outputBuffer="addDNSCryptBind cannot be used at runtime!\n";
1223 return;
1224 }
1225#ifdef HAVE_DNSCRYPT
6bb38cd6
RG
1226 bool reusePort = false;
1227 int tcpFastOpenQueueSize = 0;
c58723d7 1228 int tcpListenQueueSize = 0;
6bb38cd6
RG
1229 std::string interface;
1230 std::set<int> cpus;
37b6d73d 1231 std::vector<DNSCryptContext::CertKeyPaths> certKeys;
df111b53 1232
c58723d7 1233 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize);
df111b53 1234
37b6d73d
RG
1235 if (certFiles.type() == typeid(std::string) && keyFiles.type() == typeid(std::string)) {
1236 auto certFile = boost::get<std::string>(certFiles);
1237 auto keyFile = boost::get<std::string>(keyFiles);
1238 certKeys.push_back({certFile, keyFile});
1239 }
1240 else if (certFiles.type() == typeid(std::vector<std::pair<int,std::string>>) && keyFiles.type() == typeid(std::vector<std::pair<int,std::string>>)) {
1241 auto certFilesVect = boost::get<std::vector<std::pair<int,std::string>>>(certFiles);
1242 auto keyFilesVect = boost::get<std::vector<std::pair<int,std::string>>>(keyFiles);
1243 if (certFilesVect.size() == keyFilesVect.size()) {
1244 for (size_t idx = 0; idx < certFilesVect.size(); idx++) {
1245 certKeys.push_back({certFilesVect.at(idx).second, keyFilesVect.at(idx).second});
1246 }
1247 }
1248 else {
1249 errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind!");
1250 g_outputBuffer="Error, mismatching number of certificates and keys in call to addDNSCryptBind()!";
1251 return;
1252 }
1253 }
1254 else {
1255 errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind()!");
1256 g_outputBuffer="Error, mismatching number of certificates and keys in call to addDNSCryptBind()!";
1257 return;
1258 }
1259
6bb38cd6 1260 try {
37b6d73d 1261 auto ctx = std::make_shared<DNSCryptContext>(providerName, certKeys);
6e9fd124
RG
1262
1263 /* UDP */
1264 auto cs = std::unique_ptr<ClientState>(new ClientState(ComboAddress(addr, 443), false, reusePort, tcpFastOpenQueueSize, interface, cpus));
1265 cs->dnscryptCtx = ctx;
1266 g_dnsCryptLocals.push_back(ctx);
1267 g_frontends.push_back(std::move(cs));
1268
1269 /* TCP */
1270 cs = std::unique_ptr<ClientState>(new ClientState(ComboAddress(addr, 443), true, reusePort, tcpFastOpenQueueSize, interface, cpus));
1271 cs->dnscryptCtx = ctx;
c58723d7
RG
1272 if (tcpListenQueueSize > 0) {
1273 cs->tcpListenQueueSize = tcpListenQueueSize;
1274 }
1275
6e9fd124 1276 g_frontends.push_back(std::move(cs));
df111b53 1277 }
6bb38cd6
RG
1278 catch(std::exception& e) {
1279 errlog(e.what());
1280 g_outputBuffer="Error: "+string(e.what())+"\n";
1281 }
1282#else
1283 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1284#endif
4d5959e6 1285
df111b53 1286 });
1287
6bb38cd6 1288 g_lua.writeFunction("showDNSCryptBinds", []() {
f758857a 1289 setLuaNoSideEffect();
6bb38cd6
RG
1290#ifdef HAVE_DNSCRYPT
1291 ostringstream ret;
43234e76
RG
1292 boost::format fmt("%1$-3d %2% %|25t|%3$-20.20s");
1293 ret << (fmt % "#" % "Address" % "Provider Name") << endl;
6bb38cd6
RG
1294 size_t idx = 0;
1295
26438bc9 1296 std::unordered_set<std::shared_ptr<DNSCryptContext>> contexts;
6e9fd124
RG
1297 for (const auto& frontend : g_frontends) {
1298 const std::shared_ptr<DNSCryptContext> ctx = frontend->dnscryptCtx;
26438bc9 1299 if (!ctx || contexts.count(ctx) != 0) {
a6544e62
RG
1300 continue;
1301 }
26438bc9 1302 contexts.insert(ctx);
6e9fd124 1303 ret<< (fmt % idx % frontend->local.toStringWithPort() % ctx->getProviderName()) << endl;
6bb38cd6 1304 idx++;
df111b53 1305 }
df111b53 1306
6bb38cd6
RG
1307 g_outputBuffer=ret.str();
1308#else
1309 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1310#endif
886e2cf2 1311 });
df111b53 1312
43234e76 1313 g_lua.writeFunction("getDNSCryptBind", [](size_t idx) {
6bb38cd6
RG
1314 setLuaNoSideEffect();
1315#ifdef HAVE_DNSCRYPT
43234e76 1316 std::shared_ptr<DNSCryptContext> ret = nullptr;
6bb38cd6 1317 if (idx < g_dnsCryptLocals.size()) {
6e9fd124 1318 ret = g_dnsCryptLocals.at(idx);
d92708ed 1319 }
6bb38cd6
RG
1320 return ret;
1321#else
1322 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1323#endif
d92708ed
RG
1324 });
1325
265260f5
RG
1326 g_lua.writeFunction("getDNSCryptBindCount", []() {
1327 setLuaNoSideEffect();
1328 return g_dnsCryptLocals.size();
1329 });
1330
203b5348 1331 g_lua.writeFunction("generateDNSCryptProviderKeys", [client](const std::string& publicKeyFile, const std::string privateKeyFile) {
6bb38cd6
RG
1332 setLuaNoSideEffect();
1333#ifdef HAVE_DNSCRYPT
203b5348
RG
1334 if (client) {
1335 return;
1336 }
6bb38cd6
RG
1337 unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
1338 unsigned char privateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
1339 sodium_mlock(privateKey, sizeof(privateKey));
4c6f4321 1340
6bb38cd6 1341 try {
43234e76 1342 DNSCryptContext::generateProviderKeys(publicKey, privateKey);
4c6f4321 1343
6bb38cd6
RG
1344 ofstream pubKStream(publicKeyFile);
1345 pubKStream.write((char*) publicKey, sizeof(publicKey));
1346 pubKStream.close();
df111b53 1347
6bb38cd6
RG
1348 ofstream privKStream(privateKeyFile);
1349 privKStream.write((char*) privateKey, sizeof(privateKey));
1350 privKStream.close();
df111b53 1351
43234e76 1352 g_outputBuffer="Provider fingerprint is: " + DNSCryptContext::getProviderFingerprint(publicKey) + "\n";
6bb38cd6
RG
1353 }
1354 catch(std::exception& e) {
1355 errlog(e.what());
1356 g_outputBuffer="Error: "+string(e.what())+"\n";
1357 }
df111b53 1358
6bb38cd6
RG
1359 sodium_memzero(privateKey, sizeof(privateKey));
1360 sodium_munlock(privateKey, sizeof(privateKey));
1361#else
1362 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1363#endif
aeb36780
RG
1364 });
1365
6bb38cd6
RG
1366 g_lua.writeFunction("printDNSCryptProviderFingerprint", [](const std::string& publicKeyFile) {
1367 setLuaNoSideEffect();
1368#ifdef HAVE_DNSCRYPT
1369 unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
aeb36780 1370
6bb38cd6
RG
1371 try {
1372 ifstream file(publicKeyFile);
1373 file.read((char *) &publicKey, sizeof(publicKey));
df111b53 1374
6bb38cd6
RG
1375 if (file.fail())
1376 throw std::runtime_error("Invalid dnscrypt provider public key file " + publicKeyFile);
df111b53 1377
6bb38cd6 1378 file.close();
43234e76 1379 g_outputBuffer="Provider fingerprint is: " + DNSCryptContext::getProviderFingerprint(publicKey) + "\n";
6bb38cd6
RG
1380 }
1381 catch(std::exception& e) {
1382 errlog(e.what());
1383 g_outputBuffer="Error: "+string(e.what())+"\n";
1384 }
1385#else
1386 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1387#endif
df111b53 1388 });
1389
501983cd 1390#ifdef HAVE_DNSCRYPT
203b5348 1391 g_lua.writeFunction("generateDNSCryptCertificate", [client](const std::string& providerPrivateKeyFile, const std::string& certificateFile, const std::string privateKeyFile, uint32_t serial, time_t begin, time_t end, boost::optional<DNSCryptExchangeVersion> version) {
6bb38cd6 1392 setLuaNoSideEffect();
203b5348
RG
1393 if (client) {
1394 return;
1395 }
43234e76
RG
1396 DNSCryptPrivateKey privateKey;
1397 DNSCryptCert cert;
42fae326 1398
50bed881 1399 try {
43234e76 1400 if (generateDNSCryptCertificate(providerPrivateKeyFile, serial, begin, end, version ? *version : DNSCryptExchangeVersion::VERSION1, cert, privateKey)) {
6bb38cd6 1401 privateKey.saveToFile(privateKeyFile);
43234e76 1402 DNSCryptContext::saveCertFromFile(cert, certificateFile);
6bb38cd6 1403 }
50bed881 1404 }
6bb38cd6
RG
1405 catch(const std::exception& e) {
1406 errlog(e.what());
1407 g_outputBuffer="Error: "+string(e.what())+"\n";
50bed881 1408 }
50bed881 1409 });
501983cd 1410#endif
df111b53 1411
6bb38cd6
RG
1412 g_lua.writeFunction("showPools", []() {
1413 setLuaNoSideEffect();
df111b53 1414 try {
6bb38cd6
RG
1415 ostringstream ret;
1416 boost::format fmt("%1$-20.20s %|25t|%2$20s %|25t|%3$20s %|50t|%4%" );
1417 // 1 2 3 4
1418 ret << (fmt % "Name" % "Cache" % "ServerPolicy" % "Servers" ) << endl;
1419
1420 const auto localPools = g_pools.getCopy();
1421 for (const auto& entry : localPools) {
1422 const string& name = entry.first;
1423 const std::shared_ptr<ServerPool> pool = entry.second;
1424 string cache = pool->packetCache != nullptr ? pool->packetCache->toString() : "";
1425 string policy = g_policy.getLocal()->name;
1426 if (pool->policy != nullptr) {
1427 policy = pool->policy->name;
1428 }
1429 string servers;
1430
a1b1a29d 1431 for (const auto& server: pool->getServers()) {
6bb38cd6
RG
1432 if (!servers.empty()) {
1433 servers += ", ";
1434 }
be05aa91
RG
1435 if (!server.second->getName().empty()) {
1436 servers += server.second->getName();
6bb38cd6
RG
1437 servers += " ";
1438 }
1439 servers += server.second->remote.toStringWithPort();
1440 }
1441
1442 ret << (fmt % name % cache % policy % servers) << endl;
1443 }
1444 g_outputBuffer=ret.str();
1445 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
1446 });
1447
1448 g_lua.writeFunction("getPool", [client](const string& poolName) {
1449 if (client) {
1450 return std::make_shared<ServerPool>();
df111b53 1451 }
6bb38cd6
RG
1452 auto localPools = g_pools.getCopy();
1453 std::shared_ptr<ServerPool> pool = createPoolIfNotExists(localPools, poolName);
1454 g_pools.setState(localPools);
1455 return pool;
df111b53 1456 });
1457
6bb38cd6
RG
1458 g_lua.writeFunction("setVerboseHealthChecks", [](bool verbose) { g_verboseHealthChecks=verbose; });
1459 g_lua.writeFunction("setStaleCacheEntriesTTL", [](uint32_t ttl) { g_staleCacheEntriesTTL = ttl; });
03ebf8b2 1460
6bb38cd6 1461 g_lua.writeFunction("showBinds", []() {
f758857a 1462 setLuaNoSideEffect();
6bb38cd6
RG
1463 try {
1464 ostringstream ret;
ba7ec340 1465 boost::format fmt("%1$-3d %2$-20.20s %|35t|%3$-20.20s %|57t|%4%" );
6bb38cd6
RG
1466 // 1 2 3 4
1467 ret << (fmt % "#" % "Address" % "Protocol" % "Queries" ) << endl;
1468
1469 size_t counter = 0;
1470 for (const auto& front : g_frontends) {
ba7ec340 1471 ret << (fmt % counter % front->local.toStringWithPort() % front->getType() % front->queries) << endl;
6bb38cd6 1472 counter++;
0e41337b 1473 }
6bb38cd6
RG
1474 g_outputBuffer=ret.str();
1475 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
0e5b3cff 1476 });
1477
6bb38cd6 1478 g_lua.writeFunction("getBind", [](size_t num) {
f758857a 1479 setLuaNoSideEffect();
6bb38cd6
RG
1480 ClientState* ret = nullptr;
1481 if(num < g_frontends.size()) {
6e9fd124 1482 ret=g_frontends[num].get();
df111b53 1483 }
df111b53 1484 return ret;
6bb38cd6 1485 });
df111b53 1486
265260f5
RG
1487 g_lua.writeFunction("getBindCount", []() {
1488 setLuaNoSideEffect();
1489 return g_frontends.size();
1490 });
1491
6bb38cd6
RG
1492 g_lua.writeFunction("help", [](boost::optional<std::string> command) {
1493 setLuaNoSideEffect();
1494 g_outputBuffer = "";
1495 for (const auto& keyword : g_consoleKeywords) {
1496 if (!command) {
1497 g_outputBuffer += keyword.toString() + "\n";
1498 }
1499 else if (keyword.name == command) {
1500 g_outputBuffer = keyword.toString() + "\n";
1501 return;
1502 }
1503 }
1504 if (command) {
1505 g_outputBuffer = "Nothing found for " + *command + "\n";
786e4d8c 1506 }
786e4d8c 1507 });
520eb5a0 1508
6bb38cd6 1509 g_lua.writeFunction("showVersion", []() {
786e4d8c 1510 setLuaNoSideEffect();
6bb38cd6 1511 g_outputBuffer = "dnsdist " + std::string(VERSION) + "\n";
786e4d8c
RS
1512 });
1513
eb0ca460
PL
1514 g_lua.writeFunction("showSecurityStatus", []() {
1515 setLuaNoSideEffect();
1516 g_outputBuffer = std::to_string(g_stats.securityStatus) + "\n";
1517 });
1518
6bb38cd6
RG
1519#ifdef HAVE_EBPF
1520 g_lua.writeFunction("setDefaultBPFFilter", [](std::shared_ptr<BPFFilter> bpf) {
1521 if (g_configurationDone) {
1522 g_outputBuffer="setDefaultBPFFilter() cannot be used at runtime!\n";
1523 return;
1524 }
1525 g_defaultBPFFilter = bpf;
786e4d8c 1526 });
62edea30 1527
6bb38cd6
RG
1528 g_lua.writeFunction("registerDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
1529 if (dbpf) {
1530 g_dynBPFFilters.push_back(dbpf);
520eb5a0 1531 }
520eb5a0 1532 });
f5b58807 1533
6bb38cd6
RG
1534 g_lua.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
1535 if (dbpf) {
1536 for (auto it = g_dynBPFFilters.begin(); it != g_dynBPFFilters.end(); it++) {
1537 if (*it == dbpf) {
1538 g_dynBPFFilters.erase(it);
1539 break;
1540 }
1541 }
1542 }
2d11d1b2 1543 });
df111b53 1544
b6ba8123 1545 g_lua.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) {
6bb38cd6
RG
1546 setLuaSideEffect();
1547 struct timespec until, now;
1548 clock_gettime(CLOCK_MONOTONIC, &now);
1549 until=now;
1550 int actualSeconds = seconds ? *seconds : 10;
1551 until.tv_sec += actualSeconds;
1552 for(const auto& capair : m) {
4900be3f
RG
1553 if (dynbpf->block(capair.first, until)) {
1554 warnlog("Inserting eBPF dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg ? *msg : "");
1555 }
6bb38cd6 1556 }
df111b53 1557 });
62edea30 1558
6bb38cd6 1559#endif /* HAVE_EBPF */
2d11d1b2 1560
6bb38cd6 1561 g_lua.writeFunction<std::unordered_map<string,uint64_t>()>("getStatisticsCounters", []() {
f758857a 1562 setLuaNoSideEffect();
6bb38cd6
RG
1563 std::unordered_map<string,uint64_t> res;
1564 for(const auto& entry : g_stats.entries) {
1565 if(const auto& val = boost::get<DNSDistStats::stat_t*>(&entry.second))
1566 res[entry.first] = (*val)->load();
df111b53 1567 }
6bb38cd6
RG
1568 return res;
1569 });
df111b53 1570
6bb38cd6
RG
1571 g_lua.writeFunction("includeDirectory", [](const std::string& dirname) {
1572 if (g_configurationDone) {
1573 errlog("includeDirectory() cannot be used at runtime!");
1574 g_outputBuffer="includeDirectory() cannot be used at runtime!\n";
1575 return;
df111b53 1576 }
1577
6bb38cd6
RG
1578 if (g_included) {
1579 errlog("includeDirectory() cannot be used recursively!");
1580 g_outputBuffer="includeDirectory() cannot be used recursively!\n";
e12b3374
RG
1581 return;
1582 }
1583
6bb38cd6
RG
1584 g_included = true;
1585 struct stat st;
1586 if (stat(dirname.c_str(), &st)) {
1587 errlog("The included directory %s does not exist!", dirname.c_str());
1588 g_outputBuffer="The included directory " + dirname + " does not exist!";
1589 return;
df111b53 1590 }
df111b53 1591
6bb38cd6
RG
1592 if (!S_ISDIR(st.st_mode)) {
1593 errlog("The included directory %s is not a directory!", dirname.c_str());
1594 g_outputBuffer="The included directory " + dirname + " is not a directory!";
1595 return;
6f6b4d69 1596 }
1597
6bb38cd6
RG
1598 DIR *dirp;
1599 struct dirent *ent;
1600 std::list<std::string> files;
1601 if (!(dirp = opendir(dirname.c_str()))) {
1602 errlog("Error opening the included directory %s!", dirname.c_str());
1603 g_outputBuffer="Error opening the included directory " + dirname + "!";
1604 return;
6f6b4d69 1605 }
df111b53 1606
6bb38cd6
RG
1607 while((ent = readdir(dirp)) != NULL) {
1608 if (ent->d_name[0] == '.') {
1609 continue;
1610 }
3f6d07a4 1611
6bb38cd6
RG
1612 if (boost::ends_with(ent->d_name, ".conf")) {
1613 std::ostringstream namebuf;
1614 namebuf << dirname.c_str() << "/" << ent->d_name;
2a817e5a 1615
6bb38cd6
RG
1616 if (stat(namebuf.str().c_str(), &st) || !S_ISREG(st.st_mode)) {
1617 continue;
1618 }
e0b5e49d 1619
6bb38cd6
RG
1620 files.push_back(namebuf.str());
1621 }
e41f8165 1622 }
e41f8165 1623
6bb38cd6
RG
1624 closedir(dirp);
1625 files.sort();
1626
1627 for (auto file = files.begin(); file != files.end(); ++file) {
1628 std::ifstream ifs(*file);
1629 if (!ifs) {
1630 warnlog("Unable to read configuration from '%s'", *file);
1631 } else {
1632 vinfolog("Read configuration from '%s'", *file);
1633 }
26a6373d 1634
6bb38cd6 1635 g_lua.executeCode(ifs);
741ebe08 1636 }
26a6373d 1637
6bb38cd6 1638 g_included = false;
26a6373d
SO
1639 });
1640
6bb38cd6
RG
1641 g_lua.writeFunction("setAPIWritable", [](bool writable, boost::optional<std::string> apiConfigDir) {
1642 setLuaSideEffect();
1643 g_apiReadWrite = writable;
1644 if (apiConfigDir) {
1645 if (!(*apiConfigDir).empty()) {
1646 g_apiConfigDirectory = *apiConfigDir;
1647 }
1648 else {
1649 errlog("The API configuration directory value cannot be empty!");
1650 g_outputBuffer="The API configuration directory value cannot be empty!";
1651 }
741ebe08 1652 }
5b8255ba 1653 });
26a6373d 1654
6bb38cd6
RG
1655 g_lua.writeFunction("setServFailWhenNoServer", [](bool servfail) {
1656 setLuaSideEffect();
1657 g_servFailOnNoPolicy = servfail;
1658 });
26a6373d 1659
32b86928
RG
1660 g_lua.writeFunction("setRoundRobinFailOnNoServer", [](bool fail) {
1661 setLuaSideEffect();
1662 g_roundrobinFailOnNoServer = fail;
1663 });
1664
2b4287d4
RG
1665 g_lua.writeFunction("setConsistentHashingBalancingFactor", [](double factor) {
1666 setLuaSideEffect();
088c18d0 1667 if (factor >= 1.0) {
80ee5144
RG
1668 g_consistentHashBalancingFactor = factor;
1669 }
1670 else {
1671 errlog("Invalid value passed to setConsistentHashingBalancingFactor()!");
1672 g_outputBuffer="Invalid value passed to setConsistentHashingBalancingFactor()!\n";
1673 return;
1674 }
2b4287d4
RG
1675 });
1676
439de524
RG
1677 g_lua.writeFunction("setWeightedBalancingFactor", [](double factor) {
1678 setLuaSideEffect();
088c18d0 1679 if (factor >= 1.0) {
439de524
RG
1680 g_weightedBalancingFactor = factor;
1681 }
1682 else {
1683 errlog("Invalid value passed to setWeightedBalancingFactor()!");
1684 g_outputBuffer="Invalid value passed to setWeightedBalancingFactor()!\n";
1685 return;
1686 }
1687 });
1688
01f6920b 1689 g_lua.writeFunction("setRingBuffersSize", [](size_t capacity, boost::optional<size_t> numberOfShards) {
6bb38cd6
RG
1690 setLuaSideEffect();
1691 if (g_configurationDone) {
1692 errlog("setRingBuffersSize() cannot be used at runtime!");
1693 g_outputBuffer="setRingBuffersSize() cannot be used at runtime!\n";
1694 return;
5b8255ba 1695 }
01f6920b 1696 g_rings.setCapacity(capacity, numberOfShards ? *numberOfShards : 1);
5b8255ba 1697 });
26a6373d 1698
6d31c8b6
RG
1699 g_lua.writeFunction("setRingBuffersLockRetries", [](size_t retries) {
1700 setLuaSideEffect();
1701 g_rings.setNumberOfLockRetries(retries);
5b8255ba 1702 });
26a6373d 1703
6bb38cd6
RG
1704 g_lua.writeFunction("setWHashedPertubation", [](uint32_t pertub) {
1705 setLuaSideEffect();
1706 g_hashperturb = pertub;
1707 });
26a6373d 1708
6bb38cd6
RG
1709 g_lua.writeFunction("setTCPUseSinglePipe", [](bool flag) {
1710 if (g_configurationDone) {
1711 g_outputBuffer="setTCPUseSinglePipe() cannot be used at runtime!\n";
1712 return;
5b8255ba 1713 }
6bb38cd6
RG
1714 setLuaSideEffect();
1715 g_useTCPSinglePipe = flag;
5b8255ba 1716 });
26a6373d 1717
203b5348
RG
1718 g_lua.writeFunction("snmpAgent", [client,configCheck](bool enableTraps, boost::optional<std::string> masterSocket) {
1719 if(client || configCheck)
cd4bb56b 1720 return;
9f4eb5cc 1721#ifdef HAVE_NET_SNMP
6bb38cd6
RG
1722 if (g_configurationDone) {
1723 errlog("snmpAgent() cannot be used at runtime!");
1724 g_outputBuffer="snmpAgent() cannot be used at runtime!\n";
1725 return;
1726 }
1727
1728 if (g_snmpEnabled) {
1729 errlog("snmpAgent() cannot be used twice!");
1730 g_outputBuffer="snmpAgent() cannot be used twice!\n";
1731 return;
9f4eb5cc 1732 }
6bb38cd6
RG
1733
1734 g_snmpEnabled = true;
1735 g_snmpTrapsEnabled = enableTraps;
1736 g_snmpAgent = new DNSDistSNMPAgent("dnsdist", masterSocket ? *masterSocket : std::string());
1737#else
1738 errlog("NET SNMP support is required to use snmpAgent()");
1739 g_outputBuffer="NET SNMP support is required to use snmpAgent()\n";
9f4eb5cc
RG
1740#endif /* HAVE_NET_SNMP */
1741 });
497a6e3a 1742
6bb38cd6 1743 g_lua.writeFunction("sendCustomTrap", [](const std::string& str) {
9f4eb5cc 1744 if (g_snmpAgent && g_snmpTrapsEnabled) {
6bb38cd6 1745 g_snmpAgent->sendCustomTrap(str);
9f4eb5cc 1746 }
9f4eb5cc 1747 });
6beb5731 1748
be05aa91
RG
1749 g_lua.writeFunction("setServerPolicy", [](ServerPolicy policy) {
1750 setLuaSideEffect();
1751 g_policy.setState(policy);
1752 });
1753
1754 g_lua.writeFunction("setServerPolicyLua", [](string name, ServerPolicy::policyfunc_t policy) {
1755 setLuaSideEffect();
1756 g_policy.setState(ServerPolicy{name, policy, true});
1757 });
1758
1759 g_lua.writeFunction("setServerPolicyLuaFFI", [](string name, ServerPolicy::ffipolicyfunc_t policy) {
1760 setLuaSideEffect();
1761 auto pol = ServerPolicy(name, policy);
1762 g_policy.setState(std::move(pol));
1763 });
1764
1765 g_lua.writeFunction("showServerPolicy", []() {
1766 setLuaSideEffect();
1767 g_outputBuffer=g_policy.getLocal()->name+"\n";
1768 });
1769
6bb38cd6
RG
1770 g_lua.writeFunction("setPoolServerPolicy", [](ServerPolicy policy, string pool) {
1771 setLuaSideEffect();
1772 auto localPools = g_pools.getCopy();
1773 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(policy));
1774 g_pools.setState(localPools);
6c1ca990 1775 });
e41f8165 1776
be05aa91 1777 g_lua.writeFunction("setPoolServerPolicyLua", [](string name, ServerPolicy::policyfunc_t policy, string pool) {
6bb38cd6
RG
1778 setLuaSideEffect();
1779 auto localPools = g_pools.getCopy();
a1b1a29d 1780 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, policy, true}));
6bb38cd6 1781 g_pools.setState(localPools);
9396d955
RG
1782 });
1783
6bb38cd6
RG
1784 g_lua.writeFunction("showPoolServerPolicy", [](string pool) {
1785 setLuaSideEffect();
1786 auto localPools = g_pools.getCopy();
1787 auto poolObj = getPool(localPools, pool);
1788 if (poolObj->policy == nullptr) {
1789 g_outputBuffer=g_policy.getLocal()->name+"\n";
9396d955 1790 } else {
6bb38cd6 1791 g_outputBuffer=poolObj->policy->name+"\n";
9396d955
RG
1792 }
1793 });
1794
6bb38cd6
RG
1795 g_lua.writeFunction("setTCPDownstreamCleanupInterval", [](uint16_t interval) {
1796 setLuaSideEffect();
1797 g_downstreamTCPCleanupInterval = interval;
9396d955
RG
1798 });
1799
6bb38cd6
RG
1800 g_lua.writeFunction("setConsoleConnectionsLogging", [](bool enabled) {
1801 g_logConsoleConnections = enabled;
e65ae260
RG
1802 });
1803
03c05963
CHB
1804 g_lua.writeFunction("setConsoleOutputMaxMsgSize", [](uint32_t size) {
1805 g_consoleOutputMsgMaxSize = size;
1806 });
1807
6bb38cd6
RG
1808 g_lua.writeFunction("setUDPMultipleMessagesVectorSize", [](size_t vSize) {
1809 if (g_configurationDone) {
1810 errlog("setUDPMultipleMessagesVectorSize() cannot be used at runtime!");
1811 g_outputBuffer="setUDPMultipleMessagesVectorSize() cannot be used at runtime!\n";
1812 return;
2a817e5a 1813 }
6bb38cd6
RG
1814#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
1815 setLuaSideEffect();
1816 g_udpVectorSize = vSize;
1817#else
1818 errlog("recvmmsg() support is not available!");
1819 g_outputBuffer="recvmmsg support is not available!\n";
1820#endif
1821 });
a227f47d 1822
e7c732b8
RG
1823 g_lua.writeFunction("setAddEDNSToSelfGeneratedResponses", [](bool add) {
1824 g_addEDNSToSelfGeneratedResponses = add;
1825 });
1826
1827 g_lua.writeFunction("setPayloadSizeOnSelfGeneratedAnswers", [](uint16_t payloadSize) {
1828 if (payloadSize < 512) {
1829 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!");
1830 g_outputBuffer="setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!";
1831 payloadSize = 512;
1832 }
1833 if (payloadSize > s_udpIncomingBufferSize) {
1834 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to %d instead!", s_udpIncomingBufferSize);
1835 g_outputBuffer="setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to " + std::to_string(s_udpIncomingBufferSize) + " instead";
1836 payloadSize = s_udpIncomingBufferSize;
1837 }
1838 g_PayloadSizeSelfGenAnswers = payloadSize;
1839 });
1840
5d4e1ef8
RG
1841 g_lua.writeFunction("setSecurityPollSuffix", [](const std::string& suffix) {
1842 if (g_configurationDone) {
1843 g_outputBuffer="setSecurityPollSuffix() cannot be used at runtime!\n";
1844 return;
1845 }
1846
1847 g_secPollSuffix = suffix;
1848 });
1849
1850 g_lua.writeFunction("setSecurityPollInterval", [](time_t newInterval) {
1851 if (newInterval <= 0) {
1852 warnlog("setSecurityPollInterval() should be > 0, skipping");
1853 g_outputBuffer="setSecurityPollInterval() should be > 0, skipping";
1854 }
1855
1856 g_secPollInterval = newInterval;
1857 });
1858
0ca6a67f
RG
1859 g_lua.writeFunction("setSyslogFacility", [](int facility) {
1860 setLuaSideEffect();
1861 if (g_configurationDone) {
1862 g_outputBuffer="setSyslogFacility cannot be used at runtime!\n";
1863 return;
1864 }
1865 setSyslogFacility(facility);
1866 });
1867
44947230 1868 g_lua.writeFunction("addDOHLocal", [client](const std::string& addr, boost::optional<boost::variant<std::string, std::vector<std::pair<int,std::string>>>> certFiles, boost::optional<boost::variant<std::string, std::vector<std::pair<int,std::string>>>> keyFiles, boost::optional<boost::variant<std::string, vector<pair<int, std::string> > > > urls, boost::optional<localbind_t> vars) {
fbf14b03
RG
1869 if (client) {
1870 return;
1871 }
b35ad620 1872#ifdef HAVE_DNS_OVER_HTTPS
fbf14b03
RG
1873 setLuaSideEffect();
1874 if (g_configurationDone) {
1875 g_outputBuffer="addDOHLocal cannot be used at runtime!\n";
1876 return;
1877 }
1878 auto frontend = std::make_shared<DOHFrontend>();
bf8cd40d 1879
44947230 1880 if (certFiles && !certFiles->empty() && keyFiles && !keyFiles->empty()) {
b54e94dc 1881 if (!loadTLSCertificateAndKeys("addDOHLocal", frontend->d_tlsConfig.d_certKeyPairs, *certFiles, *keyFiles)) {
44947230
RG
1882 return;
1883 }
1884
1885 frontend->d_local = ComboAddress(addr, 443);
1886 }
1887 else {
1888 frontend->d_local = ComboAddress(addr, 80);
1889 infolog("No certificate provided for DoH endpoint %s, running in DNS over HTTP mode instead of DNS over HTTPS", frontend->d_local.toStringWithPort());
bf8cd40d
RG
1890 }
1891
0718e562
RG
1892 if (urls) {
1893 if (urls->type() == typeid(std::string)) {
1894 frontend->d_urls.push_back(boost::get<std::string>(*urls));
1895 }
1896 else if (urls->type() == typeid(std::vector<std::pair<int,std::string>>)) {
1897 auto urlsVect = boost::get<std::vector<std::pair<int,std::string>>>(*urls);
1898 for(const auto& p : urlsVect) {
1899 frontend->d_urls.push_back(p.second);
1900 }
fbf14b03
RG
1901 }
1902 }
1903 else {
a6f94324 1904 frontend->d_urls = {"/dns-query"};
fbf14b03
RG
1905 }
1906
2b0cb8f8
RG
1907 bool reusePort = false;
1908 int tcpFastOpenQueueSize = 0;
c58723d7 1909 int tcpListenQueueSize = 0;
2b0cb8f8
RG
1910 std::string interface;
1911 std::set<int> cpus;
2b0cb8f8 1912
c58723d7
RG
1913 if (vars) {
1914 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize);
2b0cb8f8 1915
fbf14b03
RG
1916 if (vars->count("idleTimeout")) {
1917 frontend->d_idleTimeout = boost::get<int>((*vars)["idleTimeout"]);
1918 }
b54e94dc 1919
3d0371b3
RG
1920 if (vars->count("serverTokens")) {
1921 frontend->d_serverTokens = boost::get<const string>((*vars)["serverTokens"]);
1922 }
b54e94dc 1923
ee01507f
CR
1924 if (vars->count("customResponseHeaders")) {
1925 for (auto const& headerMap : boost::get<std::map<std::string,std::string>>((*vars)["customResponseHeaders"])) {
cf3e149b 1926 std::pair<std::string,std::string> headerResponse = std::make_pair(boost::to_lower_copy(headerMap.first), headerMap.second);
ee01507f
CR
1927 frontend->d_customResponseHeaders.push_back(headerResponse);
1928 }
1929 }
0ef9ab19 1930
0026abf9
RG
1931 if (vars->count("sendCacheControlHeaders")) {
1932 frontend->d_sendCacheControlHeaders = boost::get<bool>((*vars)["sendCacheControlHeaders"]);
1933 }
1934
8b5f4644
RG
1935 if (vars->count("trustForwardedForHeader")) {
1936 frontend->d_trustForwardedForHeader = boost::get<bool>((*vars)["trustForwardedForHeader"]);
1937 }
1938
b54e94dc 1939 parseTLSConfig(frontend->d_tlsConfig, "addDOHLocal", vars);
fbf14b03
RG
1940 }
1941 g_dohlocals.push_back(frontend);
2b0cb8f8 1942 auto cs = std::unique_ptr<ClientState>(new ClientState(frontend->d_local, true, reusePort, tcpFastOpenQueueSize, interface, cpus));
fbf14b03 1943 cs->dohFrontend = frontend;
c58723d7
RG
1944 if (tcpListenQueueSize > 0) {
1945 cs->tcpListenQueueSize = tcpListenQueueSize;
1946 }
1947
fbf14b03
RG
1948 g_frontends.push_back(std::move(cs));
1949#else
56d44226 1950 throw std::runtime_error("addDOHLocal() called but DNS over HTTPS support is not present!");
fbf14b03
RG
1951#endif
1952 });
1953
1b51c0f3
RG
1954 g_lua.writeFunction("showDOHFrontends", []() {
1955#ifdef HAVE_DNS_OVER_HTTPS
1956 setLuaNoSideEffect();
1957 try {
1958 ostringstream ret;
b608e6c6
RG
1959 boost::format fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d");
1960 ret << (fmt % "#" % "Address" % "HTTP" % "HTTP/1" % "HTTP/2" % "GET" % "POST" % "Bad" % "Errors" % "Redirects" % "Valid" % "# ticket keys" % "Rotation delay" % "Next rotation") << endl;
1b51c0f3
RG
1961 size_t counter = 0;
1962 for (const auto& ctx : g_dohlocals) {
39c323e5 1963 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;
1b51c0f3
RG
1964 counter++;
1965 }
1966 g_outputBuffer = ret.str();
1967 }
1968 catch(const std::exception& e) {
1969 g_outputBuffer = e.what();
1970 throw;
1971 }
1972#else
1973 g_outputBuffer="DNS over HTTPS support is not present!\n";
1974#endif
1975 });
1976
5bbcbea0
RG
1977 g_lua.writeFunction("showDOHResponseCodes", []() {
1978#ifdef HAVE_DNS_OVER_HTTPS
1979 setLuaNoSideEffect();
1980 try {
1981 ostringstream ret;
1982 boost::format fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d %-15d %-15d");
1983 g_outputBuffer = "\n- HTTP/1:\n\n";
1984 ret << (fmt % "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others" ) << endl;
1985 size_t counter = 0;
1986 for (const auto& ctx : g_dohlocals) {
1987 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;
1988 counter++;
1989 }
1990 g_outputBuffer += ret.str();
1991 ret.str("");
1992
1993 g_outputBuffer += "\n- HTTP/2:\n\n";
1994 ret << (fmt % "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others" ) << endl;
1995 counter = 0;
1996 for (const auto& ctx : g_dohlocals) {
1997 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;
1998 counter++;
1999 }
2000 g_outputBuffer += ret.str();
2001 }
2002 catch(const std::exception& e) {
2003 g_outputBuffer = e.what();
2004 throw;
2005 }
2006#else
2007 g_outputBuffer="DNS over HTTPS support is not present!\n";
2008#endif
2009 });
2010
28b56482 2011 g_lua.writeFunction("getDOHFrontend", [client](size_t index) {
1b51c0f3 2012 std::shared_ptr<DOHFrontend> result = nullptr;
28b56482
RG
2013 if (client) {
2014 return result;
2015 }
1b51c0f3
RG
2016#ifdef HAVE_DNS_OVER_HTTPS
2017 setLuaNoSideEffect();
2018 try {
2019 if (index < g_dohlocals.size()) {
2020 result = g_dohlocals.at(index);
2021 }
2022 else {
0718e562
RG
2023 errlog("Error: trying to get DOH frontend with index %zu but we only have %zu frontend(s)\n", index, g_dohlocals.size());
2024 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";
1b51c0f3
RG
2025 }
2026 }
2027 catch(const std::exception& e) {
0718e562 2028 g_outputBuffer="Error while trying to get DOH frontend with index " + std::to_string(index) + ": "+string(e.what())+"\n";
56d44226 2029 errlog("Error while trying to get DOH frontend with index %zu: %s\n", index, string(e.what()));
1b51c0f3
RG
2030 }
2031#else
2032 g_outputBuffer="DNS over HTTPS support is not present!\n";
2033#endif
2034 return result;
2035 });
2036
265260f5
RG
2037 g_lua.writeFunction("getDOHFrontendCount", []() {
2038 setLuaNoSideEffect();
2039 return g_dohlocals.size();
2040 });
2041
6c7cec08 2042 g_lua.registerFunction<void(std::shared_ptr<DOHFrontend>::*)()>("reloadCertificates", [](std::shared_ptr<DOHFrontend> frontend) {
1b51c0f3 2043 if (frontend != nullptr) {
6c7cec08 2044 frontend->reloadCertificates();
1b51c0f3
RG
2045 }
2046 });
2047
0ef9ab19
RG
2048 g_lua.registerFunction<void(std::shared_ptr<DOHFrontend>::*)()>("rotateTicketsKey", [](std::shared_ptr<DOHFrontend> frontend) {
2049 if (frontend != nullptr) {
2050 frontend->rotateTicketsKey(time(nullptr));
2051 }
2052 });
2053
2054 g_lua.registerFunction<void(std::shared_ptr<DOHFrontend>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<DOHFrontend> frontend, const std::string& file) {
2055 if (frontend != nullptr) {
2056 frontend->loadTicketsKeys(file);
2057 }
2058 });
2059
28b56482
RG
2060 g_lua.registerFunction<void(std::shared_ptr<DOHFrontend>::*)(const std::map<int, std::shared_ptr<DOHResponseMapEntry>>&)>("setResponsesMap", [](std::shared_ptr<DOHFrontend> frontend, const std::map<int, std::shared_ptr<DOHResponseMapEntry>>& map) {
2061 if (frontend != nullptr) {
2062 std::vector<std::shared_ptr<DOHResponseMapEntry>> newMap;
2063 newMap.reserve(map.size());
2064
2065 for (const auto& entry : map) {
2066 newMap.push_back(entry.second);
2067 }
2068
2069 frontend->d_responsesMap = std::move(newMap);
2070 }
2071 });
2072
fa974ada 2073 g_lua.writeFunction("addTLSLocal", [client](const std::string& addr, boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> keyFiles, boost::optional<localbind_t> vars) {
b35ad620 2074 if (client) {
a227f47d 2075 return;
b35ad620
RG
2076 }
2077#ifdef HAVE_DNS_OVER_TLS
a227f47d
RG
2078 setLuaSideEffect();
2079 if (g_configurationDone) {
2080 g_outputBuffer="addTLSLocal cannot be used at runtime!\n";
2081 return;
2082 }
2083 shared_ptr<TLSFrontend> frontend = std::make_shared<TLSFrontend>();
fa974ada 2084
b54e94dc 2085 if (!loadTLSCertificateAndKeys("addTLSLocal", frontend->d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) {
fa974ada
RG
2086 return;
2087 }
a227f47d 2088
6e9fd124
RG
2089 bool reusePort = false;
2090 int tcpFastOpenQueueSize = 0;
c58723d7 2091 int tcpListenQueueSize = 0;
6e9fd124
RG
2092 std::string interface;
2093 std::set<int> cpus;
6e9fd124 2094
a227f47d 2095 if (vars) {
c58723d7 2096 parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize);
a227f47d
RG
2097
2098 if (vars->count("provider")) {
2099 frontend->d_provider = boost::get<const string>((*vars)["provider"]);
b5af510a 2100 boost::algorithm::to_lower(frontend->d_provider);
a227f47d
RG
2101 }
2102
b54e94dc 2103 parseTLSConfig(frontend->d_tlsConfig, "addTLSLocal", vars);
a227f47d
RG
2104 }
2105
2106 try {
2107 frontend->d_addr = ComboAddress(addr, 853);
d94702d6
RG
2108 if (!frontend->d_provider.empty()) {
2109 vinfolog("Loading TLS provider '%s'", frontend->d_provider);
2110 }
2111 else {
b5af510a 2112#ifdef HAVE_LIBSSL
d94702d6 2113 vinfolog("Loading default TLS provider 'openssl'");
b5af510a
RG
2114#else
2115 vinfolog("Loading default TLS provider 'gnutls'");
2116#endif
d94702d6 2117 }
6e9fd124
RG
2118 // only works pre-startup, so no sync necessary
2119 auto cs = std::unique_ptr<ClientState>(new ClientState(frontend->d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus));
2120 cs->tlsFrontend = frontend;
c58723d7
RG
2121 if (tcpListenQueueSize > 0) {
2122 cs->tcpListenQueueSize = tcpListenQueueSize;
2123 }
6e9fd124
RG
2124 g_tlslocals.push_back(cs->tlsFrontend);
2125 g_frontends.push_back(std::move(cs));
a227f47d
RG
2126 }
2127 catch(const std::exception& e) {
2128 g_outputBuffer="Error: "+string(e.what())+"\n";
2129 }
2130#else
56d44226 2131 throw std::runtime_error("addTLSLocal() called but DNS over TLS support is not present!");
a227f47d
RG
2132#endif
2133 });
2134
7f2f825d 2135 g_lua.writeFunction("showTLSContexts", []() {
a227f47d
RG
2136#ifdef HAVE_DNS_OVER_TLS
2137 setLuaNoSideEffect();
2138 try {
2139 ostringstream ret;
2140 boost::format fmt("%1$-3d %2$-20.20s %|25t|%3$-14d %|40t|%4$-14d %|54t|%5$-21.21s");
2141 // 1 2 3 4 5
2142 ret << (fmt % "#" % "Address" % "# ticket keys" % "Rotation delay" % "Next rotation" ) << endl;
2143 size_t counter = 0;
2144 for (const auto& ctx : g_tlslocals) {
2145 ret << (fmt % counter % ctx->d_addr.toStringWithPort() % ctx->getTicketsKeysCount() % ctx->getTicketsKeyRotationDelay() % ctx->getNextTicketsKeyRotation()) << endl;
2146 counter++;
2147 }
2148 g_outputBuffer = ret.str();
2149 }
2150 catch(const std::exception& e) {
2151 g_outputBuffer = e.what();
2152 throw;
2153 }
2154#else
2155 g_outputBuffer="DNS over TLS support is not present!\n";
2156#endif
2157 });
2158
7f2f825d 2159 g_lua.writeFunction("getTLSContext", [](size_t index) {
a227f47d
RG
2160 std::shared_ptr<TLSCtx> result = nullptr;
2161#ifdef HAVE_DNS_OVER_TLS
2162 setLuaNoSideEffect();
2163 try {
2164 if (index < g_tlslocals.size()) {
2165 result = g_tlslocals.at(index)->getContext();
2166 }
2167 else {
0718e562
RG
2168 errlog("Error: trying to get TLS context with index %zu but we only have %zu context(s)\n", index, g_tlslocals.size());
2169 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";
a227f47d
RG
2170 }
2171 }
2172 catch(const std::exception& e) {
0718e562
RG
2173 g_outputBuffer="Error while trying to get TLS context with index " + std::to_string(index) + ": "+string(e.what())+"\n";
2174 errlog("Error while trying to get TLS context with index %zu: %s\n", index, string(e.what()));
a227f47d
RG
2175 }
2176#else
2177 g_outputBuffer="DNS over TLS support is not present!\n";
2178#endif
2179 return result;
2180 });
2181
8ef43a02
RG
2182 g_lua.writeFunction("getTLSFrontend", [](size_t index) {
2183 std::shared_ptr<TLSFrontend> result = nullptr;
2184#ifdef HAVE_DNS_OVER_TLS
2185 setLuaNoSideEffect();
2186 try {
2187 if (index < g_tlslocals.size()) {
2188 result = g_tlslocals.at(index);
2189 }
2190 else {
0718e562
RG
2191 errlog("Error: trying to get TLS frontend with index %zu but we only have %zu frontends\n", index, g_tlslocals.size());
2192 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";
8ef43a02
RG
2193 }
2194 }
2195 catch(const std::exception& e) {
0718e562
RG
2196 g_outputBuffer="Error while trying to get TLS frontend with index " + std::to_string(index) + ": "+string(e.what())+"\n";
2197 errlog("Error while trying to get TLS frontend with index %zu: %s\n", index, string(e.what()));
8ef43a02
RG
2198 }
2199#else
2200 g_outputBuffer="DNS over TLS support is not present!\n";
2201#endif
2202 return result;
2203 });
2204
265260f5
RG
2205 g_lua.writeFunction("getTLSFrontendCount", []() {
2206 setLuaNoSideEffect();
2207 return g_tlslocals.size();
2208 });
2209
a227f47d
RG
2210 g_lua.registerFunction<void(std::shared_ptr<TLSCtx>::*)()>("rotateTicketsKey", [](std::shared_ptr<TLSCtx> ctx) {
2211 if (ctx != nullptr) {
2212 ctx->rotateTicketsKey(time(nullptr));
2213 }
2214 });
2215
2216 g_lua.registerFunction<void(std::shared_ptr<TLSCtx>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<TLSCtx> ctx, const std::string& file) {
2217 if (ctx != nullptr) {
2218 ctx->loadTicketsKeys(file);
2219 }
2220 });
8ef43a02
RG
2221
2222 g_lua.registerFunction<void(std::shared_ptr<TLSFrontend>::*)(boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> keyFiles)>("loadNewCertificatesAndKeys", [](std::shared_ptr<TLSFrontend>& frontend, boost::variant<std::string, std::vector<std::pair<int,std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int,std::string>>> keyFiles) {
9ce6a273 2223#ifdef HAVE_DNS_OVER_TLS
b54e94dc 2224 if (loadTLSCertificateAndKeys("loadNewCertificatesAndKeys", frontend->d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) {
8ef43a02
RG
2225 frontend->setupTLS();
2226 }
9ce6a273 2227#endif
8ef43a02 2228 });
0dffe9e3 2229
bcc62bfb
RG
2230 g_lua.writeFunction("reloadAllCertificates", []() {
2231 for (auto& frontend : g_frontends) {
2232 if (!frontend) {
2233 continue;
2234 }
2235 try {
2236#ifdef HAVE_DNSCRYPT
2237 if (frontend->dnscryptCtx) {
37b6d73d 2238 frontend->dnscryptCtx->reloadCertificates();
bcc62bfb
RG
2239 }
2240#endif /* HAVE_DNSCRYPT */
2241#ifdef HAVE_DNS_OVER_TLS
2242 if (frontend->tlsFrontend) {
2243 frontend->tlsFrontend->setupTLS();
2244 }
2245#endif /* HAVE_DNS_OVER_TLS */
6e3a9fe4
RG
2246#ifdef HAVE_DNS_OVER_HTTPS
2247 if (frontend->dohFrontend) {
6c7cec08 2248 frontend->dohFrontend->reloadCertificates();
6e3a9fe4
RG
2249 }
2250#endif /* HAVE_DNS_OVER_HTTPS */
bcc62bfb
RG
2251 }
2252 catch(const std::exception& e) {
2253 errlog("Error reloading certificates for frontend %s: %s", frontend->local.toStringWithPort(), e.what());
2254 }
2255 }
2256 });
2257
2258 g_lua.writeFunction("setAllowEmptyResponse", [](bool allow) { g_allowEmptyResponse=allow; });
8c15553e
RG
2259
2260#if defined(HAVE_LIBSSL) && defined(HAVE_OCSP_BASIC_SIGN)
203b5348
RG
2261 g_lua.writeFunction("generateOCSPResponse", [client](const std::string& certFile, const std::string& caCert, const std::string& caKey, const std::string& outFile, int ndays, int nmin) {
2262 if (client) {
2263 return;
2264 }
2265
2266 libssl_generate_ocsp_response(certFile, caCert, caKey, outFile, ndays, nmin);
8c15553e
RG
2267 });
2268#endif /* HAVE_LIBSSL && HAVE_OCSP_BASIC_SIGN*/
6bb38cd6 2269}
2a817e5a 2270
203b5348 2271vector<std::function<void(void)>> setupLua(bool client, bool configCheck, const std::string& config)
6bb38cd6
RG
2272{
2273 g_launchWork= new vector<std::function<void(void)>>();
2a817e5a 2274
6bb38cd6 2275 setupLuaActions();
203b5348 2276 setupLuaConfig(client, configCheck);
6bb38cd6 2277 setupLuaBindings(client);
4d4d5623 2278 setupLuaBindingsDNSCrypt();
6bb38cd6 2279 setupLuaBindingsDNSQuestion();
4d4d5623
RG
2280 setupLuaBindingsKVS(client);
2281 setupLuaBindingsPacketCache();
203b5348 2282 setupLuaBindingsProtoBuf(client, configCheck);
6bb38cd6
RG
2283 setupLuaInspection();
2284 setupLuaRules();
2285 setupLuaVars();
80a216c9 2286
0ed8f0fa
RG
2287#ifdef LUAJIT_VERSION
2288 g_lua.executeCode(getLuaFFIWrappers());
2289#endif
2290
839f3021 2291 std::ifstream ifs(config);
6bb38cd6 2292 if(!ifs)
839f3021 2293 warnlog("Unable to read configuration from '%s'", config);
2e72cc0e 2294 else
cdc04ede 2295 vinfolog("Read configuration from '%s'", config);
df111b53 2296
2297 g_lua.executeCode(ifs);
d8c19b98 2298
6bb38cd6 2299 auto ret = *g_launchWork;
2e72cc0e 2300 delete g_launchWork;
6bb38cd6 2301 g_launchWork = nullptr;
2e72cc0e 2302 return ret;
df111b53 2303}