]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsdist-lua.cc
dnsdist: add ability to update webserver credentials
[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 setWebserverConfig(password, apiKey, customHeaders);
635 thread t(dnsdistWebserverThread, sock, local);
636 t.detach();
637 };
638 if(g_launchWork)
639 g_launchWork->push_back(launch);
640 else
641 launch();
642 }
643 catch(std::exception& e) {
644 g_outputBuffer="Unable to bind to webserver socket on " + local.toStringWithPort() + ": " + e.what();
645 errlog("Unable to bind to webserver socket on %s: %s", local.toStringWithPort(), e.what());
646 }
647
648 });
649
650 g_lua.writeFunction("setWebserverConfig", [](const std::string& password, const boost::optional<std::string> apiKey, const boost::optional<std::map<std::string, std::string> > customHeaders) {
651 setLuaSideEffect();
652 setWebserverConfig(password, apiKey, customHeaders);
653 });
654
655 g_lua.writeFunction("controlSocket", [client](const std::string& str) {
656 setLuaSideEffect();
657 ComboAddress local(str, 5199);
658
659 if(client) {
660 g_serverControl = local;
661 return;
662 }
663
664 g_consoleEnabled = true;
665 #ifdef HAVE_LIBSODIUM
666 if (g_configurationDone && g_consoleKey.empty()) {
667 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");
668 }
669 #endif
670
671 try {
672 int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
673 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
674 SBind(sock, local);
675 SListen(sock, 5);
676 auto launch=[sock, local]() {
677 thread t(controlThread, sock, local);
678 t.detach();
679 };
680 if(g_launchWork)
681 g_launchWork->push_back(launch);
682 else
683 launch();
684
685 }
686 catch(std::exception& e) {
687 g_outputBuffer="Unable to bind to control socket on " + local.toStringWithPort() + ": " + e.what();
688 errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what());
689 }
690 });
691
692 g_lua.writeFunction("addConsoleACL", [](const std::string& netmask) {
693 setLuaSideEffect();
694 #ifndef HAVE_LIBSODIUM
695 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
696 #endif
697
698 g_consoleACL.modify([netmask](NetmaskGroup& nmg) { nmg.addMask(netmask); });
699 });
700
701 g_lua.writeFunction("setConsoleACL", [](boost::variant<string,vector<pair<int, string>>> inp) {
702 setLuaSideEffect();
703
704 #ifndef HAVE_LIBSODIUM
705 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
706 #endif
707
708 NetmaskGroup nmg;
709 if(auto str = boost::get<string>(&inp)) {
710 nmg.addMask(*str);
711 }
712 else for(const auto& p : boost::get<vector<pair<int,string>>>(inp)) {
713 nmg.addMask(p.second);
714 }
715 g_consoleACL.setState(nmg);
716 });
717
718 g_lua.writeFunction("showConsoleACL", []() {
719 setLuaNoSideEffect();
720
721 #ifndef HAVE_LIBSODIUM
722 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
723 #endif
724
725 vector<string> vec;
726 g_consoleACL.getLocal()->toStringVector(&vec);
727
728 for(const auto& s : vec) {
729 g_outputBuffer += s + "\n";
730 }
731 });
732
733 g_lua.writeFunction("clearQueryCounters", []() {
734 unsigned int size{0};
735 {
736 WriteLock wl(&g_qcount.queryLock);
737 size = g_qcount.records.size();
738 g_qcount.records.clear();
739 }
740
741 boost::format fmt("%d records cleared from query counter buffer\n");
742 g_outputBuffer = (fmt % size).str();
743 });
744
745 g_lua.writeFunction("getQueryCounters", [](boost::optional<unsigned int> optMax) {
746 setLuaNoSideEffect();
747 ReadLock rl(&g_qcount.queryLock);
748 g_outputBuffer = "query counting is currently: ";
749 g_outputBuffer+= g_qcount.enabled ? "enabled" : "disabled";
750 g_outputBuffer+= (boost::format(" (%d records in buffer)\n") % g_qcount.records.size()).str();
751
752 boost::format fmt("%-3d %s: %d request(s)\n");
753 QueryCountRecords::iterator it;
754 unsigned int max = optMax ? *optMax : 10;
755 unsigned int index{1};
756 for(it = g_qcount.records.begin(); it != g_qcount.records.end() && index <= max; ++it, ++index) {
757 g_outputBuffer += (fmt % index % it->first % it->second).str();
758 }
759 });
760
761 g_lua.writeFunction("setQueryCount", [](bool enabled) { g_qcount.enabled=enabled; });
762
763 g_lua.writeFunction("setQueryCountFilter", [](QueryCountFilter func) {
764 g_qcount.filter = func;
765 });
766
767 g_lua.writeFunction("makeKey", []() {
768 setLuaNoSideEffect();
769 g_outputBuffer="setKey("+newKey()+")\n";
770 });
771
772 g_lua.writeFunction("setKey", [](const std::string& key) {
773 if(!g_configurationDone && ! g_consoleKey.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
774 return; // but later setKeys() trump the -k value again
775 }
776 #ifndef HAVE_LIBSODIUM
777 warnlog("Calling setKey() while libsodium support has not been enabled is not secure, and will result in cleartext communications");
778 #endif
779
780 setLuaSideEffect();
781 string newkey;
782 if(B64Decode(key, newkey) < 0) {
783 g_outputBuffer=string("Unable to decode ")+key+" as Base64";
784 errlog("%s", g_outputBuffer);
785 }
786 else
787 g_consoleKey=newkey;
788 });
789
790 g_lua.writeFunction("testCrypto", [](boost::optional<string> optTestMsg)
791 {
792 setLuaNoSideEffect();
793 #ifdef HAVE_LIBSODIUM
794 try {
795 string testmsg;
796
797 if (optTestMsg) {
798 testmsg = *optTestMsg;
799 }
800 else {
801 testmsg = "testStringForCryptoTests";
802 }
803
804 SodiumNonce sn, sn2;
805 sn.init();
806 sn2=sn;
807 string encrypted = sodEncryptSym(testmsg, g_consoleKey, sn);
808 string decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2);
809
810 sn.increment();
811 sn2.increment();
812
813 encrypted = sodEncryptSym(testmsg, g_consoleKey, sn);
814 decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2);
815
816 if(testmsg == decrypted)
817 g_outputBuffer="Everything is ok!\n";
818 else
819 g_outputBuffer="Crypto failed..\n";
820
821 }
822 catch(...) {
823 g_outputBuffer="Crypto failed..\n";
824 }
825 #else
826 g_outputBuffer="Crypto not available.\n";
827 #endif
828 });
829
830 g_lua.writeFunction("setTCPRecvTimeout", [](int timeout) { g_tcpRecvTimeout=timeout; });
831
832 g_lua.writeFunction("setTCPSendTimeout", [](int timeout) { g_tcpSendTimeout=timeout; });
833
834 g_lua.writeFunction("setUDPTimeout", [](int timeout) { g_udpTimeout=timeout; });
835
836 g_lua.writeFunction("setMaxUDPOutstanding", [](uint16_t max) {
837 if (!g_configurationDone) {
838 g_maxOutstanding = max;
839 } else {
840 g_outputBuffer="Max UDP outstanding cannot be altered at runtime!\n";
841 }
842 });
843
844 g_lua.writeFunction("setMaxTCPClientThreads", [](uint64_t max) {
845 if (!g_configurationDone) {
846 g_maxTCPClientThreads = max;
847 } else {
848 g_outputBuffer="Maximum TCP client threads count cannot be altered at runtime!\n";
849 }
850 });
851
852 g_lua.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max) {
853 if (!g_configurationDone) {
854 g_maxTCPQueuedConnections = max;
855 } else {
856 g_outputBuffer="The maximum number of queued TCP connections cannot be altered at runtime!\n";
857 }
858 });
859
860 g_lua.writeFunction("setMaxTCPQueriesPerConnection", [](size_t max) {
861 if (!g_configurationDone) {
862 g_maxTCPQueriesPerConn = max;
863 } else {
864 g_outputBuffer="The maximum number of queries per TCP connection cannot be altered at runtime!\n";
865 }
866 });
867
868 g_lua.writeFunction("setMaxTCPConnectionsPerClient", [](size_t max) {
869 if (!g_configurationDone) {
870 g_maxTCPConnectionsPerClient = max;
871 } else {
872 g_outputBuffer="The maximum number of TCP connection per client cannot be altered at runtime!\n";
873 }
874 });
875
876 g_lua.writeFunction("setMaxTCPConnectionDuration", [](size_t max) {
877 if (!g_configurationDone) {
878 g_maxTCPConnectionDuration = max;
879 } else {
880 g_outputBuffer="The maximum duration of a TCP connection cannot be altered at runtime!\n";
881 }
882 });
883
884 g_lua.writeFunction("setCacheCleaningDelay", [](uint32_t delay) { g_cacheCleaningDelay = delay; });
885
886 g_lua.writeFunction("setCacheCleaningPercentage", [](uint16_t percentage) { if (percentage < 100) g_cacheCleaningPercentage = percentage; else g_cacheCleaningPercentage = 100; });
887
888 g_lua.writeFunction("setECSSourcePrefixV4", [](uint16_t prefix) { g_ECSSourcePrefixV4=prefix; });
889
890 g_lua.writeFunction("setECSSourcePrefixV6", [](uint16_t prefix) { g_ECSSourcePrefixV6=prefix; });
891
892 g_lua.writeFunction("setECSOverride", [](bool override) { g_ECSOverride=override; });
893
894 g_lua.writeFunction("showDynBlocks", []() {
895 setLuaNoSideEffect();
896 auto slow = g_dynblockNMG.getCopy();
897 struct timespec now;
898 gettime(&now);
899 boost::format fmt("%-24s %8d %8d %-10s %-20s %s\n");
900 g_outputBuffer = (fmt % "What" % "Seconds" % "Blocks" % "Warning" % "Action" % "Reason").str();
901 for(const auto& e: slow) {
902 if(now < e->second.until)
903 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();
904 }
905 auto slow2 = g_dynblockSMT.getCopy();
906 slow2.visit([&now, &fmt](const SuffixMatchTree<DynBlock>& node) {
907 if(now <node.d_value.until) {
908 string dom("empty");
909 if(!node.d_value.domain.empty())
910 dom = node.d_value.domain.toString();
911 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();
912 }
913 });
914
915 });
916
917 g_lua.writeFunction("clearDynBlocks", []() {
918 setLuaSideEffect();
919 nmts_t nmg;
920 g_dynblockNMG.setState(nmg);
921 SuffixMatchTree<DynBlock> smt;
922 g_dynblockSMT.setState(smt);
923 });
924
925 g_lua.writeFunction("addDynBlocks",
926 [](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) {
927 if (m.empty()) {
928 return;
929 }
930 setLuaSideEffect();
931 auto slow = g_dynblockNMG.getCopy();
932 struct timespec until, now;
933 gettime(&now);
934 until=now;
935 int actualSeconds = seconds ? *seconds : 10;
936 until.tv_sec += actualSeconds;
937 for(const auto& capair : m) {
938 unsigned int count = 0;
939 auto got = slow.lookup(Netmask(capair.first));
940 bool expired=false;
941 if(got) {
942 if(until < got->second.until) // had a longer policy
943 continue;
944 if(now < got->second.until) // only inherit count on fresh query we are extending
945 count=got->second.blocks;
946 else
947 expired=true;
948 }
949 DynBlock db{msg,until,DNSName(),(action ? *action : DNSAction::Action::None)};
950 db.blocks=count;
951 if(!got || expired)
952 warnlog("Inserting dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg);
953 slow.insert(Netmask(capair.first)).second=db;
954 }
955 g_dynblockNMG.setState(slow);
956 });
957
958 g_lua.writeFunction("addDynBlockSMT",
959 [](const vector<pair<unsigned int, string> >&names, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
960 if (names.empty()) {
961 return;
962 }
963 setLuaSideEffect();
964 auto slow = g_dynblockSMT.getCopy();
965 struct timespec until, now;
966 gettime(&now);
967 until=now;
968 int actualSeconds = seconds ? *seconds : 10;
969 until.tv_sec += actualSeconds;
970
971 for(const auto& capair : names) {
972 unsigned int count = 0;
973 DNSName domain(capair.second);
974 auto got = slow.lookup(domain);
975 bool expired=false;
976 if(got) {
977 if(until < got->until) // had a longer policy
978 continue;
979 if(now < got->until) // only inherit count on fresh query we are extending
980 count=got->blocks;
981 else
982 expired=true;
983 }
984
985 DynBlock db{msg,until,domain,(action ? *action : DNSAction::Action::None)};
986 db.blocks=count;
987 if(!got || expired)
988 warnlog("Inserting dynamic block for %s for %d seconds: %s", domain, actualSeconds, msg);
989 slow.add(domain, db);
990 }
991 g_dynblockSMT.setState(slow);
992 });
993
994 g_lua.writeFunction("setDynBlocksAction", [](DNSAction::Action action) {
995 if (!g_configurationDone) {
996 if (action == DNSAction::Action::Drop || action == DNSAction::Action::NoOp || action == DNSAction::Action::Nxdomain || action == DNSAction::Action::Refused || action == DNSAction::Action::Truncate) {
997 g_dynBlockAction = action;
998 }
999 else {
1000 errlog("Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused or Truncate!");
1001 g_outputBuffer="Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused or Truncate!\n";
1002 }
1003 } else {
1004 g_outputBuffer="Dynamic blocks action cannot be altered at runtime!\n";
1005 }
1006 });
1007
1008 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) {
1009 if (g_configurationDone) {
1010 g_outputBuffer="addDNSCryptBind cannot be used at runtime!\n";
1011 return;
1012 }
1013 #ifdef HAVE_DNSCRYPT
1014 bool doTCP = true;
1015 bool reusePort = false;
1016 int tcpFastOpenQueueSize = 0;
1017 std::string interface;
1018 std::set<int> cpus;
1019
1020 parseLocalBindVars(vars, doTCP, reusePort, tcpFastOpenQueueSize, interface, cpus);
1021
1022 try {
1023 auto ctx = std::make_shared<DNSCryptContext>(providerName, certFile, keyFile);
1024 g_dnsCryptLocals.push_back(std::make_tuple(ComboAddress(addr, 443), ctx, reusePort, tcpFastOpenQueueSize, interface, cpus));
1025 }
1026 catch(std::exception& e) {
1027 errlog(e.what());
1028 g_outputBuffer="Error: "+string(e.what())+"\n";
1029 }
1030 #else
1031 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1032 #endif
1033
1034 });
1035
1036 g_lua.writeFunction("showDNSCryptBinds", []() {
1037 setLuaNoSideEffect();
1038 #ifdef HAVE_DNSCRYPT
1039 ostringstream ret;
1040 boost::format fmt("%1$-3d %2% %|25t|%3$-20.20s");
1041 ret << (fmt % "#" % "Address" % "Provider Name") << endl;
1042 size_t idx = 0;
1043
1044 for (const auto& local : g_dnsCryptLocals) {
1045 const std::shared_ptr<DNSCryptContext> ctx = std::get<1>(local);
1046 ret<< (fmt % idx % std::get<0>(local).toStringWithPort() % ctx->getProviderName()) << endl;
1047 idx++;
1048 }
1049
1050 g_outputBuffer=ret.str();
1051 #else
1052 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1053 #endif
1054 });
1055
1056 g_lua.writeFunction("getDNSCryptBind", [](size_t idx) {
1057 setLuaNoSideEffect();
1058 #ifdef HAVE_DNSCRYPT
1059 std::shared_ptr<DNSCryptContext> ret = nullptr;
1060 if (idx < g_dnsCryptLocals.size()) {
1061 ret = std::get<1>(g_dnsCryptLocals.at(idx));
1062 }
1063 return ret;
1064 #else
1065 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1066 #endif
1067 });
1068
1069 g_lua.writeFunction("generateDNSCryptProviderKeys", [](const std::string& publicKeyFile, const std::string privateKeyFile) {
1070 setLuaNoSideEffect();
1071 #ifdef HAVE_DNSCRYPT
1072 unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
1073 unsigned char privateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
1074 sodium_mlock(privateKey, sizeof(privateKey));
1075
1076 try {
1077 DNSCryptContext::generateProviderKeys(publicKey, privateKey);
1078
1079 ofstream pubKStream(publicKeyFile);
1080 pubKStream.write((char*) publicKey, sizeof(publicKey));
1081 pubKStream.close();
1082
1083 ofstream privKStream(privateKeyFile);
1084 privKStream.write((char*) privateKey, sizeof(privateKey));
1085 privKStream.close();
1086
1087 g_outputBuffer="Provider fingerprint is: " + DNSCryptContext::getProviderFingerprint(publicKey) + "\n";
1088 }
1089 catch(std::exception& e) {
1090 errlog(e.what());
1091 g_outputBuffer="Error: "+string(e.what())+"\n";
1092 }
1093
1094 sodium_memzero(privateKey, sizeof(privateKey));
1095 sodium_munlock(privateKey, sizeof(privateKey));
1096 #else
1097 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1098 #endif
1099 });
1100
1101 g_lua.writeFunction("printDNSCryptProviderFingerprint", [](const std::string& publicKeyFile) {
1102 setLuaNoSideEffect();
1103 #ifdef HAVE_DNSCRYPT
1104 unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
1105
1106 try {
1107 ifstream file(publicKeyFile);
1108 file.read((char *) &publicKey, sizeof(publicKey));
1109
1110 if (file.fail())
1111 throw std::runtime_error("Invalid dnscrypt provider public key file " + publicKeyFile);
1112
1113 file.close();
1114 g_outputBuffer="Provider fingerprint is: " + DNSCryptContext::getProviderFingerprint(publicKey) + "\n";
1115 }
1116 catch(std::exception& e) {
1117 errlog(e.what());
1118 g_outputBuffer="Error: "+string(e.what())+"\n";
1119 }
1120 #else
1121 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1122 #endif
1123 });
1124
1125 #ifdef HAVE_DNSCRYPT
1126 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) {
1127 setLuaNoSideEffect();
1128 DNSCryptPrivateKey privateKey;
1129 DNSCryptCert cert;
1130
1131 try {
1132 if (generateDNSCryptCertificate(providerPrivateKeyFile, serial, begin, end, version ? *version : DNSCryptExchangeVersion::VERSION1, cert, privateKey)) {
1133 privateKey.saveToFile(privateKeyFile);
1134 DNSCryptContext::saveCertFromFile(cert, certificateFile);
1135 }
1136 }
1137 catch(const std::exception& e) {
1138 errlog(e.what());
1139 g_outputBuffer="Error: "+string(e.what())+"\n";
1140 }
1141 });
1142 #endif
1143
1144 g_lua.writeFunction("showPools", []() {
1145 setLuaNoSideEffect();
1146 try {
1147 ostringstream ret;
1148 boost::format fmt("%1$-20.20s %|25t|%2$20s %|25t|%3$20s %|50t|%4%" );
1149 // 1 2 3 4
1150 ret << (fmt % "Name" % "Cache" % "ServerPolicy" % "Servers" ) << endl;
1151
1152 const auto localPools = g_pools.getCopy();
1153 for (const auto& entry : localPools) {
1154 const string& name = entry.first;
1155 const std::shared_ptr<ServerPool> pool = entry.second;
1156 string cache = pool->packetCache != nullptr ? pool->packetCache->toString() : "";
1157 string policy = g_policy.getLocal()->name;
1158 if (pool->policy != nullptr) {
1159 policy = pool->policy->name;
1160 }
1161 string servers;
1162
1163 for (const auto& server: pool->getServers()) {
1164 if (!servers.empty()) {
1165 servers += ", ";
1166 }
1167 if (!server.second->name.empty()) {
1168 servers += server.second->name;
1169 servers += " ";
1170 }
1171 servers += server.second->remote.toStringWithPort();
1172 }
1173
1174 ret << (fmt % name % cache % policy % servers) << endl;
1175 }
1176 g_outputBuffer=ret.str();
1177 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
1178 });
1179
1180 g_lua.writeFunction("getPool", [client](const string& poolName) {
1181 if (client) {
1182 return std::make_shared<ServerPool>();
1183 }
1184 auto localPools = g_pools.getCopy();
1185 std::shared_ptr<ServerPool> pool = createPoolIfNotExists(localPools, poolName);
1186 g_pools.setState(localPools);
1187 return pool;
1188 });
1189
1190 g_lua.writeFunction("setVerboseHealthChecks", [](bool verbose) { g_verboseHealthChecks=verbose; });
1191 g_lua.writeFunction("setStaleCacheEntriesTTL", [](uint32_t ttl) { g_staleCacheEntriesTTL = ttl; });
1192
1193 g_lua.writeFunction("showBinds", []() {
1194 setLuaNoSideEffect();
1195 try {
1196 ostringstream ret;
1197 boost::format fmt("%1$-3d %2$-20.20s %|25t|%3$-8.8s %|35t|%4%" );
1198 // 1 2 3 4
1199 ret << (fmt % "#" % "Address" % "Protocol" % "Queries" ) << endl;
1200
1201 size_t counter = 0;
1202 for (const auto& front : g_frontends) {
1203 ret << (fmt % counter % front->local.toStringWithPort() % (front->udpFD != -1 ? "UDP" : "TCP") % front->queries) << endl;
1204 counter++;
1205 }
1206 g_outputBuffer=ret.str();
1207 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
1208 });
1209
1210 g_lua.writeFunction("getBind", [](size_t num) {
1211 setLuaNoSideEffect();
1212 ClientState* ret = nullptr;
1213 if(num < g_frontends.size()) {
1214 ret=g_frontends[num];
1215 }
1216 return ret;
1217 });
1218
1219 g_lua.writeFunction("help", [](boost::optional<std::string> command) {
1220 setLuaNoSideEffect();
1221 g_outputBuffer = "";
1222 for (const auto& keyword : g_consoleKeywords) {
1223 if (!command) {
1224 g_outputBuffer += keyword.toString() + "\n";
1225 }
1226 else if (keyword.name == command) {
1227 g_outputBuffer = keyword.toString() + "\n";
1228 return;
1229 }
1230 }
1231 if (command) {
1232 g_outputBuffer = "Nothing found for " + *command + "\n";
1233 }
1234 });
1235
1236 g_lua.writeFunction("showVersion", []() {
1237 setLuaNoSideEffect();
1238 g_outputBuffer = "dnsdist " + std::string(VERSION) + "\n";
1239 });
1240
1241 #ifdef HAVE_EBPF
1242 g_lua.writeFunction("setDefaultBPFFilter", [](std::shared_ptr<BPFFilter> bpf) {
1243 if (g_configurationDone) {
1244 g_outputBuffer="setDefaultBPFFilter() cannot be used at runtime!\n";
1245 return;
1246 }
1247 g_defaultBPFFilter = bpf;
1248 });
1249
1250 g_lua.writeFunction("registerDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
1251 if (dbpf) {
1252 g_dynBPFFilters.push_back(dbpf);
1253 }
1254 });
1255
1256 g_lua.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
1257 if (dbpf) {
1258 for (auto it = g_dynBPFFilters.begin(); it != g_dynBPFFilters.end(); it++) {
1259 if (*it == dbpf) {
1260 g_dynBPFFilters.erase(it);
1261 break;
1262 }
1263 }
1264 }
1265 });
1266
1267 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) {
1268 setLuaSideEffect();
1269 struct timespec until, now;
1270 clock_gettime(CLOCK_MONOTONIC, &now);
1271 until=now;
1272 int actualSeconds = seconds ? *seconds : 10;
1273 until.tv_sec += actualSeconds;
1274 for(const auto& capair : m) {
1275 if (dynbpf->block(capair.first, until)) {
1276 warnlog("Inserting eBPF dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg ? *msg : "");
1277 }
1278 }
1279 });
1280
1281 #endif /* HAVE_EBPF */
1282
1283 g_lua.writeFunction<std::unordered_map<string,uint64_t>()>("getStatisticsCounters", []() {
1284 setLuaNoSideEffect();
1285 std::unordered_map<string,uint64_t> res;
1286 for(const auto& entry : g_stats.entries) {
1287 if(const auto& val = boost::get<DNSDistStats::stat_t*>(&entry.second))
1288 res[entry.first] = (*val)->load();
1289 }
1290 return res;
1291 });
1292
1293 g_lua.writeFunction("includeDirectory", [](const std::string& dirname) {
1294 if (g_configurationDone) {
1295 errlog("includeDirectory() cannot be used at runtime!");
1296 g_outputBuffer="includeDirectory() cannot be used at runtime!\n";
1297 return;
1298 }
1299
1300 if (g_included) {
1301 errlog("includeDirectory() cannot be used recursively!");
1302 g_outputBuffer="includeDirectory() cannot be used recursively!\n";
1303 return;
1304 }
1305
1306 g_included = true;
1307 struct stat st;
1308 if (stat(dirname.c_str(), &st)) {
1309 errlog("The included directory %s does not exist!", dirname.c_str());
1310 g_outputBuffer="The included directory " + dirname + " does not exist!";
1311 return;
1312 }
1313
1314 if (!S_ISDIR(st.st_mode)) {
1315 errlog("The included directory %s is not a directory!", dirname.c_str());
1316 g_outputBuffer="The included directory " + dirname + " is not a directory!";
1317 return;
1318 }
1319
1320 DIR *dirp;
1321 struct dirent *ent;
1322 std::list<std::string> files;
1323 if (!(dirp = opendir(dirname.c_str()))) {
1324 errlog("Error opening the included directory %s!", dirname.c_str());
1325 g_outputBuffer="Error opening the included directory " + dirname + "!";
1326 return;
1327 }
1328
1329 while((ent = readdir(dirp)) != NULL) {
1330 if (ent->d_name[0] == '.') {
1331 continue;
1332 }
1333
1334 if (boost::ends_with(ent->d_name, ".conf")) {
1335 std::ostringstream namebuf;
1336 namebuf << dirname.c_str() << "/" << ent->d_name;
1337
1338 if (stat(namebuf.str().c_str(), &st) || !S_ISREG(st.st_mode)) {
1339 continue;
1340 }
1341
1342 files.push_back(namebuf.str());
1343 }
1344 }
1345
1346 closedir(dirp);
1347 files.sort();
1348
1349 for (auto file = files.begin(); file != files.end(); ++file) {
1350 std::ifstream ifs(*file);
1351 if (!ifs) {
1352 warnlog("Unable to read configuration from '%s'", *file);
1353 } else {
1354 vinfolog("Read configuration from '%s'", *file);
1355 }
1356
1357 g_lua.executeCode(ifs);
1358 }
1359
1360 g_included = false;
1361 });
1362
1363 g_lua.writeFunction("setAPIWritable", [](bool writable, boost::optional<std::string> apiConfigDir) {
1364 setLuaSideEffect();
1365 g_apiReadWrite = writable;
1366 if (apiConfigDir) {
1367 if (!(*apiConfigDir).empty()) {
1368 g_apiConfigDirectory = *apiConfigDir;
1369 }
1370 else {
1371 errlog("The API configuration directory value cannot be empty!");
1372 g_outputBuffer="The API configuration directory value cannot be empty!";
1373 }
1374 }
1375 });
1376
1377 g_lua.writeFunction("setServFailWhenNoServer", [](bool servfail) {
1378 setLuaSideEffect();
1379 g_servFailOnNoPolicy = servfail;
1380 });
1381
1382 g_lua.writeFunction("setRingBuffersSize", [](size_t capacity, boost::optional<size_t> numberOfShards) {
1383 setLuaSideEffect();
1384 if (g_configurationDone) {
1385 errlog("setRingBuffersSize() cannot be used at runtime!");
1386 g_outputBuffer="setRingBuffersSize() cannot be used at runtime!\n";
1387 return;
1388 }
1389 g_rings.setCapacity(capacity, numberOfShards ? *numberOfShards : 1);
1390 });
1391
1392 g_lua.writeFunction("setRingBuffersLockRetries", [](size_t retries) {
1393 setLuaSideEffect();
1394 g_rings.setNumberOfLockRetries(retries);
1395 });
1396
1397 g_lua.writeFunction("setWHashedPertubation", [](uint32_t pertub) {
1398 setLuaSideEffect();
1399 g_hashperturb = pertub;
1400 });
1401
1402 g_lua.writeFunction("setTCPUseSinglePipe", [](bool flag) {
1403 if (g_configurationDone) {
1404 g_outputBuffer="setTCPUseSinglePipe() cannot be used at runtime!\n";
1405 return;
1406 }
1407 setLuaSideEffect();
1408 g_useTCPSinglePipe = flag;
1409 });
1410
1411 g_lua.writeFunction("snmpAgent", [client](bool enableTraps, boost::optional<std::string> masterSocket) {
1412 if(client)
1413 return;
1414 #ifdef HAVE_NET_SNMP
1415 if (g_configurationDone) {
1416 errlog("snmpAgent() cannot be used at runtime!");
1417 g_outputBuffer="snmpAgent() cannot be used at runtime!\n";
1418 return;
1419 }
1420
1421 if (g_snmpEnabled) {
1422 errlog("snmpAgent() cannot be used twice!");
1423 g_outputBuffer="snmpAgent() cannot be used twice!\n";
1424 return;
1425 }
1426
1427 g_snmpEnabled = true;
1428 g_snmpTrapsEnabled = enableTraps;
1429 g_snmpAgent = new DNSDistSNMPAgent("dnsdist", masterSocket ? *masterSocket : std::string());
1430 #else
1431 errlog("NET SNMP support is required to use snmpAgent()");
1432 g_outputBuffer="NET SNMP support is required to use snmpAgent()\n";
1433 #endif /* HAVE_NET_SNMP */
1434 });
1435
1436 g_lua.writeFunction("sendCustomTrap", [](const std::string& str) {
1437 #ifdef HAVE_NET_SNMP
1438 if (g_snmpAgent && g_snmpTrapsEnabled) {
1439 g_snmpAgent->sendCustomTrap(str);
1440 }
1441 #endif /* HAVE_NET_SNMP */
1442 });
1443
1444 g_lua.writeFunction("setPoolServerPolicy", [](ServerPolicy policy, string pool) {
1445 setLuaSideEffect();
1446 auto localPools = g_pools.getCopy();
1447 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(policy));
1448 g_pools.setState(localPools);
1449 });
1450
1451 g_lua.writeFunction("setPoolServerPolicyLua", [](string name, policyfunc_t policy, string pool) {
1452 setLuaSideEffect();
1453 auto localPools = g_pools.getCopy();
1454 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, policy, true}));
1455 g_pools.setState(localPools);
1456 });
1457
1458 g_lua.writeFunction("showPoolServerPolicy", [](string pool) {
1459 setLuaSideEffect();
1460 auto localPools = g_pools.getCopy();
1461 auto poolObj = getPool(localPools, pool);
1462 if (poolObj->policy == nullptr) {
1463 g_outputBuffer=g_policy.getLocal()->name+"\n";
1464 } else {
1465 g_outputBuffer=poolObj->policy->name+"\n";
1466 }
1467 });
1468
1469 g_lua.writeFunction("setTCPDownstreamCleanupInterval", [](uint16_t interval) {
1470 setLuaSideEffect();
1471 g_downstreamTCPCleanupInterval = interval;
1472 });
1473
1474 g_lua.writeFunction("setConsoleConnectionsLogging", [](bool enabled) {
1475 g_logConsoleConnections = enabled;
1476 });
1477
1478 g_lua.writeFunction("setConsoleOutputMaxMsgSize", [](uint32_t size) {
1479 g_consoleOutputMsgMaxSize = size;
1480 });
1481
1482 g_lua.writeFunction("setUDPMultipleMessagesVectorSize", [](size_t vSize) {
1483 if (g_configurationDone) {
1484 errlog("setUDPMultipleMessagesVectorSize() cannot be used at runtime!");
1485 g_outputBuffer="setUDPMultipleMessagesVectorSize() cannot be used at runtime!\n";
1486 return;
1487 }
1488 #if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
1489 setLuaSideEffect();
1490 g_udpVectorSize = vSize;
1491 #else
1492 errlog("recvmmsg() support is not available!");
1493 g_outputBuffer="recvmmsg support is not available!\n";
1494 #endif
1495 });
1496
1497 g_lua.writeFunction("setAddEDNSToSelfGeneratedResponses", [](bool add) {
1498 g_addEDNSToSelfGeneratedResponses = add;
1499 });
1500
1501 g_lua.writeFunction("setPayloadSizeOnSelfGeneratedAnswers", [](uint16_t payloadSize) {
1502 if (payloadSize < 512) {
1503 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!");
1504 g_outputBuffer="setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!";
1505 payloadSize = 512;
1506 }
1507 if (payloadSize > s_udpIncomingBufferSize) {
1508 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to %d instead!", s_udpIncomingBufferSize);
1509 g_outputBuffer="setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to " + std::to_string(s_udpIncomingBufferSize) + " instead";
1510 payloadSize = s_udpIncomingBufferSize;
1511 }
1512 g_PayloadSizeSelfGenAnswers = payloadSize;
1513 });
1514
1515 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) {
1516 if (client)
1517 return;
1518 #ifdef HAVE_DNS_OVER_TLS
1519 setLuaSideEffect();
1520 if (g_configurationDone) {
1521 g_outputBuffer="addTLSLocal cannot be used at runtime!\n";
1522 return;
1523 }
1524 shared_ptr<TLSFrontend> frontend = std::make_shared<TLSFrontend>();
1525
1526 if (!loadTLSCertificateAndKeys(frontend, certFiles, keyFiles)) {
1527 return;
1528 }
1529
1530 if (vars) {
1531 bool doTCP = true;
1532 parseLocalBindVars(vars, doTCP, frontend->d_reusePort, frontend->d_tcpFastOpenQueueSize, frontend->d_interface, frontend->d_cpus);
1533
1534 if (vars->count("provider")) {
1535 frontend->d_provider = boost::get<const string>((*vars)["provider"]);
1536 }
1537
1538 if (vars->count("ciphers")) {
1539 frontend->d_ciphers = boost::get<const string>((*vars)["ciphers"]);
1540 }
1541
1542 if (vars->count("ticketKeyFile")) {
1543 frontend->d_ticketKeyFile = boost::get<const string>((*vars)["ticketKeyFile"]);
1544 }
1545
1546 if (vars->count("ticketsKeysRotationDelay")) {
1547 frontend->d_ticketsKeyRotationDelay = std::stoi(boost::get<const string>((*vars)["ticketsKeysRotationDelay"]));
1548 }
1549
1550 if (vars->count("numberOfTicketsKeys")) {
1551 frontend->d_numberOfTicketsKeys = std::stoi(boost::get<const string>((*vars)["numberOfTicketsKeys"]));
1552 }
1553
1554 if (vars->count("sessionTickets")) {
1555 frontend->d_enableTickets = boost::get<bool>((*vars)["sessionTickets"]);
1556 }
1557
1558 if (vars->count("numberOfStoredSessions")) {
1559 auto value = boost::get<int>((*vars)["numberOfStoredSessions"]);
1560 if (value < 0) {
1561 errlog("Invalid value '%d' for addTLSLocal() parameter 'numberOfStoredSessions', should be >= 0, dismissing", value);
1562 g_outputBuffer="Invalid value '" + std::to_string(value) + "' for addTLSLocal() parameter 'numberOfStoredSessions', should be >= 0, dimissing";
1563 return;
1564 }
1565 frontend->d_maxStoredSessions = value;
1566 }
1567 }
1568
1569 try {
1570 frontend->d_addr = ComboAddress(addr, 853);
1571 vinfolog("Loading TLS provider %s", frontend->d_provider);
1572 g_tlslocals.push_back(frontend); /// only works pre-startup, so no sync necessary
1573 }
1574 catch(const std::exception& e) {
1575 g_outputBuffer="Error: "+string(e.what())+"\n";
1576 }
1577 #else
1578 g_outputBuffer="DNS over TLS support is not present!\n";
1579 #endif
1580 });
1581
1582 g_lua.writeFunction("showTLSContexts", []() {
1583 #ifdef HAVE_DNS_OVER_TLS
1584 setLuaNoSideEffect();
1585 try {
1586 ostringstream ret;
1587 boost::format fmt("%1$-3d %2$-20.20s %|25t|%3$-14d %|40t|%4$-14d %|54t|%5$-21.21s");
1588 // 1 2 3 4 5
1589 ret << (fmt % "#" % "Address" % "# ticket keys" % "Rotation delay" % "Next rotation" ) << endl;
1590 size_t counter = 0;
1591 for (const auto& ctx : g_tlslocals) {
1592 ret << (fmt % counter % ctx->d_addr.toStringWithPort() % ctx->getTicketsKeysCount() % ctx->getTicketsKeyRotationDelay() % ctx->getNextTicketsKeyRotation()) << endl;
1593 counter++;
1594 }
1595 g_outputBuffer = ret.str();
1596 }
1597 catch(const std::exception& e) {
1598 g_outputBuffer = e.what();
1599 throw;
1600 }
1601 #else
1602 g_outputBuffer="DNS over TLS support is not present!\n";
1603 #endif
1604 });
1605
1606 g_lua.writeFunction("getTLSContext", [](size_t index) {
1607 std::shared_ptr<TLSCtx> result = nullptr;
1608 #ifdef HAVE_DNS_OVER_TLS
1609 setLuaNoSideEffect();
1610 try {
1611 if (index < g_tlslocals.size()) {
1612 result = g_tlslocals.at(index)->getContext();
1613 }
1614 else {
1615 errlog("Error: trying to get TLS context with index %zu but we only have %zu\n", index, g_tlslocals.size());
1616 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";
1617 }
1618 }
1619 catch(const std::exception& e) {
1620 g_outputBuffer="Error: "+string(e.what())+"\n";
1621 errlog("Error: %s\n", string(e.what()));
1622 }
1623 #else
1624 g_outputBuffer="DNS over TLS support is not present!\n";
1625 #endif
1626 return result;
1627 });
1628
1629 g_lua.writeFunction("getTLSFrontend", [](size_t index) {
1630 std::shared_ptr<TLSFrontend> result = nullptr;
1631 #ifdef HAVE_DNS_OVER_TLS
1632 setLuaNoSideEffect();
1633 try {
1634 if (index < g_tlslocals.size()) {
1635 result = g_tlslocals.at(index);
1636 }
1637 else {
1638 errlog("Error: trying to get TLS frontend with index %zu but we only have %zu\n", index, g_tlslocals.size());
1639 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";
1640 }
1641 }
1642 catch(const std::exception& e) {
1643 g_outputBuffer="Error: "+string(e.what())+"\n";
1644 errlog("Error: %s\n", string(e.what()));
1645 }
1646 #else
1647 g_outputBuffer="DNS over TLS support is not present!\n";
1648 #endif
1649 return result;
1650 });
1651
1652 g_lua.registerFunction<void(std::shared_ptr<TLSCtx>::*)()>("rotateTicketsKey", [](std::shared_ptr<TLSCtx> ctx) {
1653 if (ctx != nullptr) {
1654 ctx->rotateTicketsKey(time(nullptr));
1655 }
1656 });
1657
1658 g_lua.registerFunction<void(std::shared_ptr<TLSCtx>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<TLSCtx> ctx, const std::string& file) {
1659 if (ctx != nullptr) {
1660 ctx->loadTicketsKeys(file);
1661 }
1662 });
1663
1664 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) {
1665 #ifdef HAVE_DNS_OVER_TLS
1666 if (loadTLSCertificateAndKeys(frontend, certFiles, keyFiles)) {
1667 frontend->setupTLS();
1668 }
1669 #endif
1670 });
1671 }
1672
1673 vector<std::function<void(void)>> setupLua(bool client, const std::string& config)
1674 {
1675 g_launchWork= new vector<std::function<void(void)>>();
1676
1677 setupLuaActions();
1678 setupLuaConfig(client);
1679 setupLuaBindings(client);
1680 setupLuaBindingsDNSQuestion();
1681 setupLuaInspection();
1682 setupLuaRules();
1683 setupLuaVars();
1684
1685 std::ifstream ifs(config);
1686 if(!ifs)
1687 warnlog("Unable to read configuration from '%s'", config);
1688 else
1689 vinfolog("Read configuration from '%s'", config);
1690
1691 g_lua.executeCode(ifs);
1692
1693 auto ret = *g_launchWork;
1694 delete g_launchWork;
1695 g_launchWork = nullptr;
1696 return ret;
1697 }