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