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