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