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