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