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