]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsdist-lua.cc
Merge pull request #7704 from ahupowerdns/boost-stringref-cutoff
[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 for (auto it = g_frontends.begin(); it != g_frontends.end(); ) {
501 /* TLS and DNSCrypt frontends are separate */
502 if ((*it)->tlsFrontend == nullptr && (*it)->dnscryptCtx == nullptr) {
503 it = g_frontends.erase(it);
504 }
505 else {
506 ++it;
507 }
508 }
509
510 // only works pre-startup, so no sync necessary
511 g_frontends.push_back(std::unique_ptr<ClientState>(new ClientState(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus)));
512 if (doTCP) {
513 g_frontends.push_back(std::unique_ptr<ClientState>(new ClientState(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus)));
514 }
515 }
516 catch(const std::exception& e) {
517 g_outputBuffer="Error: "+string(e.what())+"\n";
518 }
519 });
520
521 g_lua.writeFunction("addLocal", [client](const std::string& addr, boost::optional<localbind_t> vars) {
522 setLuaSideEffect();
523 if(client)
524 return;
525 if (g_configurationDone) {
526 g_outputBuffer="addLocal cannot be used at runtime!\n";
527 return;
528 }
529 bool doTCP = true;
530 bool reusePort = false;
531 int tcpFastOpenQueueSize = 0;
532 std::string interface;
533 std::set<int> cpus;
534
535 parseLocalBindVars(vars, doTCP, reusePort, tcpFastOpenQueueSize, interface, cpus);
536
537 try {
538 ComboAddress loc(addr, 53);
539 // only works pre-startup, so no sync necessary
540 g_frontends.push_back(std::unique_ptr<ClientState>(new ClientState(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus)));
541 if (doTCP) {
542 g_frontends.push_back(std::unique_ptr<ClientState>(new ClientState(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus)));
543 }
544 }
545 catch(std::exception& e) {
546 g_outputBuffer="Error: "+string(e.what())+"\n";
547 }
548 });
549
550 g_lua.writeFunction("setACL", [](boost::variant<string,vector<pair<int, string>>> inp) {
551 setLuaSideEffect();
552 NetmaskGroup nmg;
553 if(auto str = boost::get<string>(&inp)) {
554 nmg.addMask(*str);
555 }
556 else for(const auto& p : boost::get<vector<pair<int,string>>>(inp)) {
557 nmg.addMask(p.second);
558 }
559 g_ACL.setState(nmg);
560 });
561
562 g_lua.writeFunction("showACL", []() {
563 setLuaNoSideEffect();
564 vector<string> vec;
565
566 g_ACL.getLocal()->toStringVector(&vec);
567
568 for(const auto& s : vec)
569 g_outputBuffer+=s+"\n";
570
571 });
572
573 g_lua.writeFunction("shutdown", []() {
574 #ifdef HAVE_SYSTEMD
575 sd_notify(0, "STOPPING=1");
576 #endif /* HAVE_SYSTEMD */
577 #if 0
578 // Useful for debugging leaks, but might lead to race under load
579 // since other threads are still runing.
580 for(auto& frontend : g_tlslocals) {
581 frontend->cleanup();
582 }
583 g_tlslocals.clear();
584 #ifdef HAVE_PROTOBUF
585 google::protobuf::ShutdownProtobufLibrary();
586 #endif /* HAVE_PROTOBUF */
587 #endif /* 0 */
588 _exit(0);
589 } );
590
591 typedef std::unordered_map<std::string, boost::variant<bool, std::string> > showserversopts_t;
592
593 g_lua.writeFunction("showServers", [](boost::optional<showserversopts_t> vars) {
594 setLuaNoSideEffect();
595 bool showUUIDs = false;
596 if (vars) {
597 if (vars->count("showUUIDs")) {
598 showUUIDs = boost::get<bool>((*vars)["showUUIDs"]);
599 }
600 }
601 try {
602 ostringstream ret;
603 boost::format fmt;
604 if (showUUIDs) {
605 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%" );
606 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
607 ret << (fmt % "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools" % "UUID") << endl;
608 } else {
609 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%" );
610 ret << (fmt % "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools") << endl;
611 }
612
613 uint64_t totQPS{0}, totQueries{0}, totDrops{0};
614 int counter=0;
615 auto states = g_dstates.getLocal();
616 for(const auto& s : *states) {
617 string status = s->getStatus();
618 string pools;
619 for(auto& p : s->pools) {
620 if(!pools.empty())
621 pools+=" ";
622 pools+=p;
623 }
624 if (showUUIDs) {
625 ret << (fmt % counter % s->name % s->remote.toStringWithPort() %
626 status %
627 s->queryLoad % s->qps.getRate() % s->order % s->weight % s->queries.load() % s->reuseds.load() % (s->dropRate) % (s->latencyUsec/1000.0) % s->outstanding.load() % pools % s->id) << endl;
628 } else {
629 ret << (fmt % counter % s->name % s->remote.toStringWithPort() %
630 status %
631 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;
632 }
633 totQPS += s->queryLoad;
634 totQueries += s->queries.load();
635 totDrops += s->reuseds.load();
636 ++counter;
637 }
638 if (showUUIDs) {
639 ret<< (fmt % "All" % "" % "" % ""
640 %
641 (double)totQPS % "" % "" % "" % totQueries % totDrops % "" % "" % "" % "" % "" ) << endl;
642 } else {
643 ret<< (fmt % "All" % "" % "" % ""
644 %
645 (double)totQPS % "" % "" % "" % totQueries % totDrops % "" % "" % "" % "" ) << endl;
646 }
647
648 g_outputBuffer=ret.str();
649 } catch(std::exception& e) {
650 g_outputBuffer=e.what();
651 throw;
652 }
653 });
654
655 g_lua.writeFunction("getServers", []() {
656 setLuaNoSideEffect();
657 vector<pair<int, std::shared_ptr<DownstreamState> > > ret;
658 int count=1;
659 for(const auto& s : g_dstates.getCopy()) {
660 ret.push_back(make_pair(count++, s));
661 }
662 return ret;
663 });
664
665 g_lua.writeFunction("getPoolServers", [](string pool) {
666 return getDownstreamCandidates(g_pools.getCopy(), pool);
667 });
668
669 g_lua.writeFunction("getServer", [client](int i) {
670 if (client)
671 return std::make_shared<DownstreamState>(ComboAddress());
672 return g_dstates.getCopy().at(i);
673 });
674
675 g_lua.writeFunction("carbonServer", [](const std::string& address, boost::optional<string> ourName,
676 boost::optional<unsigned int> interval, boost::optional<string> namespace_name,
677 boost::optional<string> instance_name) {
678 setLuaSideEffect();
679 auto ours = g_carbon.getCopy();
680 ours.push_back({
681 ComboAddress(address, 2003),
682 (namespace_name && !namespace_name->empty()) ? *namespace_name : "dnsdist",
683 ourName ? *ourName : "",
684 (instance_name && !instance_name->empty()) ? *instance_name : "main" ,
685 interval ? *interval : 30
686 });
687 g_carbon.setState(ours);
688 });
689
690 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) {
691 setLuaSideEffect();
692 if(client)
693 return;
694 ComboAddress local(address);
695 try {
696 int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
697 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
698 SBind(sock, local);
699 SListen(sock, 5);
700 auto launch=[sock, local, password, apiKey, customHeaders]() {
701 setWebserverPassword(password);
702 setWebserverAPIKey(apiKey);
703 setWebserverCustomHeaders(customHeaders);
704 thread t(dnsdistWebserverThread, sock, local);
705 t.detach();
706 };
707 if(g_launchWork)
708 g_launchWork->push_back(launch);
709 else
710 launch();
711 }
712 catch(std::exception& e) {
713 g_outputBuffer="Unable to bind to webserver socket on " + local.toStringWithPort() + ": " + e.what();
714 errlog("Unable to bind to webserver socket on %s: %s", local.toStringWithPort(), e.what());
715 }
716
717 });
718
719 typedef std::unordered_map<std::string, boost::variant<std::string, std::map<std::string, std::string>> > webserveropts_t;
720
721 g_lua.writeFunction("setWebserverConfig", [](boost::optional<webserveropts_t> vars) {
722 setLuaSideEffect();
723
724 if (!vars) {
725 return ;
726 }
727 if(vars->count("password")) {
728 const std::string password = boost::get<std::string>(vars->at("password"));
729
730 setWebserverPassword(password);
731 }
732 if(vars->count("apiKey")) {
733 const std::string apiKey = boost::get<std::string>(vars->at("apiKey"));
734
735 setWebserverAPIKey(apiKey);
736 }
737 if(vars->count("customHeaders")) {
738 const boost::optional<std::map<std::string, std::string> > headers = boost::get<std::map<std::string, std::string> >(vars->at("customHeaders"));
739
740 setWebserverCustomHeaders(headers);
741 }
742 });
743
744 g_lua.writeFunction("controlSocket", [client](const std::string& str) {
745 setLuaSideEffect();
746 ComboAddress local(str, 5199);
747
748 if(client) {
749 g_serverControl = local;
750 return;
751 }
752
753 g_consoleEnabled = true;
754 #ifdef HAVE_LIBSODIUM
755 if (g_configurationDone && g_consoleKey.empty()) {
756 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");
757 }
758 #endif
759
760 try {
761 int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
762 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
763 SBind(sock, local);
764 SListen(sock, 5);
765 auto launch=[sock, local]() {
766 thread t(controlThread, sock, local);
767 t.detach();
768 };
769 if(g_launchWork)
770 g_launchWork->push_back(launch);
771 else
772 launch();
773
774 }
775 catch(std::exception& e) {
776 g_outputBuffer="Unable to bind to control socket on " + local.toStringWithPort() + ": " + e.what();
777 errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what());
778 }
779 });
780
781 g_lua.writeFunction("addConsoleACL", [](const std::string& netmask) {
782 setLuaSideEffect();
783 #ifndef HAVE_LIBSODIUM
784 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
785 #endif
786
787 g_consoleACL.modify([netmask](NetmaskGroup& nmg) { nmg.addMask(netmask); });
788 });
789
790 g_lua.writeFunction("setConsoleACL", [](boost::variant<string,vector<pair<int, string>>> inp) {
791 setLuaSideEffect();
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 NetmaskGroup nmg;
798 if(auto str = boost::get<string>(&inp)) {
799 nmg.addMask(*str);
800 }
801 else for(const auto& p : boost::get<vector<pair<int,string>>>(inp)) {
802 nmg.addMask(p.second);
803 }
804 g_consoleACL.setState(nmg);
805 });
806
807 g_lua.writeFunction("showConsoleACL", []() {
808 setLuaNoSideEffect();
809
810 #ifndef HAVE_LIBSODIUM
811 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
812 #endif
813
814 vector<string> vec;
815 g_consoleACL.getLocal()->toStringVector(&vec);
816
817 for(const auto& s : vec) {
818 g_outputBuffer += s + "\n";
819 }
820 });
821
822 g_lua.writeFunction("clearQueryCounters", []() {
823 unsigned int size{0};
824 {
825 WriteLock wl(&g_qcount.queryLock);
826 size = g_qcount.records.size();
827 g_qcount.records.clear();
828 }
829
830 boost::format fmt("%d records cleared from query counter buffer\n");
831 g_outputBuffer = (fmt % size).str();
832 });
833
834 g_lua.writeFunction("getQueryCounters", [](boost::optional<unsigned int> optMax) {
835 setLuaNoSideEffect();
836 ReadLock rl(&g_qcount.queryLock);
837 g_outputBuffer = "query counting is currently: ";
838 g_outputBuffer+= g_qcount.enabled ? "enabled" : "disabled";
839 g_outputBuffer+= (boost::format(" (%d records in buffer)\n") % g_qcount.records.size()).str();
840
841 boost::format fmt("%-3d %s: %d request(s)\n");
842 QueryCountRecords::iterator it;
843 unsigned int max = optMax ? *optMax : 10;
844 unsigned int index{1};
845 for(it = g_qcount.records.begin(); it != g_qcount.records.end() && index <= max; ++it, ++index) {
846 g_outputBuffer += (fmt % index % it->first % it->second).str();
847 }
848 });
849
850 g_lua.writeFunction("setQueryCount", [](bool enabled) { g_qcount.enabled=enabled; });
851
852 g_lua.writeFunction("setQueryCountFilter", [](QueryCountFilter func) {
853 g_qcount.filter = func;
854 });
855
856 g_lua.writeFunction("makeKey", []() {
857 setLuaNoSideEffect();
858 g_outputBuffer="setKey("+newKey()+")\n";
859 });
860
861 g_lua.writeFunction("setKey", [](const std::string& key) {
862 if(!g_configurationDone && ! g_consoleKey.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
863 return; // but later setKeys() trump the -k value again
864 }
865 #ifndef HAVE_LIBSODIUM
866 warnlog("Calling setKey() while libsodium support has not been enabled is not secure, and will result in cleartext communications");
867 #endif
868
869 setLuaSideEffect();
870 string newkey;
871 if(B64Decode(key, newkey) < 0) {
872 g_outputBuffer=string("Unable to decode ")+key+" as Base64";
873 errlog("%s", g_outputBuffer);
874 }
875 else
876 g_consoleKey=newkey;
877 });
878
879 g_lua.writeFunction("testCrypto", [](boost::optional<string> optTestMsg)
880 {
881 setLuaNoSideEffect();
882 #ifdef HAVE_LIBSODIUM
883 try {
884 string testmsg;
885
886 if (optTestMsg) {
887 testmsg = *optTestMsg;
888 }
889 else {
890 testmsg = "testStringForCryptoTests";
891 }
892
893 SodiumNonce sn, sn2;
894 sn.init();
895 sn2=sn;
896 string encrypted = sodEncryptSym(testmsg, g_consoleKey, sn);
897 string decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2);
898
899 sn.increment();
900 sn2.increment();
901
902 encrypted = sodEncryptSym(testmsg, g_consoleKey, sn);
903 decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2);
904
905 if(testmsg == decrypted)
906 g_outputBuffer="Everything is ok!\n";
907 else
908 g_outputBuffer="Crypto failed..\n";
909
910 }
911 catch(...) {
912 g_outputBuffer="Crypto failed..\n";
913 }
914 #else
915 g_outputBuffer="Crypto not available.\n";
916 #endif
917 });
918
919 g_lua.writeFunction("setTCPRecvTimeout", [](int timeout) { g_tcpRecvTimeout=timeout; });
920
921 g_lua.writeFunction("setTCPSendTimeout", [](int timeout) { g_tcpSendTimeout=timeout; });
922
923 g_lua.writeFunction("setUDPTimeout", [](int timeout) { g_udpTimeout=timeout; });
924
925 g_lua.writeFunction("setMaxUDPOutstanding", [](uint16_t max) {
926 if (!g_configurationDone) {
927 g_maxOutstanding = max;
928 } else {
929 g_outputBuffer="Max UDP outstanding cannot be altered at runtime!\n";
930 }
931 });
932
933 g_lua.writeFunction("setMaxTCPClientThreads", [](uint64_t max) {
934 if (!g_configurationDone) {
935 g_maxTCPClientThreads = max;
936 } else {
937 g_outputBuffer="Maximum TCP client threads count cannot be altered at runtime!\n";
938 }
939 });
940
941 g_lua.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max) {
942 if (!g_configurationDone) {
943 g_maxTCPQueuedConnections = max;
944 } else {
945 g_outputBuffer="The maximum number of queued TCP connections cannot be altered at runtime!\n";
946 }
947 });
948
949 g_lua.writeFunction("setMaxTCPQueriesPerConnection", [](size_t max) {
950 if (!g_configurationDone) {
951 g_maxTCPQueriesPerConn = max;
952 } else {
953 g_outputBuffer="The maximum number of queries per TCP connection cannot be altered at runtime!\n";
954 }
955 });
956
957 g_lua.writeFunction("setMaxTCPConnectionsPerClient", [](size_t max) {
958 if (!g_configurationDone) {
959 g_maxTCPConnectionsPerClient = max;
960 } else {
961 g_outputBuffer="The maximum number of TCP connection per client cannot be altered at runtime!\n";
962 }
963 });
964
965 g_lua.writeFunction("setMaxTCPConnectionDuration", [](size_t max) {
966 if (!g_configurationDone) {
967 g_maxTCPConnectionDuration = max;
968 } else {
969 g_outputBuffer="The maximum duration of a TCP connection cannot be altered at runtime!\n";
970 }
971 });
972
973 g_lua.writeFunction("setCacheCleaningDelay", [](uint32_t delay) { g_cacheCleaningDelay = delay; });
974
975 g_lua.writeFunction("setCacheCleaningPercentage", [](uint16_t percentage) { if (percentage < 100) g_cacheCleaningPercentage = percentage; else g_cacheCleaningPercentage = 100; });
976
977 g_lua.writeFunction("setECSSourcePrefixV4", [](uint16_t prefix) { g_ECSSourcePrefixV4=prefix; });
978
979 g_lua.writeFunction("setECSSourcePrefixV6", [](uint16_t prefix) { g_ECSSourcePrefixV6=prefix; });
980
981 g_lua.writeFunction("setECSOverride", [](bool override) { g_ECSOverride=override; });
982
983 g_lua.writeFunction("setPreserveTrailingData", [](bool preserve) { g_preserveTrailingData = preserve; });
984
985 g_lua.writeFunction("showDynBlocks", []() {
986 setLuaNoSideEffect();
987 auto slow = g_dynblockNMG.getCopy();
988 struct timespec now;
989 gettime(&now);
990 boost::format fmt("%-24s %8d %8d %-10s %-20s %s\n");
991 g_outputBuffer = (fmt % "What" % "Seconds" % "Blocks" % "Warning" % "Action" % "Reason").str();
992 for(const auto& e: slow) {
993 if(now < e->second.until)
994 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();
995 }
996 auto slow2 = g_dynblockSMT.getCopy();
997 slow2.visit([&now, &fmt](const SuffixMatchTree<DynBlock>& node) {
998 if(now <node.d_value.until) {
999 string dom("empty");
1000 if(!node.d_value.domain.empty())
1001 dom = node.d_value.domain.toString();
1002 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();
1003 }
1004 });
1005
1006 });
1007
1008 g_lua.writeFunction("clearDynBlocks", []() {
1009 setLuaSideEffect();
1010 nmts_t nmg;
1011 g_dynblockNMG.setState(nmg);
1012 SuffixMatchTree<DynBlock> smt;
1013 g_dynblockSMT.setState(smt);
1014 });
1015
1016 g_lua.writeFunction("addDynBlocks",
1017 [](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) {
1018 if (m.empty()) {
1019 return;
1020 }
1021 setLuaSideEffect();
1022 auto slow = g_dynblockNMG.getCopy();
1023 struct timespec until, now;
1024 gettime(&now);
1025 until=now;
1026 int actualSeconds = seconds ? *seconds : 10;
1027 until.tv_sec += actualSeconds;
1028 for(const auto& capair : m) {
1029 unsigned int count = 0;
1030 auto got = slow.lookup(Netmask(capair.first));
1031 bool expired=false;
1032 if(got) {
1033 if(until < got->second.until) // had a longer policy
1034 continue;
1035 if(now < got->second.until) // only inherit count on fresh query we are extending
1036 count=got->second.blocks;
1037 else
1038 expired=true;
1039 }
1040 DynBlock db{msg,until,DNSName(),(action ? *action : DNSAction::Action::None)};
1041 db.blocks=count;
1042 if(!got || expired)
1043 warnlog("Inserting dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg);
1044 slow.insert(Netmask(capair.first)).second=db;
1045 }
1046 g_dynblockNMG.setState(slow);
1047 });
1048
1049 g_lua.writeFunction("addDynBlockSMT",
1050 [](const vector<pair<unsigned int, string> >&names, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
1051 if (names.empty()) {
1052 return;
1053 }
1054 setLuaSideEffect();
1055 auto slow = g_dynblockSMT.getCopy();
1056 struct timespec until, now;
1057 gettime(&now);
1058 until=now;
1059 int actualSeconds = seconds ? *seconds : 10;
1060 until.tv_sec += actualSeconds;
1061
1062 for(const auto& capair : names) {
1063 unsigned int count = 0;
1064 DNSName domain(capair.second);
1065 auto got = slow.lookup(domain);
1066 bool expired=false;
1067 if(got) {
1068 if(until < got->until) // had a longer policy
1069 continue;
1070 if(now < got->until) // only inherit count on fresh query we are extending
1071 count=got->blocks;
1072 else
1073 expired=true;
1074 }
1075
1076 DynBlock db{msg,until,domain,(action ? *action : DNSAction::Action::None)};
1077 db.blocks=count;
1078 if(!got || expired)
1079 warnlog("Inserting dynamic block for %s for %d seconds: %s", domain, actualSeconds, msg);
1080 slow.add(domain, db);
1081 }
1082 g_dynblockSMT.setState(slow);
1083 });
1084
1085 g_lua.writeFunction("setDynBlocksAction", [](DNSAction::Action action) {
1086 if (!g_configurationDone) {
1087 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) {
1088 g_dynBlockAction = action;
1089 }
1090 else {
1091 errlog("Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!");
1092 g_outputBuffer="Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!\n";
1093 }
1094 } else {
1095 g_outputBuffer="Dynamic blocks action cannot be altered at runtime!\n";
1096 }
1097 });
1098
1099 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) {
1100 if (g_configurationDone) {
1101 g_outputBuffer="addDNSCryptBind cannot be used at runtime!\n";
1102 return;
1103 }
1104 #ifdef HAVE_DNSCRYPT
1105 bool doTCP = true;
1106 bool reusePort = false;
1107 int tcpFastOpenQueueSize = 0;
1108 std::string interface;
1109 std::set<int> cpus;
1110
1111 parseLocalBindVars(vars, doTCP, reusePort, tcpFastOpenQueueSize, interface, cpus);
1112
1113 try {
1114 auto ctx = std::make_shared<DNSCryptContext>(providerName, certFile, keyFile);
1115
1116 /* UDP */
1117 auto cs = std::unique_ptr<ClientState>(new ClientState(ComboAddress(addr, 443), false, reusePort, tcpFastOpenQueueSize, interface, cpus));
1118 cs->dnscryptCtx = ctx;
1119 g_dnsCryptLocals.push_back(ctx);
1120 g_frontends.push_back(std::move(cs));
1121
1122 /* TCP */
1123 cs = std::unique_ptr<ClientState>(new ClientState(ComboAddress(addr, 443), true, reusePort, tcpFastOpenQueueSize, interface, cpus));
1124 cs->dnscryptCtx = ctx;
1125 g_frontends.push_back(std::move(cs));
1126 }
1127 catch(std::exception& e) {
1128 errlog(e.what());
1129 g_outputBuffer="Error: "+string(e.what())+"\n";
1130 }
1131 #else
1132 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1133 #endif
1134
1135 });
1136
1137 g_lua.writeFunction("showDNSCryptBinds", []() {
1138 setLuaNoSideEffect();
1139 #ifdef HAVE_DNSCRYPT
1140 ostringstream ret;
1141 boost::format fmt("%1$-3d %2% %|25t|%3$-20.20s");
1142 ret << (fmt % "#" % "Address" % "Provider Name") << endl;
1143 size_t idx = 0;
1144
1145 for (const auto& frontend : g_frontends) {
1146 const std::shared_ptr<DNSCryptContext> ctx = frontend->dnscryptCtx;
1147 ret<< (fmt % idx % frontend->local.toStringWithPort() % ctx->getProviderName()) << endl;
1148 idx++;
1149 }
1150
1151 g_outputBuffer=ret.str();
1152 #else
1153 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1154 #endif
1155 });
1156
1157 g_lua.writeFunction("getDNSCryptBind", [](size_t idx) {
1158 setLuaNoSideEffect();
1159 #ifdef HAVE_DNSCRYPT
1160 std::shared_ptr<DNSCryptContext> ret = nullptr;
1161 if (idx < g_dnsCryptLocals.size()) {
1162 ret = g_dnsCryptLocals.at(idx);
1163 }
1164 return ret;
1165 #else
1166 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1167 #endif
1168 });
1169
1170 g_lua.writeFunction("generateDNSCryptProviderKeys", [](const std::string& publicKeyFile, const std::string privateKeyFile) {
1171 setLuaNoSideEffect();
1172 #ifdef HAVE_DNSCRYPT
1173 unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
1174 unsigned char privateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
1175 sodium_mlock(privateKey, sizeof(privateKey));
1176
1177 try {
1178 DNSCryptContext::generateProviderKeys(publicKey, privateKey);
1179
1180 ofstream pubKStream(publicKeyFile);
1181 pubKStream.write((char*) publicKey, sizeof(publicKey));
1182 pubKStream.close();
1183
1184 ofstream privKStream(privateKeyFile);
1185 privKStream.write((char*) privateKey, sizeof(privateKey));
1186 privKStream.close();
1187
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
1195 sodium_memzero(privateKey, sizeof(privateKey));
1196 sodium_munlock(privateKey, sizeof(privateKey));
1197 #else
1198 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1199 #endif
1200 });
1201
1202 g_lua.writeFunction("printDNSCryptProviderFingerprint", [](const std::string& publicKeyFile) {
1203 setLuaNoSideEffect();
1204 #ifdef HAVE_DNSCRYPT
1205 unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
1206
1207 try {
1208 ifstream file(publicKeyFile);
1209 file.read((char *) &publicKey, sizeof(publicKey));
1210
1211 if (file.fail())
1212 throw std::runtime_error("Invalid dnscrypt provider public key file " + publicKeyFile);
1213
1214 file.close();
1215 g_outputBuffer="Provider fingerprint is: " + DNSCryptContext::getProviderFingerprint(publicKey) + "\n";
1216 }
1217 catch(std::exception& e) {
1218 errlog(e.what());
1219 g_outputBuffer="Error: "+string(e.what())+"\n";
1220 }
1221 #else
1222 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1223 #endif
1224 });
1225
1226 #ifdef HAVE_DNSCRYPT
1227 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) {
1228 setLuaNoSideEffect();
1229 DNSCryptPrivateKey privateKey;
1230 DNSCryptCert cert;
1231
1232 try {
1233 if (generateDNSCryptCertificate(providerPrivateKeyFile, serial, begin, end, version ? *version : DNSCryptExchangeVersion::VERSION1, cert, privateKey)) {
1234 privateKey.saveToFile(privateKeyFile);
1235 DNSCryptContext::saveCertFromFile(cert, certificateFile);
1236 }
1237 }
1238 catch(const std::exception& e) {
1239 errlog(e.what());
1240 g_outputBuffer="Error: "+string(e.what())+"\n";
1241 }
1242 });
1243 #endif
1244
1245 g_lua.writeFunction("showPools", []() {
1246 setLuaNoSideEffect();
1247 try {
1248 ostringstream ret;
1249 boost::format fmt("%1$-20.20s %|25t|%2$20s %|25t|%3$20s %|50t|%4%" );
1250 // 1 2 3 4
1251 ret << (fmt % "Name" % "Cache" % "ServerPolicy" % "Servers" ) << endl;
1252
1253 const auto localPools = g_pools.getCopy();
1254 for (const auto& entry : localPools) {
1255 const string& name = entry.first;
1256 const std::shared_ptr<ServerPool> pool = entry.second;
1257 string cache = pool->packetCache != nullptr ? pool->packetCache->toString() : "";
1258 string policy = g_policy.getLocal()->name;
1259 if (pool->policy != nullptr) {
1260 policy = pool->policy->name;
1261 }
1262 string servers;
1263
1264 for (const auto& server: pool->getServers()) {
1265 if (!servers.empty()) {
1266 servers += ", ";
1267 }
1268 if (!server.second->name.empty()) {
1269 servers += server.second->name;
1270 servers += " ";
1271 }
1272 servers += server.second->remote.toStringWithPort();
1273 }
1274
1275 ret << (fmt % name % cache % policy % servers) << endl;
1276 }
1277 g_outputBuffer=ret.str();
1278 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
1279 });
1280
1281 g_lua.writeFunction("getPool", [client](const string& poolName) {
1282 if (client) {
1283 return std::make_shared<ServerPool>();
1284 }
1285 auto localPools = g_pools.getCopy();
1286 std::shared_ptr<ServerPool> pool = createPoolIfNotExists(localPools, poolName);
1287 g_pools.setState(localPools);
1288 return pool;
1289 });
1290
1291 g_lua.writeFunction("setVerboseHealthChecks", [](bool verbose) { g_verboseHealthChecks=verbose; });
1292 g_lua.writeFunction("setStaleCacheEntriesTTL", [](uint32_t ttl) { g_staleCacheEntriesTTL = ttl; });
1293
1294 g_lua.writeFunction("showBinds", []() {
1295 setLuaNoSideEffect();
1296 try {
1297 ostringstream ret;
1298 boost::format fmt("%1$-3d %2$-20.20s %|35t|%3$-20.20s %|57t|%4%" );
1299 // 1 2 3 4
1300 ret << (fmt % "#" % "Address" % "Protocol" % "Queries" ) << endl;
1301
1302 size_t counter = 0;
1303 for (const auto& front : g_frontends) {
1304 ret << (fmt % counter % front->local.toStringWithPort() % front->getType() % front->queries) << endl;
1305 counter++;
1306 }
1307 g_outputBuffer=ret.str();
1308 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
1309 });
1310
1311 g_lua.writeFunction("getBind", [](size_t num) {
1312 setLuaNoSideEffect();
1313 ClientState* ret = nullptr;
1314 if(num < g_frontends.size()) {
1315 ret=g_frontends[num].get();
1316 }
1317 return ret;
1318 });
1319
1320 g_lua.writeFunction("help", [](boost::optional<std::string> command) {
1321 setLuaNoSideEffect();
1322 g_outputBuffer = "";
1323 for (const auto& keyword : g_consoleKeywords) {
1324 if (!command) {
1325 g_outputBuffer += keyword.toString() + "\n";
1326 }
1327 else if (keyword.name == command) {
1328 g_outputBuffer = keyword.toString() + "\n";
1329 return;
1330 }
1331 }
1332 if (command) {
1333 g_outputBuffer = "Nothing found for " + *command + "\n";
1334 }
1335 });
1336
1337 g_lua.writeFunction("showVersion", []() {
1338 setLuaNoSideEffect();
1339 g_outputBuffer = "dnsdist " + std::string(VERSION) + "\n";
1340 });
1341
1342 g_lua.writeFunction("showSecurityStatus", []() {
1343 setLuaNoSideEffect();
1344 g_outputBuffer = std::to_string(g_stats.securityStatus) + "\n";
1345 });
1346
1347 #ifdef HAVE_EBPF
1348 g_lua.writeFunction("setDefaultBPFFilter", [](std::shared_ptr<BPFFilter> bpf) {
1349 if (g_configurationDone) {
1350 g_outputBuffer="setDefaultBPFFilter() cannot be used at runtime!\n";
1351 return;
1352 }
1353 g_defaultBPFFilter = bpf;
1354 });
1355
1356 g_lua.writeFunction("registerDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
1357 if (dbpf) {
1358 g_dynBPFFilters.push_back(dbpf);
1359 }
1360 });
1361
1362 g_lua.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
1363 if (dbpf) {
1364 for (auto it = g_dynBPFFilters.begin(); it != g_dynBPFFilters.end(); it++) {
1365 if (*it == dbpf) {
1366 g_dynBPFFilters.erase(it);
1367 break;
1368 }
1369 }
1370 }
1371 });
1372
1373 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) {
1374 setLuaSideEffect();
1375 struct timespec until, now;
1376 clock_gettime(CLOCK_MONOTONIC, &now);
1377 until=now;
1378 int actualSeconds = seconds ? *seconds : 10;
1379 until.tv_sec += actualSeconds;
1380 for(const auto& capair : m) {
1381 if (dynbpf->block(capair.first, until)) {
1382 warnlog("Inserting eBPF dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg ? *msg : "");
1383 }
1384 }
1385 });
1386
1387 #endif /* HAVE_EBPF */
1388
1389 g_lua.writeFunction<std::unordered_map<string,uint64_t>()>("getStatisticsCounters", []() {
1390 setLuaNoSideEffect();
1391 std::unordered_map<string,uint64_t> res;
1392 for(const auto& entry : g_stats.entries) {
1393 if(const auto& val = boost::get<DNSDistStats::stat_t*>(&entry.second))
1394 res[entry.first] = (*val)->load();
1395 }
1396 return res;
1397 });
1398
1399 g_lua.writeFunction("includeDirectory", [](const std::string& dirname) {
1400 if (g_configurationDone) {
1401 errlog("includeDirectory() cannot be used at runtime!");
1402 g_outputBuffer="includeDirectory() cannot be used at runtime!\n";
1403 return;
1404 }
1405
1406 if (g_included) {
1407 errlog("includeDirectory() cannot be used recursively!");
1408 g_outputBuffer="includeDirectory() cannot be used recursively!\n";
1409 return;
1410 }
1411
1412 g_included = true;
1413 struct stat st;
1414 if (stat(dirname.c_str(), &st)) {
1415 errlog("The included directory %s does not exist!", dirname.c_str());
1416 g_outputBuffer="The included directory " + dirname + " does not exist!";
1417 return;
1418 }
1419
1420 if (!S_ISDIR(st.st_mode)) {
1421 errlog("The included directory %s is not a directory!", dirname.c_str());
1422 g_outputBuffer="The included directory " + dirname + " is not a directory!";
1423 return;
1424 }
1425
1426 DIR *dirp;
1427 struct dirent *ent;
1428 std::list<std::string> files;
1429 if (!(dirp = opendir(dirname.c_str()))) {
1430 errlog("Error opening the included directory %s!", dirname.c_str());
1431 g_outputBuffer="Error opening the included directory " + dirname + "!";
1432 return;
1433 }
1434
1435 while((ent = readdir(dirp)) != NULL) {
1436 if (ent->d_name[0] == '.') {
1437 continue;
1438 }
1439
1440 if (boost::ends_with(ent->d_name, ".conf")) {
1441 std::ostringstream namebuf;
1442 namebuf << dirname.c_str() << "/" << ent->d_name;
1443
1444 if (stat(namebuf.str().c_str(), &st) || !S_ISREG(st.st_mode)) {
1445 continue;
1446 }
1447
1448 files.push_back(namebuf.str());
1449 }
1450 }
1451
1452 closedir(dirp);
1453 files.sort();
1454
1455 for (auto file = files.begin(); file != files.end(); ++file) {
1456 std::ifstream ifs(*file);
1457 if (!ifs) {
1458 warnlog("Unable to read configuration from '%s'", *file);
1459 } else {
1460 vinfolog("Read configuration from '%s'", *file);
1461 }
1462
1463 g_lua.executeCode(ifs);
1464 }
1465
1466 g_included = false;
1467 });
1468
1469 g_lua.writeFunction("setAPIWritable", [](bool writable, boost::optional<std::string> apiConfigDir) {
1470 setLuaSideEffect();
1471 g_apiReadWrite = writable;
1472 if (apiConfigDir) {
1473 if (!(*apiConfigDir).empty()) {
1474 g_apiConfigDirectory = *apiConfigDir;
1475 }
1476 else {
1477 errlog("The API configuration directory value cannot be empty!");
1478 g_outputBuffer="The API configuration directory value cannot be empty!";
1479 }
1480 }
1481 });
1482
1483 g_lua.writeFunction("setServFailWhenNoServer", [](bool servfail) {
1484 setLuaSideEffect();
1485 g_servFailOnNoPolicy = servfail;
1486 });
1487
1488 g_lua.writeFunction("setRoundRobinFailOnNoServer", [](bool fail) {
1489 setLuaSideEffect();
1490 g_roundrobinFailOnNoServer = fail;
1491 });
1492
1493 g_lua.writeFunction("setRingBuffersSize", [](size_t capacity, boost::optional<size_t> numberOfShards) {
1494 setLuaSideEffect();
1495 if (g_configurationDone) {
1496 errlog("setRingBuffersSize() cannot be used at runtime!");
1497 g_outputBuffer="setRingBuffersSize() cannot be used at runtime!\n";
1498 return;
1499 }
1500 g_rings.setCapacity(capacity, numberOfShards ? *numberOfShards : 1);
1501 });
1502
1503 g_lua.writeFunction("setRingBuffersLockRetries", [](size_t retries) {
1504 setLuaSideEffect();
1505 g_rings.setNumberOfLockRetries(retries);
1506 });
1507
1508 g_lua.writeFunction("setWHashedPertubation", [](uint32_t pertub) {
1509 setLuaSideEffect();
1510 g_hashperturb = pertub;
1511 });
1512
1513 g_lua.writeFunction("setTCPUseSinglePipe", [](bool flag) {
1514 if (g_configurationDone) {
1515 g_outputBuffer="setTCPUseSinglePipe() cannot be used at runtime!\n";
1516 return;
1517 }
1518 setLuaSideEffect();
1519 g_useTCPSinglePipe = flag;
1520 });
1521
1522 g_lua.writeFunction("snmpAgent", [client](bool enableTraps, boost::optional<std::string> masterSocket) {
1523 if(client)
1524 return;
1525 #ifdef HAVE_NET_SNMP
1526 if (g_configurationDone) {
1527 errlog("snmpAgent() cannot be used at runtime!");
1528 g_outputBuffer="snmpAgent() cannot be used at runtime!\n";
1529 return;
1530 }
1531
1532 if (g_snmpEnabled) {
1533 errlog("snmpAgent() cannot be used twice!");
1534 g_outputBuffer="snmpAgent() cannot be used twice!\n";
1535 return;
1536 }
1537
1538 g_snmpEnabled = true;
1539 g_snmpTrapsEnabled = enableTraps;
1540 g_snmpAgent = new DNSDistSNMPAgent("dnsdist", masterSocket ? *masterSocket : std::string());
1541 #else
1542 errlog("NET SNMP support is required to use snmpAgent()");
1543 g_outputBuffer="NET SNMP support is required to use snmpAgent()\n";
1544 #endif /* HAVE_NET_SNMP */
1545 });
1546
1547 g_lua.writeFunction("sendCustomTrap", [](const std::string& str) {
1548 #ifdef HAVE_NET_SNMP
1549 if (g_snmpAgent && g_snmpTrapsEnabled) {
1550 g_snmpAgent->sendCustomTrap(str);
1551 }
1552 #endif /* HAVE_NET_SNMP */
1553 });
1554
1555 g_lua.writeFunction("setPoolServerPolicy", [](ServerPolicy policy, string pool) {
1556 setLuaSideEffect();
1557 auto localPools = g_pools.getCopy();
1558 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(policy));
1559 g_pools.setState(localPools);
1560 });
1561
1562 g_lua.writeFunction("setPoolServerPolicyLua", [](string name, policyfunc_t policy, string pool) {
1563 setLuaSideEffect();
1564 auto localPools = g_pools.getCopy();
1565 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, policy, true}));
1566 g_pools.setState(localPools);
1567 });
1568
1569 g_lua.writeFunction("showPoolServerPolicy", [](string pool) {
1570 setLuaSideEffect();
1571 auto localPools = g_pools.getCopy();
1572 auto poolObj = getPool(localPools, pool);
1573 if (poolObj->policy == nullptr) {
1574 g_outputBuffer=g_policy.getLocal()->name+"\n";
1575 } else {
1576 g_outputBuffer=poolObj->policy->name+"\n";
1577 }
1578 });
1579
1580 g_lua.writeFunction("setTCPDownstreamCleanupInterval", [](uint16_t interval) {
1581 setLuaSideEffect();
1582 g_downstreamTCPCleanupInterval = interval;
1583 });
1584
1585 g_lua.writeFunction("setConsoleConnectionsLogging", [](bool enabled) {
1586 g_logConsoleConnections = enabled;
1587 });
1588
1589 g_lua.writeFunction("setConsoleOutputMaxMsgSize", [](uint32_t size) {
1590 g_consoleOutputMsgMaxSize = size;
1591 });
1592
1593 g_lua.writeFunction("setUDPMultipleMessagesVectorSize", [](size_t vSize) {
1594 if (g_configurationDone) {
1595 errlog("setUDPMultipleMessagesVectorSize() cannot be used at runtime!");
1596 g_outputBuffer="setUDPMultipleMessagesVectorSize() cannot be used at runtime!\n";
1597 return;
1598 }
1599 #if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
1600 setLuaSideEffect();
1601 g_udpVectorSize = vSize;
1602 #else
1603 errlog("recvmmsg() support is not available!");
1604 g_outputBuffer="recvmmsg support is not available!\n";
1605 #endif
1606 });
1607
1608 g_lua.writeFunction("setAddEDNSToSelfGeneratedResponses", [](bool add) {
1609 g_addEDNSToSelfGeneratedResponses = add;
1610 });
1611
1612 g_lua.writeFunction("setPayloadSizeOnSelfGeneratedAnswers", [](uint16_t payloadSize) {
1613 if (payloadSize < 512) {
1614 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!");
1615 g_outputBuffer="setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!";
1616 payloadSize = 512;
1617 }
1618 if (payloadSize > s_udpIncomingBufferSize) {
1619 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to %d instead!", s_udpIncomingBufferSize);
1620 g_outputBuffer="setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to " + std::to_string(s_udpIncomingBufferSize) + " instead";
1621 payloadSize = s_udpIncomingBufferSize;
1622 }
1623 g_PayloadSizeSelfGenAnswers = payloadSize;
1624 });
1625
1626 g_lua.writeFunction("setSecurityPollSuffix", [](const std::string& suffix) {
1627 if (g_configurationDone) {
1628 g_outputBuffer="setSecurityPollSuffix() cannot be used at runtime!\n";
1629 return;
1630 }
1631
1632 g_secPollSuffix = suffix;
1633 });
1634
1635 g_lua.writeFunction("setSecurityPollInterval", [](time_t newInterval) {
1636 if (newInterval <= 0) {
1637 warnlog("setSecurityPollInterval() should be > 0, skipping");
1638 g_outputBuffer="setSecurityPollInterval() should be > 0, skipping";
1639 }
1640
1641 g_secPollInterval = newInterval;
1642 });
1643
1644 g_lua.writeFunction("setSyslogFacility", [](int facility) {
1645 setLuaSideEffect();
1646 if (g_configurationDone) {
1647 g_outputBuffer="setSyslogFacility cannot be used at runtime!\n";
1648 return;
1649 }
1650 setSyslogFacility(facility);
1651 });
1652
1653 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) {
1654 if (client)
1655 return;
1656 #ifdef HAVE_DNS_OVER_TLS
1657 setLuaSideEffect();
1658 if (g_configurationDone) {
1659 g_outputBuffer="addTLSLocal cannot be used at runtime!\n";
1660 return;
1661 }
1662 shared_ptr<TLSFrontend> frontend = std::make_shared<TLSFrontend>();
1663
1664 if (!loadTLSCertificateAndKeys(frontend, certFiles, keyFiles)) {
1665 return;
1666 }
1667
1668 bool doTCP = true;
1669 bool reusePort = false;
1670 int tcpFastOpenQueueSize = 0;
1671 std::string interface;
1672 std::set<int> cpus;
1673 (void) doTCP;
1674
1675 if (vars) {
1676 bool doTCP = true;
1677 parseLocalBindVars(vars, doTCP, reusePort, tcpFastOpenQueueSize, interface, cpus);
1678
1679 if (vars->count("provider")) {
1680 frontend->d_provider = boost::get<const string>((*vars)["provider"]);
1681 }
1682
1683 if (vars->count("ciphers")) {
1684 frontend->d_ciphers = boost::get<const string>((*vars)["ciphers"]);
1685 }
1686
1687 if (vars->count("ticketKeyFile")) {
1688 frontend->d_ticketKeyFile = boost::get<const string>((*vars)["ticketKeyFile"]);
1689 }
1690
1691 if (vars->count("ticketsKeysRotationDelay")) {
1692 frontend->d_ticketsKeyRotationDelay = boost::get<int>((*vars)["ticketsKeysRotationDelay"]);
1693 }
1694
1695 if (vars->count("numberOfTicketsKeys")) {
1696 frontend->d_numberOfTicketsKeys = boost::get<int>((*vars)["numberOfTicketsKeys"]);
1697 }
1698
1699 if (vars->count("sessionTickets")) {
1700 frontend->d_enableTickets = boost::get<bool>((*vars)["sessionTickets"]);
1701 }
1702
1703 if (vars->count("numberOfStoredSessions")) {
1704 auto value = boost::get<int>((*vars)["numberOfStoredSessions"]);
1705 if (value < 0) {
1706 errlog("Invalid value '%d' for addTLSLocal() parameter 'numberOfStoredSessions', should be >= 0, dismissing", value);
1707 g_outputBuffer="Invalid value '" + std::to_string(value) + "' for addTLSLocal() parameter 'numberOfStoredSessions', should be >= 0, dimissing";
1708 return;
1709 }
1710 frontend->d_maxStoredSessions = value;
1711 }
1712 }
1713
1714 try {
1715 frontend->d_addr = ComboAddress(addr, 853);
1716 vinfolog("Loading TLS provider %s", frontend->d_provider);
1717 // only works pre-startup, so no sync necessary
1718 auto cs = std::unique_ptr<ClientState>(new ClientState(frontend->d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus));
1719 cs->tlsFrontend = frontend;
1720 g_tlslocals.push_back(cs->tlsFrontend);
1721 g_frontends.push_back(std::move(cs));
1722 }
1723 catch(const std::exception& e) {
1724 g_outputBuffer="Error: "+string(e.what())+"\n";
1725 }
1726 #else
1727 g_outputBuffer="DNS over TLS support is not present!\n";
1728 #endif
1729 });
1730
1731 g_lua.writeFunction("showTLSContexts", []() {
1732 #ifdef HAVE_DNS_OVER_TLS
1733 setLuaNoSideEffect();
1734 try {
1735 ostringstream ret;
1736 boost::format fmt("%1$-3d %2$-20.20s %|25t|%3$-14d %|40t|%4$-14d %|54t|%5$-21.21s");
1737 // 1 2 3 4 5
1738 ret << (fmt % "#" % "Address" % "# ticket keys" % "Rotation delay" % "Next rotation" ) << endl;
1739 size_t counter = 0;
1740 for (const auto& ctx : g_tlslocals) {
1741 ret << (fmt % counter % ctx->d_addr.toStringWithPort() % ctx->getTicketsKeysCount() % ctx->getTicketsKeyRotationDelay() % ctx->getNextTicketsKeyRotation()) << endl;
1742 counter++;
1743 }
1744 g_outputBuffer = ret.str();
1745 }
1746 catch(const std::exception& e) {
1747 g_outputBuffer = e.what();
1748 throw;
1749 }
1750 #else
1751 g_outputBuffer="DNS over TLS support is not present!\n";
1752 #endif
1753 });
1754
1755 g_lua.writeFunction("getTLSContext", [](size_t index) {
1756 std::shared_ptr<TLSCtx> result = nullptr;
1757 #ifdef HAVE_DNS_OVER_TLS
1758 setLuaNoSideEffect();
1759 try {
1760 if (index < g_tlslocals.size()) {
1761 result = g_tlslocals.at(index)->getContext();
1762 }
1763 else {
1764 errlog("Error: trying to get TLS context with index %zu but we only have %zu\n", index, g_tlslocals.size());
1765 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";
1766 }
1767 }
1768 catch(const std::exception& e) {
1769 g_outputBuffer="Error: "+string(e.what())+"\n";
1770 errlog("Error: %s\n", string(e.what()));
1771 }
1772 #else
1773 g_outputBuffer="DNS over TLS support is not present!\n";
1774 #endif
1775 return result;
1776 });
1777
1778 g_lua.writeFunction("getTLSFrontend", [](size_t index) {
1779 std::shared_ptr<TLSFrontend> result = nullptr;
1780 #ifdef HAVE_DNS_OVER_TLS
1781 setLuaNoSideEffect();
1782 try {
1783 if (index < g_tlslocals.size()) {
1784 result = g_tlslocals.at(index);
1785 }
1786 else {
1787 errlog("Error: trying to get TLS frontend with index %zu but we only have %zu\n", index, g_tlslocals.size());
1788 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";
1789 }
1790 }
1791 catch(const std::exception& e) {
1792 g_outputBuffer="Error: "+string(e.what())+"\n";
1793 errlog("Error: %s\n", string(e.what()));
1794 }
1795 #else
1796 g_outputBuffer="DNS over TLS support is not present!\n";
1797 #endif
1798 return result;
1799 });
1800
1801 g_lua.registerFunction<void(std::shared_ptr<TLSCtx>::*)()>("rotateTicketsKey", [](std::shared_ptr<TLSCtx> ctx) {
1802 if (ctx != nullptr) {
1803 ctx->rotateTicketsKey(time(nullptr));
1804 }
1805 });
1806
1807 g_lua.registerFunction<void(std::shared_ptr<TLSCtx>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<TLSCtx> ctx, const std::string& file) {
1808 if (ctx != nullptr) {
1809 ctx->loadTicketsKeys(file);
1810 }
1811 });
1812
1813 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) {
1814 #ifdef HAVE_DNS_OVER_TLS
1815 if (loadTLSCertificateAndKeys(frontend, certFiles, keyFiles)) {
1816 frontend->setupTLS();
1817 }
1818 #endif
1819 });
1820
1821 g_lua.writeFunction("reloadAllCertificates", []() {
1822 for (auto& frontend : g_frontends) {
1823 if (!frontend) {
1824 continue;
1825 }
1826 try {
1827 #ifdef HAVE_DNSCRYPT
1828 if (frontend->dnscryptCtx) {
1829 frontend->dnscryptCtx->reloadCertificate();
1830 }
1831 #endif /* HAVE_DNSCRYPT */
1832 #ifdef HAVE_DNS_OVER_TLS
1833 if (frontend->tlsFrontend) {
1834 frontend->tlsFrontend->setupTLS();
1835 }
1836 #endif /* HAVE_DNS_OVER_TLS */
1837 }
1838 catch(const std::exception& e) {
1839 errlog("Error reloading certificates for frontend %s: %s", frontend->local.toStringWithPort(), e.what());
1840 }
1841 }
1842 });
1843
1844 g_lua.writeFunction("setAllowEmptyResponse", [](bool allow) { g_allowEmptyResponse=allow; });
1845 }
1846
1847 vector<std::function<void(void)>> setupLua(bool client, const std::string& config)
1848 {
1849 g_launchWork= new vector<std::function<void(void)>>();
1850
1851 setupLuaActions();
1852 setupLuaConfig(client);
1853 setupLuaBindings(client);
1854 setupLuaBindingsDNSQuestion();
1855 setupLuaInspection();
1856 setupLuaRules();
1857 setupLuaVars();
1858
1859 std::ifstream ifs(config);
1860 if(!ifs)
1861 warnlog("Unable to read configuration from '%s'", config);
1862 else
1863 vinfolog("Read configuration from '%s'", config);
1864
1865 g_lua.executeCode(ifs);
1866
1867 auto ret = *g_launchWork;
1868 delete g_launchWork;
1869 g_launchWork = nullptr;
1870 return ret;
1871 }