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