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