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