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