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