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