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