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