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