2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
26 // for OpenBSD, sys/socket.h needs to come before net/if.h
27 #include <sys/socket.h>
30 #include <sys/types.h>
35 #include "dnsdist-console.hh"
36 #include "dnsdist-ecs.hh"
37 #include "dnsdist-lua.hh"
38 #include "dnsdist-rings.hh"
41 #include "dnswriter.hh"
44 #include "protobuf.hh"
45 #include "sodcrypto.hh"
47 #include <boost/logic/tribool.hpp>
48 #include <boost/lexical_cast.hpp>
51 #include <systemd/sd-daemon.h>
56 static vector
<std::function
<void(void)>>* g_launchWork
= nullptr;
58 boost::tribool g_noLuaSideEffect
;
59 static bool g_included
{false};
61 /* this is a best effort way to prevent logging calls with no side-effects in the output of delta()
62 Functions can declare setLuaNoSideEffect() and if nothing else does declare a side effect, or nothing
63 has done so before on this invocation, this call won't be part of delta() output */
64 void setLuaNoSideEffect()
66 if(g_noLuaSideEffect
==false) // there has been a side effect already
68 g_noLuaSideEffect
=true;
71 void setLuaSideEffect()
73 g_noLuaSideEffect
=false;
76 bool getLuaNoSideEffect()
78 if (g_noLuaSideEffect
) {
84 void resetLuaSideEffect()
86 g_noLuaSideEffect
= boost::logic::indeterminate
;
89 typedef std::unordered_map
<std::string
, boost::variant
<bool, int, std::string
, std::vector
<std::pair
<int,int> > > > localbind_t
;
91 static void parseLocalBindVars(boost::optional
<localbind_t
> vars
, bool& doTCP
, bool& reusePort
, int& tcpFastOpenQueueSize
, std::string
& interface
, std::set
<int>& cpus
)
94 if (vars
->count("doTCP")) {
95 doTCP
= boost::get
<bool>((*vars
)["doTCP"]);
97 if (vars
->count("reusePort")) {
98 reusePort
= boost::get
<bool>((*vars
)["reusePort"]);
100 if (vars
->count("tcpFastOpenQueueSize")) {
101 tcpFastOpenQueueSize
= boost::get
<int>((*vars
)["tcpFastOpenQueueSize"]);
103 if (vars
->count("interface")) {
104 interface
= boost::get
<std::string
>((*vars
)["interface"]);
106 if (vars
->count("cpus")) {
107 for (const auto cpu
: boost::get
<std::vector
<std::pair
<int,int>>>((*vars
)["cpus"])) {
108 cpus
.insert(cpu
.second
);
114 #ifdef HAVE_DNS_OVER_TLS
115 static bool loadTLSCertificateAndKeys(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
)
117 if (certFiles
.type() == typeid(std::string
) && keyFiles
.type() == typeid(std::string
)) {
118 auto certFile
= boost::get
<std::string
>(certFiles
);
119 auto keyFile
= boost::get
<std::string
>(keyFiles
);
120 frontend
->d_certKeyPairs
.clear();
121 frontend
->d_certKeyPairs
.push_back({certFile
, keyFile
});
123 else if (certFiles
.type() == typeid(std::vector
<std::pair
<int,std::string
>>) && keyFiles
.type() == typeid(std::vector
<std::pair
<int,std::string
>>))
125 auto certFilesVect
= boost::get
<std::vector
<std::pair
<int,std::string
>>>(certFiles
);
126 auto keyFilesVect
= boost::get
<std::vector
<std::pair
<int,std::string
>>>(keyFiles
);
127 if (certFilesVect
.size() == keyFilesVect
.size()) {
128 frontend
->d_certKeyPairs
.clear();
129 for (size_t idx
= 0; idx
< certFilesVect
.size(); idx
++) {
130 frontend
->d_certKeyPairs
.push_back({certFilesVect
.at(idx
).second
, keyFilesVect
.at(idx
).second
});
134 errlog("Error, mismatching number of certificates and keys in call to addTLSLocal()!");
135 g_outputBuffer
="Error, mismatching number of certificates and keys in call to addTLSLocal()!";
140 errlog("Error, mismatching number of certificates and keys in call to addTLSLocal()!");
141 g_outputBuffer
="Error, mismatching number of certificates and keys in call to addTLSLocal()!";
147 #endif /* HAVE_DNS_OVER_TLS */
149 void setupLuaConfig(bool client
)
151 typedef std::unordered_map
<std::string
, boost::variant
<bool, std::string
, vector
<pair
<int, std::string
> >, DownstreamState::checkfunc_t
> > newserver_t
;
153 g_lua
.writeFunction("inClientStartup", [client
]() {
154 return client
&& !g_configurationDone
;
157 g_lua
.writeFunction("newServer",
158 [client
](boost::variant
<string
,newserver_t
> pvars
, boost::optional
<int> qps
) {
161 std::shared_ptr
<DownstreamState
> ret
= std::make_shared
<DownstreamState
>(ComboAddress());
164 ComboAddress serverAddr
;
165 std::string serverAddressStr
;
166 if(auto addrStr
= boost::get
<string
>(&pvars
)) {
167 serverAddressStr
= *addrStr
;
169 vars
["qps"] = std::to_string(*qps
);
172 vars
= boost::get
<newserver_t
>(pvars
);
173 serverAddressStr
= boost::get
<string
>(vars
["address"]);
177 serverAddr
= ComboAddress(serverAddressStr
, 53);
179 catch(const PDNSException
& e
) {
180 g_outputBuffer
="Error creating new server: "+string(e
.reason
);
181 errlog("Error creating new server with address %s: %s", serverAddressStr
, e
.reason
);
184 catch(std::exception
& e
) {
185 g_outputBuffer
="Error creating new server: "+string(e
.what());
186 errlog("Error creating new server with address %s: %s", serverAddressStr
, e
.what());
190 if(IsAnyAddress(serverAddr
)) {
191 g_outputBuffer
="Error creating new server: invalid address for a downstream server.";
192 errlog("Error creating new server: %s is not a valid address for a downstream server", serverAddressStr
);
196 ComboAddress sourceAddr
;
197 unsigned int sourceItf
= 0;
198 size_t numberOfSockets
= 1;
201 if(vars
.count("source")) {
202 /* handle source in the following forms:
203 - v4 address ("192.0.2.1")
204 - v6 address ("2001:DB8::1")
205 - interface name ("eth0")
206 - v4 address and interface name ("192.0.2.1@eth0")
207 - v6 address and interface name ("2001:DB8::1@eth0")
209 const string source
= boost::get
<string
>(vars
["source"]);
211 std::string::size_type pos
= source
.find("@");
212 if (pos
== std::string::npos
) {
213 /* no '@', try to parse that as a valid v4/v6 address */
215 sourceAddr
= ComboAddress(source
);
225 /* try to parse as interface name, or v4/v6@itf */
226 string itfName
= source
.substr(pos
== std::string::npos
? 0 : pos
+ 1);
227 unsigned int itfIdx
= if_nametoindex(itfName
.c_str());
230 if (pos
== 0 || pos
== std::string::npos
) {
231 /* "eth0" or "@eth0" */
235 /* "192.0.2.1@eth0" */
236 sourceAddr
= ComboAddress(source
.substr(0, pos
));
242 warnlog("Dismissing source %s because '%s' is not a valid interface name", source
, itfName
);
247 if (vars
.count("sockets")) {
248 numberOfSockets
= std::stoul(boost::get
<string
>(vars
["sockets"]));
249 if (numberOfSockets
== 0) {
250 warnlog("Dismissing invalid number of sockets '%s', using 1 instead", boost::get
<string
>(vars
["sockets"]));
256 // do not construct DownstreamState now, it would try binding sockets.
259 ret
=std::make_shared
<DownstreamState
>(serverAddr
, sourceAddr
, sourceItf
, numberOfSockets
);
261 if(vars
.count("qps")) {
262 int qpsVal
=std::stoi(boost::get
<string
>(vars
["qps"]));
263 ret
->qps
=QPSLimiter(qpsVal
, qpsVal
);
266 if(vars
.count("order")) {
267 ret
->order
=std::stoi(boost::get
<string
>(vars
["order"]));
270 if(vars
.count("weight")) {
272 int weightVal
=std::stoi(boost::get
<string
>(vars
["weight"]));
275 errlog("Error creating new server: downstream weight value must be greater than 0.");
279 ret
->setWeight(weightVal
);
281 catch(std::exception
& e
) {
282 // std::stoi will throw an exception if the string isn't in a value int range
283 errlog("Error creating new server: downstream weight value must be between %s and %s", 1, std::numeric_limits
<int>::max());
288 if(vars
.count("retries")) {
289 ret
->retries
=std::stoi(boost::get
<string
>(vars
["retries"]));
292 if(vars
.count("tcpConnectTimeout")) {
293 ret
->tcpConnectTimeout
=std::stoi(boost::get
<string
>(vars
["tcpConnectTimeout"]));
296 if(vars
.count("tcpSendTimeout")) {
297 ret
->tcpSendTimeout
=std::stoi(boost::get
<string
>(vars
["tcpSendTimeout"]));
300 if(vars
.count("tcpRecvTimeout")) {
301 ret
->tcpRecvTimeout
=std::stoi(boost::get
<string
>(vars
["tcpRecvTimeout"]));
304 if(vars
.count("tcpFastOpen")) {
305 bool fastOpen
= boost::get
<bool>(vars
["tcpFastOpen"]);
308 ret
->tcpFastOpen
=true;
310 warnlog("TCP Fast Open has been configured on downstream server %s but is not supported", boost::get
<string
>(vars
["address"]));
315 if(vars
.count("name")) {
316 ret
->name
=boost::get
<string
>(vars
["name"]);
319 if (vars
.count("id")) {
320 ret
->setId(boost::lexical_cast
<boost::uuids::uuid
>(boost::get
<string
>(vars
["id"])));
323 if(vars
.count("checkName")) {
324 ret
->checkName
=DNSName(boost::get
<string
>(vars
["checkName"]));
327 if(vars
.count("checkType")) {
328 ret
->checkType
=boost::get
<string
>(vars
["checkType"]);
331 if(vars
.count("checkClass")) {
332 ret
->checkClass
=std::stoi(boost::get
<string
>(vars
["checkClass"]));
335 if(vars
.count("checkFunction")) {
336 ret
->checkFunction
= boost::get
<DownstreamState::checkfunc_t
>(vars
["checkFunction"]);
339 if(vars
.count("setCD")) {
340 ret
->setCD
=boost::get
<bool>(vars
["setCD"]);
343 if(vars
.count("mustResolve")) {
344 ret
->mustResolve
=boost::get
<bool>(vars
["mustResolve"]);
347 if(vars
.count("useClientSubnet")) {
348 ret
->useECS
=boost::get
<bool>(vars
["useClientSubnet"]);
351 if(vars
.count("ipBindAddrNoPort")) {
352 ret
->ipBindAddrNoPort
=boost::get
<bool>(vars
["ipBindAddrNoPort"]);
355 if(vars
.count("addXPF")) {
356 ret
->xpfRRCode
=std::stoi(boost::get
<string
>(vars
["addXPF"]));
359 if(vars
.count("maxCheckFailures")) {
360 ret
->maxCheckFailures
=std::stoi(boost::get
<string
>(vars
["maxCheckFailures"]));
363 if(vars
.count("cpus")) {
364 for (const auto cpu
: boost::get
<vector
<pair
<int,string
>>>(vars
["cpus"])) {
365 cpus
.insert(std::stoi(cpu
.second
));
369 /* this needs to be done _AFTER_ the order has been set,
370 since the server are kept ordered inside the pool */
371 auto localPools
= g_pools
.getCopy();
372 if(vars
.count("pool")) {
373 if(auto* pool
= boost::get
<string
>(&vars
["pool"])) {
374 ret
->pools
.insert(*pool
);
377 auto pools
= boost::get
<vector
<pair
<int, string
> > >(vars
["pool"]);
378 for(auto& p
: pools
) {
379 ret
->pools
.insert(p
.second
);
382 for(const auto& poolName
: ret
->pools
) {
383 addServerToPool(localPools
, poolName
, ret
);
387 addServerToPool(localPools
, "", ret
);
389 g_pools
.setState(localPools
);
391 if (ret
->connected
) {
392 ret
->threadStarted
.test_and_set();
395 g_launchWork
->push_back([ret
,cpus
]() {
396 ret
->tid
= thread(responderThread
, ret
);
398 mapThreadToCPUList(ret
->tid
.native_handle(), cpus
);
403 ret
->tid
= thread(responderThread
, ret
);
405 mapThreadToCPUList(ret
->tid
.native_handle(), cpus
);
410 auto states
= g_dstates
.getCopy();
411 states
.push_back(ret
);
412 std::stable_sort(states
.begin(), states
.end(), [](const decltype(ret
)& a
, const decltype(ret
)& b
) {
413 return a
->order
< b
->order
;
415 g_dstates
.setState(states
);
419 g_lua
.writeFunction("rmServer",
420 [](boost::variant
<std::shared_ptr
<DownstreamState
>, int> var
)
423 shared_ptr
<DownstreamState
> server
;
424 auto* rem
= boost::get
<shared_ptr
<DownstreamState
>>(&var
);
425 auto states
= g_dstates
.getCopy();
430 int idx
= boost::get
<int>(var
);
431 server
= states
.at(idx
);
433 auto localPools
= g_pools
.getCopy();
434 for (const string
& poolName
: server
->pools
) {
435 removeServerFromPool(localPools
, poolName
, server
);
437 /* the server might also be in the default pool */
438 removeServerFromPool(localPools
, "", server
);
439 g_pools
.setState(localPools
);
440 states
.erase(remove(states
.begin(), states
.end(), server
), states
.end());
441 g_dstates
.setState(states
);
444 g_lua
.writeFunction("setServerPolicy", [](ServerPolicy policy
) {
446 g_policy
.setState(policy
);
448 g_lua
.writeFunction("setServerPolicyLua", [](string name
, policyfunc_t policy
) {
450 g_policy
.setState(ServerPolicy
{name
, policy
, true});
453 g_lua
.writeFunction("showServerPolicy", []() {
455 g_outputBuffer
=g_policy
.getLocal()->name
+"\n";
458 g_lua
.writeFunction("truncateTC", [](bool tc
) { setLuaSideEffect(); g_truncateTC
=tc
; });
459 g_lua
.writeFunction("fixupCase", [](bool fu
) { setLuaSideEffect(); g_fixupCase
=fu
; });
461 g_lua
.writeFunction("addACL", [](const std::string
& domain
) {
463 g_ACL
.modify([domain
](NetmaskGroup
& nmg
) { nmg
.addMask(domain
); });
466 g_lua
.writeFunction("setLocal", [client
](const std::string
& addr
, boost::optional
<localbind_t
> vars
) {
470 if (g_configurationDone
) {
471 g_outputBuffer
="setLocal cannot be used at runtime!\n";
475 bool reusePort
= false;
476 int tcpFastOpenQueueSize
= 0;
477 std::string interface
;
480 parseLocalBindVars(vars
, doTCP
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
483 ComboAddress
loc(addr
, 53);
485 g_locals
.push_back(std::make_tuple(loc
, doTCP
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
)); /// only works pre-startup, so no sync necessary
487 catch(std::exception
& e
) {
488 g_outputBuffer
="Error: "+string(e
.what())+"\n";
492 g_lua
.writeFunction("addLocal", [client
](const std::string
& addr
, boost::optional
<localbind_t
> vars
) {
496 if (g_configurationDone
) {
497 g_outputBuffer
="addLocal cannot be used at runtime!\n";
501 bool reusePort
= false;
502 int tcpFastOpenQueueSize
= 0;
503 std::string interface
;
506 parseLocalBindVars(vars
, doTCP
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
509 ComboAddress
loc(addr
, 53);
510 g_locals
.push_back(std::make_tuple(loc
, doTCP
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
)); /// only works pre-startup, so no sync necessary
512 catch(std::exception
& e
) {
513 g_outputBuffer
="Error: "+string(e
.what())+"\n";
517 g_lua
.writeFunction("setACL", [](boost::variant
<string
,vector
<pair
<int, string
>>> inp
) {
520 if(auto str
= boost::get
<string
>(&inp
)) {
523 else for(const auto& p
: boost::get
<vector
<pair
<int,string
>>>(inp
)) {
524 nmg
.addMask(p
.second
);
529 g_lua
.writeFunction("showACL", []() {
530 setLuaNoSideEffect();
533 g_ACL
.getLocal()->toStringVector(&vec
);
535 for(const auto& s
: vec
)
536 g_outputBuffer
+=s
+"\n";
540 g_lua
.writeFunction("shutdown", []() {
542 sd_notify(0, "STOPPING=1");
543 #endif /* HAVE_SYSTEMD */
545 // Useful for debugging leaks, but might lead to race under load
546 // since other threads are still runing.
547 for(auto& frontend
: g_tlslocals
) {
552 google::protobuf::ShutdownProtobufLibrary();
553 #endif /* HAVE_PROTOBUF */
558 g_lua
.writeFunction("showServers", []() {
559 setLuaNoSideEffect();
562 boost::format
fmt("%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%" );
563 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14
564 ret
<< (fmt
% "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools") << endl
;
566 uint64_t totQPS
{0}, totQueries
{0}, totDrops
{0};
568 auto states
= g_dstates
.getLocal();
569 for(const auto& s
: *states
) {
570 string status
= s
->getStatus();
572 for(auto& p
: s
->pools
) {
578 ret
<< (fmt
% counter
% s
->name
% s
->remote
.toStringWithPort() %
580 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
;
582 totQPS
+= s
->queryLoad
;
583 totQueries
+= s
->queries
.load();
584 totDrops
+= s
->reuseds
.load();
587 ret
<< (fmt
% "All" % "" % "" % ""
589 (double)totQPS
% "" % "" % "" % totQueries
% totDrops
% "" % "" % "" % "" ) << endl
;
591 g_outputBuffer
=ret
.str();
592 }catch(std::exception
& e
) { g_outputBuffer
=e
.what(); throw; }
595 g_lua
.writeFunction("getServers", []() {
596 setLuaNoSideEffect();
597 vector
<pair
<int, std::shared_ptr
<DownstreamState
> > > ret
;
599 for(const auto& s
: g_dstates
.getCopy()) {
600 ret
.push_back(make_pair(count
++, s
));
605 g_lua
.writeFunction("getPoolServers", [](string pool
) {
606 return getDownstreamCandidates(g_pools
.getCopy(), pool
);
609 g_lua
.writeFunction("getServer", [client
](int i
) {
611 return std::make_shared
<DownstreamState
>(ComboAddress());
612 return g_dstates
.getCopy().at(i
);
615 g_lua
.writeFunction("carbonServer", [](const std::string
& address
, boost::optional
<string
> ourName
,
616 boost::optional
<unsigned int> interval
) {
618 auto ours
= g_carbon
.getCopy();
619 ours
.push_back({ComboAddress(address
, 2003), ourName
? *ourName
: "", interval
? *interval
: 30});
620 g_carbon
.setState(ours
);
623 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
) {
627 ComboAddress
local(address
);
629 int sock
= SSocket(local
.sin4
.sin_family
, SOCK_STREAM
, 0);
630 SSetsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, 1);
633 auto launch
=[sock
, local
, password
, apiKey
, customHeaders
]() {
634 setWebserverConfig(password
, apiKey
, customHeaders
);
635 thread
t(dnsdistWebserverThread
, sock
, local
);
639 g_launchWork
->push_back(launch
);
643 catch(std::exception
& e
) {
644 g_outputBuffer
="Unable to bind to webserver socket on " + local
.toStringWithPort() + ": " + e
.what();
645 errlog("Unable to bind to webserver socket on %s: %s", local
.toStringWithPort(), e
.what());
650 g_lua
.writeFunction("setWebserverConfig", [](const std::string
& password
, const boost::optional
<std::string
> apiKey
, const boost::optional
<std::map
<std::string
, std::string
> > customHeaders
) {
652 setWebserverConfig(password
, apiKey
, customHeaders
);
655 g_lua
.writeFunction("controlSocket", [client
](const std::string
& str
) {
657 ComboAddress
local(str
, 5199);
660 g_serverControl
= local
;
664 g_consoleEnabled
= true;
665 #ifdef HAVE_LIBSODIUM
666 if (g_configurationDone
&& g_consoleKey
.empty()) {
667 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");
672 int sock
= SSocket(local
.sin4
.sin_family
, SOCK_STREAM
, 0);
673 SSetsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, 1);
676 auto launch
=[sock
, local
]() {
677 thread
t(controlThread
, sock
, local
);
681 g_launchWork
->push_back(launch
);
686 catch(std::exception
& e
) {
687 g_outputBuffer
="Unable to bind to control socket on " + local
.toStringWithPort() + ": " + e
.what();
688 errlog("Unable to bind to control socket on %s: %s", local
.toStringWithPort(), e
.what());
692 g_lua
.writeFunction("addConsoleACL", [](const std::string
& netmask
) {
694 #ifndef HAVE_LIBSODIUM
695 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
698 g_consoleACL
.modify([netmask
](NetmaskGroup
& nmg
) { nmg
.addMask(netmask
); });
701 g_lua
.writeFunction("setConsoleACL", [](boost::variant
<string
,vector
<pair
<int, string
>>> inp
) {
704 #ifndef HAVE_LIBSODIUM
705 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
709 if(auto str
= boost::get
<string
>(&inp
)) {
712 else for(const auto& p
: boost::get
<vector
<pair
<int,string
>>>(inp
)) {
713 nmg
.addMask(p
.second
);
715 g_consoleACL
.setState(nmg
);
718 g_lua
.writeFunction("showConsoleACL", []() {
719 setLuaNoSideEffect();
721 #ifndef HAVE_LIBSODIUM
722 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
726 g_consoleACL
.getLocal()->toStringVector(&vec
);
728 for(const auto& s
: vec
) {
729 g_outputBuffer
+= s
+ "\n";
733 g_lua
.writeFunction("clearQueryCounters", []() {
734 unsigned int size
{0};
736 WriteLock
wl(&g_qcount
.queryLock
);
737 size
= g_qcount
.records
.size();
738 g_qcount
.records
.clear();
741 boost::format
fmt("%d records cleared from query counter buffer\n");
742 g_outputBuffer
= (fmt
% size
).str();
745 g_lua
.writeFunction("getQueryCounters", [](boost::optional
<unsigned int> optMax
) {
746 setLuaNoSideEffect();
747 ReadLock
rl(&g_qcount
.queryLock
);
748 g_outputBuffer
= "query counting is currently: ";
749 g_outputBuffer
+= g_qcount
.enabled
? "enabled" : "disabled";
750 g_outputBuffer
+= (boost::format(" (%d records in buffer)\n") % g_qcount
.records
.size()).str();
752 boost::format
fmt("%-3d %s: %d request(s)\n");
753 QueryCountRecords::iterator it
;
754 unsigned int max
= optMax
? *optMax
: 10;
755 unsigned int index
{1};
756 for(it
= g_qcount
.records
.begin(); it
!= g_qcount
.records
.end() && index
<= max
; ++it
, ++index
) {
757 g_outputBuffer
+= (fmt
% index
% it
->first
% it
->second
).str();
761 g_lua
.writeFunction("setQueryCount", [](bool enabled
) { g_qcount
.enabled
=enabled
; });
763 g_lua
.writeFunction("setQueryCountFilter", [](QueryCountFilter func
) {
764 g_qcount
.filter
= func
;
767 g_lua
.writeFunction("makeKey", []() {
768 setLuaNoSideEffect();
769 g_outputBuffer
="setKey("+newKey()+")\n";
772 g_lua
.writeFunction("setKey", [](const std::string
& key
) {
773 if(!g_configurationDone
&& ! g_consoleKey
.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
774 return; // but later setKeys() trump the -k value again
776 #ifndef HAVE_LIBSODIUM
777 warnlog("Calling setKey() while libsodium support has not been enabled is not secure, and will result in cleartext communications");
782 if(B64Decode(key
, newkey
) < 0) {
783 g_outputBuffer
=string("Unable to decode ")+key
+" as Base64";
784 errlog("%s", g_outputBuffer
);
790 g_lua
.writeFunction("testCrypto", [](boost::optional
<string
> optTestMsg
)
792 setLuaNoSideEffect();
793 #ifdef HAVE_LIBSODIUM
798 testmsg
= *optTestMsg
;
801 testmsg
= "testStringForCryptoTests";
807 string encrypted
= sodEncryptSym(testmsg
, g_consoleKey
, sn
);
808 string decrypted
= sodDecryptSym(encrypted
, g_consoleKey
, sn2
);
813 encrypted
= sodEncryptSym(testmsg
, g_consoleKey
, sn
);
814 decrypted
= sodDecryptSym(encrypted
, g_consoleKey
, sn2
);
816 if(testmsg
== decrypted
)
817 g_outputBuffer
="Everything is ok!\n";
819 g_outputBuffer
="Crypto failed..\n";
823 g_outputBuffer
="Crypto failed..\n";
826 g_outputBuffer
="Crypto not available.\n";
830 g_lua
.writeFunction("setTCPRecvTimeout", [](int timeout
) { g_tcpRecvTimeout
=timeout
; });
832 g_lua
.writeFunction("setTCPSendTimeout", [](int timeout
) { g_tcpSendTimeout
=timeout
; });
834 g_lua
.writeFunction("setUDPTimeout", [](int timeout
) { g_udpTimeout
=timeout
; });
836 g_lua
.writeFunction("setMaxUDPOutstanding", [](uint16_t max
) {
837 if (!g_configurationDone
) {
838 g_maxOutstanding
= max
;
840 g_outputBuffer
="Max UDP outstanding cannot be altered at runtime!\n";
844 g_lua
.writeFunction("setMaxTCPClientThreads", [](uint64_t max
) {
845 if (!g_configurationDone
) {
846 g_maxTCPClientThreads
= max
;
848 g_outputBuffer
="Maximum TCP client threads count cannot be altered at runtime!\n";
852 g_lua
.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max
) {
853 if (!g_configurationDone
) {
854 g_maxTCPQueuedConnections
= max
;
856 g_outputBuffer
="The maximum number of queued TCP connections cannot be altered at runtime!\n";
860 g_lua
.writeFunction("setMaxTCPQueriesPerConnection", [](size_t max
) {
861 if (!g_configurationDone
) {
862 g_maxTCPQueriesPerConn
= max
;
864 g_outputBuffer
="The maximum number of queries per TCP connection cannot be altered at runtime!\n";
868 g_lua
.writeFunction("setMaxTCPConnectionsPerClient", [](size_t max
) {
869 if (!g_configurationDone
) {
870 g_maxTCPConnectionsPerClient
= max
;
872 g_outputBuffer
="The maximum number of TCP connection per client cannot be altered at runtime!\n";
876 g_lua
.writeFunction("setMaxTCPConnectionDuration", [](size_t max
) {
877 if (!g_configurationDone
) {
878 g_maxTCPConnectionDuration
= max
;
880 g_outputBuffer
="The maximum duration of a TCP connection cannot be altered at runtime!\n";
884 g_lua
.writeFunction("setCacheCleaningDelay", [](uint32_t delay
) { g_cacheCleaningDelay
= delay
; });
886 g_lua
.writeFunction("setCacheCleaningPercentage", [](uint16_t percentage
) { if (percentage
< 100) g_cacheCleaningPercentage
= percentage
; else g_cacheCleaningPercentage
= 100; });
888 g_lua
.writeFunction("setECSSourcePrefixV4", [](uint16_t prefix
) { g_ECSSourcePrefixV4
=prefix
; });
890 g_lua
.writeFunction("setECSSourcePrefixV6", [](uint16_t prefix
) { g_ECSSourcePrefixV6
=prefix
; });
892 g_lua
.writeFunction("setECSOverride", [](bool override
) { g_ECSOverride
=override
; });
894 g_lua
.writeFunction("showDynBlocks", []() {
895 setLuaNoSideEffect();
896 auto slow
= g_dynblockNMG
.getCopy();
899 boost::format
fmt("%-24s %8d %8d %-10s %-20s %s\n");
900 g_outputBuffer
= (fmt
% "What" % "Seconds" % "Blocks" % "Warning" % "Action" % "Reason").str();
901 for(const auto& e
: slow
) {
902 if(now
< e
->second
.until
)
903 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();
905 auto slow2
= g_dynblockSMT
.getCopy();
906 slow2
.visit([&now
, &fmt
](const SuffixMatchTree
<DynBlock
>& node
) {
907 if(now
<node
.d_value
.until
) {
909 if(!node
.d_value
.domain
.empty())
910 dom
= node
.d_value
.domain
.toString();
911 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();
917 g_lua
.writeFunction("clearDynBlocks", []() {
920 g_dynblockNMG
.setState(nmg
);
921 SuffixMatchTree
<DynBlock
> smt
;
922 g_dynblockSMT
.setState(smt
);
925 g_lua
.writeFunction("addDynBlocks",
926 [](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
) {
931 auto slow
= g_dynblockNMG
.getCopy();
932 struct timespec until
, now
;
935 int actualSeconds
= seconds
? *seconds
: 10;
936 until
.tv_sec
+= actualSeconds
;
937 for(const auto& capair
: m
) {
938 unsigned int count
= 0;
939 auto got
= slow
.lookup(Netmask(capair
.first
));
942 if(until
< got
->second
.until
) // had a longer policy
944 if(now
< got
->second
.until
) // only inherit count on fresh query we are extending
945 count
=got
->second
.blocks
;
949 DynBlock db
{msg
,until
,DNSName(),(action
? *action
: DNSAction::Action::None
)};
952 warnlog("Inserting dynamic block for %s for %d seconds: %s", capair
.first
.toString(), actualSeconds
, msg
);
953 slow
.insert(Netmask(capair
.first
)).second
=db
;
955 g_dynblockNMG
.setState(slow
);
958 g_lua
.writeFunction("addDynBlockSMT",
959 [](const vector
<pair
<unsigned int, string
> >&names
, const std::string
& msg
, boost::optional
<int> seconds
, boost::optional
<DNSAction::Action
> action
) {
964 auto slow
= g_dynblockSMT
.getCopy();
965 struct timespec until
, now
;
968 int actualSeconds
= seconds
? *seconds
: 10;
969 until
.tv_sec
+= actualSeconds
;
971 for(const auto& capair
: names
) {
972 unsigned int count
= 0;
973 DNSName
domain(capair
.second
);
974 auto got
= slow
.lookup(domain
);
977 if(until
< got
->until
) // had a longer policy
979 if(now
< got
->until
) // only inherit count on fresh query we are extending
985 DynBlock db
{msg
,until
,domain
,(action
? *action
: DNSAction::Action::None
)};
988 warnlog("Inserting dynamic block for %s for %d seconds: %s", domain
, actualSeconds
, msg
);
989 slow
.add(domain
, db
);
991 g_dynblockSMT
.setState(slow
);
994 g_lua
.writeFunction("setDynBlocksAction", [](DNSAction::Action action
) {
995 if (!g_configurationDone
) {
996 if (action
== DNSAction::Action::Drop
|| action
== DNSAction::Action::NoOp
|| action
== DNSAction::Action::Nxdomain
|| action
== DNSAction::Action::Refused
|| action
== DNSAction::Action::Truncate
) {
997 g_dynBlockAction
= action
;
1000 errlog("Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused or Truncate!");
1001 g_outputBuffer
="Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused or Truncate!\n";
1004 g_outputBuffer
="Dynamic blocks action cannot be altered at runtime!\n";
1008 g_lua
.writeFunction("addDNSCryptBind", [](const std::string
& addr
, const std::string
& providerName
, const std::string
& certFile
, const std::string keyFile
, boost::optional
<localbind_t
> vars
) {
1009 if (g_configurationDone
) {
1010 g_outputBuffer
="addDNSCryptBind cannot be used at runtime!\n";
1013 #ifdef HAVE_DNSCRYPT
1015 bool reusePort
= false;
1016 int tcpFastOpenQueueSize
= 0;
1017 std::string interface
;
1020 parseLocalBindVars(vars
, doTCP
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
);
1023 auto ctx
= std::make_shared
<DNSCryptContext
>(providerName
, certFile
, keyFile
);
1024 g_dnsCryptLocals
.push_back(std::make_tuple(ComboAddress(addr
, 443), ctx
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
));
1026 catch(std::exception
& e
) {
1028 g_outputBuffer
="Error: "+string(e
.what())+"\n";
1031 g_outputBuffer
="Error: DNSCrypt support is not enabled.\n";
1036 g_lua
.writeFunction("showDNSCryptBinds", []() {
1037 setLuaNoSideEffect();
1038 #ifdef HAVE_DNSCRYPT
1040 boost::format
fmt("%1$-3d %2% %|25t|%3$-20.20s");
1041 ret
<< (fmt
% "#" % "Address" % "Provider Name") << endl
;
1044 for (const auto& local
: g_dnsCryptLocals
) {
1045 const std::shared_ptr
<DNSCryptContext
> ctx
= std::get
<1>(local
);
1046 ret
<< (fmt
% idx
% std::get
<0>(local
).toStringWithPort() % ctx
->getProviderName()) << endl
;
1050 g_outputBuffer
=ret
.str();
1052 g_outputBuffer
="Error: DNSCrypt support is not enabled.\n";
1056 g_lua
.writeFunction("getDNSCryptBind", [](size_t idx
) {
1057 setLuaNoSideEffect();
1058 #ifdef HAVE_DNSCRYPT
1059 std::shared_ptr
<DNSCryptContext
> ret
= nullptr;
1060 if (idx
< g_dnsCryptLocals
.size()) {
1061 ret
= std::get
<1>(g_dnsCryptLocals
.at(idx
));
1065 g_outputBuffer
="Error: DNSCrypt support is not enabled.\n";
1069 g_lua
.writeFunction("generateDNSCryptProviderKeys", [](const std::string
& publicKeyFile
, const std::string privateKeyFile
) {
1070 setLuaNoSideEffect();
1071 #ifdef HAVE_DNSCRYPT
1072 unsigned char publicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
];
1073 unsigned char privateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
];
1074 sodium_mlock(privateKey
, sizeof(privateKey
));
1077 DNSCryptContext::generateProviderKeys(publicKey
, privateKey
);
1079 ofstream
pubKStream(publicKeyFile
);
1080 pubKStream
.write((char*) publicKey
, sizeof(publicKey
));
1083 ofstream
privKStream(privateKeyFile
);
1084 privKStream
.write((char*) privateKey
, sizeof(privateKey
));
1085 privKStream
.close();
1087 g_outputBuffer
="Provider fingerprint is: " + DNSCryptContext::getProviderFingerprint(publicKey
) + "\n";
1089 catch(std::exception
& e
) {
1091 g_outputBuffer
="Error: "+string(e
.what())+"\n";
1094 sodium_memzero(privateKey
, sizeof(privateKey
));
1095 sodium_munlock(privateKey
, sizeof(privateKey
));
1097 g_outputBuffer
="Error: DNSCrypt support is not enabled.\n";
1101 g_lua
.writeFunction("printDNSCryptProviderFingerprint", [](const std::string
& publicKeyFile
) {
1102 setLuaNoSideEffect();
1103 #ifdef HAVE_DNSCRYPT
1104 unsigned char publicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
];
1107 ifstream
file(publicKeyFile
);
1108 file
.read((char *) &publicKey
, sizeof(publicKey
));
1111 throw std::runtime_error("Invalid dnscrypt provider public key file " + publicKeyFile
);
1114 g_outputBuffer
="Provider fingerprint is: " + DNSCryptContext::getProviderFingerprint(publicKey
) + "\n";
1116 catch(std::exception
& e
) {
1118 g_outputBuffer
="Error: "+string(e
.what())+"\n";
1121 g_outputBuffer
="Error: DNSCrypt support is not enabled.\n";
1125 #ifdef HAVE_DNSCRYPT
1126 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
) {
1127 setLuaNoSideEffect();
1128 DNSCryptPrivateKey privateKey
;
1132 if (generateDNSCryptCertificate(providerPrivateKeyFile
, serial
, begin
, end
, version
? *version
: DNSCryptExchangeVersion::VERSION1
, cert
, privateKey
)) {
1133 privateKey
.saveToFile(privateKeyFile
);
1134 DNSCryptContext::saveCertFromFile(cert
, certificateFile
);
1137 catch(const std::exception
& e
) {
1139 g_outputBuffer
="Error: "+string(e
.what())+"\n";
1144 g_lua
.writeFunction("showPools", []() {
1145 setLuaNoSideEffect();
1148 boost::format
fmt("%1$-20.20s %|25t|%2$20s %|25t|%3$20s %|50t|%4%" );
1150 ret
<< (fmt
% "Name" % "Cache" % "ServerPolicy" % "Servers" ) << endl
;
1152 const auto localPools
= g_pools
.getCopy();
1153 for (const auto& entry
: localPools
) {
1154 const string
& name
= entry
.first
;
1155 const std::shared_ptr
<ServerPool
> pool
= entry
.second
;
1156 string cache
= pool
->packetCache
!= nullptr ? pool
->packetCache
->toString() : "";
1157 string policy
= g_policy
.getLocal()->name
;
1158 if (pool
->policy
!= nullptr) {
1159 policy
= pool
->policy
->name
;
1163 for (const auto& server
: pool
->getServers()) {
1164 if (!servers
.empty()) {
1167 if (!server
.second
->name
.empty()) {
1168 servers
+= server
.second
->name
;
1171 servers
+= server
.second
->remote
.toStringWithPort();
1174 ret
<< (fmt
% name
% cache
% policy
% servers
) << endl
;
1176 g_outputBuffer
=ret
.str();
1177 }catch(std::exception
& e
) { g_outputBuffer
=e
.what(); throw; }
1180 g_lua
.writeFunction("getPool", [client
](const string
& poolName
) {
1182 return std::make_shared
<ServerPool
>();
1184 auto localPools
= g_pools
.getCopy();
1185 std::shared_ptr
<ServerPool
> pool
= createPoolIfNotExists(localPools
, poolName
);
1186 g_pools
.setState(localPools
);
1190 g_lua
.writeFunction("setVerboseHealthChecks", [](bool verbose
) { g_verboseHealthChecks
=verbose
; });
1191 g_lua
.writeFunction("setStaleCacheEntriesTTL", [](uint32_t ttl
) { g_staleCacheEntriesTTL
= ttl
; });
1193 g_lua
.writeFunction("showBinds", []() {
1194 setLuaNoSideEffect();
1197 boost::format
fmt("%1$-3d %2$-20.20s %|25t|%3$-8.8s %|35t|%4%" );
1199 ret
<< (fmt
% "#" % "Address" % "Protocol" % "Queries" ) << endl
;
1202 for (const auto& front
: g_frontends
) {
1203 ret
<< (fmt
% counter
% front
->local
.toStringWithPort() % (front
->udpFD
!= -1 ? "UDP" : "TCP") % front
->queries
) << endl
;
1206 g_outputBuffer
=ret
.str();
1207 }catch(std::exception
& e
) { g_outputBuffer
=e
.what(); throw; }
1210 g_lua
.writeFunction("getBind", [](size_t num
) {
1211 setLuaNoSideEffect();
1212 ClientState
* ret
= nullptr;
1213 if(num
< g_frontends
.size()) {
1214 ret
=g_frontends
[num
];
1219 g_lua
.writeFunction("help", [](boost::optional
<std::string
> command
) {
1220 setLuaNoSideEffect();
1221 g_outputBuffer
= "";
1222 for (const auto& keyword
: g_consoleKeywords
) {
1224 g_outputBuffer
+= keyword
.toString() + "\n";
1226 else if (keyword
.name
== command
) {
1227 g_outputBuffer
= keyword
.toString() + "\n";
1232 g_outputBuffer
= "Nothing found for " + *command
+ "\n";
1236 g_lua
.writeFunction("showVersion", []() {
1237 setLuaNoSideEffect();
1238 g_outputBuffer
= "dnsdist " + std::string(VERSION
) + "\n";
1242 g_lua
.writeFunction("setDefaultBPFFilter", [](std::shared_ptr
<BPFFilter
> bpf
) {
1243 if (g_configurationDone
) {
1244 g_outputBuffer
="setDefaultBPFFilter() cannot be used at runtime!\n";
1247 g_defaultBPFFilter
= bpf
;
1250 g_lua
.writeFunction("registerDynBPFFilter", [](std::shared_ptr
<DynBPFFilter
> dbpf
) {
1252 g_dynBPFFilters
.push_back(dbpf
);
1256 g_lua
.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr
<DynBPFFilter
> dbpf
) {
1258 for (auto it
= g_dynBPFFilters
.begin(); it
!= g_dynBPFFilters
.end(); it
++) {
1260 g_dynBPFFilters
.erase(it
);
1267 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
) {
1269 struct timespec until
, now
;
1270 clock_gettime(CLOCK_MONOTONIC
, &now
);
1272 int actualSeconds
= seconds
? *seconds
: 10;
1273 until
.tv_sec
+= actualSeconds
;
1274 for(const auto& capair
: m
) {
1275 if (dynbpf
->block(capair
.first
, until
)) {
1276 warnlog("Inserting eBPF dynamic block for %s for %d seconds: %s", capair
.first
.toString(), actualSeconds
, msg
? *msg
: "");
1281 #endif /* HAVE_EBPF */
1283 g_lua
.writeFunction
<std::unordered_map
<string
,uint64_t>()>("getStatisticsCounters", []() {
1284 setLuaNoSideEffect();
1285 std::unordered_map
<string
,uint64_t> res
;
1286 for(const auto& entry
: g_stats
.entries
) {
1287 if(const auto& val
= boost::get
<DNSDistStats::stat_t
*>(&entry
.second
))
1288 res
[entry
.first
] = (*val
)->load();
1293 g_lua
.writeFunction("includeDirectory", [](const std::string
& dirname
) {
1294 if (g_configurationDone
) {
1295 errlog("includeDirectory() cannot be used at runtime!");
1296 g_outputBuffer
="includeDirectory() cannot be used at runtime!\n";
1301 errlog("includeDirectory() cannot be used recursively!");
1302 g_outputBuffer
="includeDirectory() cannot be used recursively!\n";
1308 if (stat(dirname
.c_str(), &st
)) {
1309 errlog("The included directory %s does not exist!", dirname
.c_str());
1310 g_outputBuffer
="The included directory " + dirname
+ " does not exist!";
1314 if (!S_ISDIR(st
.st_mode
)) {
1315 errlog("The included directory %s is not a directory!", dirname
.c_str());
1316 g_outputBuffer
="The included directory " + dirname
+ " is not a directory!";
1322 std::list
<std::string
> files
;
1323 if (!(dirp
= opendir(dirname
.c_str()))) {
1324 errlog("Error opening the included directory %s!", dirname
.c_str());
1325 g_outputBuffer
="Error opening the included directory " + dirname
+ "!";
1329 while((ent
= readdir(dirp
)) != NULL
) {
1330 if (ent
->d_name
[0] == '.') {
1334 if (boost::ends_with(ent
->d_name
, ".conf")) {
1335 std::ostringstream namebuf
;
1336 namebuf
<< dirname
.c_str() << "/" << ent
->d_name
;
1338 if (stat(namebuf
.str().c_str(), &st
) || !S_ISREG(st
.st_mode
)) {
1342 files
.push_back(namebuf
.str());
1349 for (auto file
= files
.begin(); file
!= files
.end(); ++file
) {
1350 std::ifstream
ifs(*file
);
1352 warnlog("Unable to read configuration from '%s'", *file
);
1354 vinfolog("Read configuration from '%s'", *file
);
1357 g_lua
.executeCode(ifs
);
1363 g_lua
.writeFunction("setAPIWritable", [](bool writable
, boost::optional
<std::string
> apiConfigDir
) {
1365 g_apiReadWrite
= writable
;
1367 if (!(*apiConfigDir
).empty()) {
1368 g_apiConfigDirectory
= *apiConfigDir
;
1371 errlog("The API configuration directory value cannot be empty!");
1372 g_outputBuffer
="The API configuration directory value cannot be empty!";
1377 g_lua
.writeFunction("setServFailWhenNoServer", [](bool servfail
) {
1379 g_servFailOnNoPolicy
= servfail
;
1382 g_lua
.writeFunction("setRingBuffersSize", [](size_t capacity
, boost::optional
<size_t> numberOfShards
) {
1384 if (g_configurationDone
) {
1385 errlog("setRingBuffersSize() cannot be used at runtime!");
1386 g_outputBuffer
="setRingBuffersSize() cannot be used at runtime!\n";
1389 g_rings
.setCapacity(capacity
, numberOfShards
? *numberOfShards
: 1);
1392 g_lua
.writeFunction("setRingBuffersLockRetries", [](size_t retries
) {
1394 g_rings
.setNumberOfLockRetries(retries
);
1397 g_lua
.writeFunction("setWHashedPertubation", [](uint32_t pertub
) {
1399 g_hashperturb
= pertub
;
1402 g_lua
.writeFunction("setTCPUseSinglePipe", [](bool flag
) {
1403 if (g_configurationDone
) {
1404 g_outputBuffer
="setTCPUseSinglePipe() cannot be used at runtime!\n";
1408 g_useTCPSinglePipe
= flag
;
1411 g_lua
.writeFunction("snmpAgent", [client
](bool enableTraps
, boost::optional
<std::string
> masterSocket
) {
1414 #ifdef HAVE_NET_SNMP
1415 if (g_configurationDone
) {
1416 errlog("snmpAgent() cannot be used at runtime!");
1417 g_outputBuffer
="snmpAgent() cannot be used at runtime!\n";
1421 if (g_snmpEnabled
) {
1422 errlog("snmpAgent() cannot be used twice!");
1423 g_outputBuffer
="snmpAgent() cannot be used twice!\n";
1427 g_snmpEnabled
= true;
1428 g_snmpTrapsEnabled
= enableTraps
;
1429 g_snmpAgent
= new DNSDistSNMPAgent("dnsdist", masterSocket
? *masterSocket
: std::string());
1431 errlog("NET SNMP support is required to use snmpAgent()");
1432 g_outputBuffer
="NET SNMP support is required to use snmpAgent()\n";
1433 #endif /* HAVE_NET_SNMP */
1436 g_lua
.writeFunction("sendCustomTrap", [](const std::string
& str
) {
1437 #ifdef HAVE_NET_SNMP
1438 if (g_snmpAgent
&& g_snmpTrapsEnabled
) {
1439 g_snmpAgent
->sendCustomTrap(str
);
1441 #endif /* HAVE_NET_SNMP */
1444 g_lua
.writeFunction("setPoolServerPolicy", [](ServerPolicy policy
, string pool
) {
1446 auto localPools
= g_pools
.getCopy();
1447 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(policy
));
1448 g_pools
.setState(localPools
);
1451 g_lua
.writeFunction("setPoolServerPolicyLua", [](string name
, policyfunc_t policy
, string pool
) {
1453 auto localPools
= g_pools
.getCopy();
1454 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(ServerPolicy
{name
, policy
, true}));
1455 g_pools
.setState(localPools
);
1458 g_lua
.writeFunction("showPoolServerPolicy", [](string pool
) {
1460 auto localPools
= g_pools
.getCopy();
1461 auto poolObj
= getPool(localPools
, pool
);
1462 if (poolObj
->policy
== nullptr) {
1463 g_outputBuffer
=g_policy
.getLocal()->name
+"\n";
1465 g_outputBuffer
=poolObj
->policy
->name
+"\n";
1469 g_lua
.writeFunction("setTCPDownstreamCleanupInterval", [](uint16_t interval
) {
1471 g_downstreamTCPCleanupInterval
= interval
;
1474 g_lua
.writeFunction("setConsoleConnectionsLogging", [](bool enabled
) {
1475 g_logConsoleConnections
= enabled
;
1478 g_lua
.writeFunction("setConsoleOutputMaxMsgSize", [](uint32_t size
) {
1479 g_consoleOutputMsgMaxSize
= size
;
1482 g_lua
.writeFunction("setUDPMultipleMessagesVectorSize", [](size_t vSize
) {
1483 if (g_configurationDone
) {
1484 errlog("setUDPMultipleMessagesVectorSize() cannot be used at runtime!");
1485 g_outputBuffer
="setUDPMultipleMessagesVectorSize() cannot be used at runtime!\n";
1488 #if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
1490 g_udpVectorSize
= vSize
;
1492 errlog("recvmmsg() support is not available!");
1493 g_outputBuffer
="recvmmsg support is not available!\n";
1497 g_lua
.writeFunction("setAddEDNSToSelfGeneratedResponses", [](bool add
) {
1498 g_addEDNSToSelfGeneratedResponses
= add
;
1501 g_lua
.writeFunction("setPayloadSizeOnSelfGeneratedAnswers", [](uint16_t payloadSize
) {
1502 if (payloadSize
< 512) {
1503 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!");
1504 g_outputBuffer
="setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!";
1507 if (payloadSize
> s_udpIncomingBufferSize
) {
1508 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to %d instead!", s_udpIncomingBufferSize
);
1509 g_outputBuffer
="setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to " + std::to_string(s_udpIncomingBufferSize
) + " instead";
1510 payloadSize
= s_udpIncomingBufferSize
;
1512 g_PayloadSizeSelfGenAnswers
= payloadSize
;
1515 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
) {
1518 #ifdef HAVE_DNS_OVER_TLS
1520 if (g_configurationDone
) {
1521 g_outputBuffer
="addTLSLocal cannot be used at runtime!\n";
1524 shared_ptr
<TLSFrontend
> frontend
= std::make_shared
<TLSFrontend
>();
1526 if (!loadTLSCertificateAndKeys(frontend
, certFiles
, keyFiles
)) {
1532 parseLocalBindVars(vars
, doTCP
, frontend
->d_reusePort
, frontend
->d_tcpFastOpenQueueSize
, frontend
->d_interface
, frontend
->d_cpus
);
1534 if (vars
->count("provider")) {
1535 frontend
->d_provider
= boost::get
<const string
>((*vars
)["provider"]);
1538 if (vars
->count("ciphers")) {
1539 frontend
->d_ciphers
= boost::get
<const string
>((*vars
)["ciphers"]);
1542 if (vars
->count("ticketKeyFile")) {
1543 frontend
->d_ticketKeyFile
= boost::get
<const string
>((*vars
)["ticketKeyFile"]);
1546 if (vars
->count("ticketsKeysRotationDelay")) {
1547 frontend
->d_ticketsKeyRotationDelay
= std::stoi(boost::get
<const string
>((*vars
)["ticketsKeysRotationDelay"]));
1550 if (vars
->count("numberOfTicketsKeys")) {
1551 frontend
->d_numberOfTicketsKeys
= std::stoi(boost::get
<const string
>((*vars
)["numberOfTicketsKeys"]));
1554 if (vars
->count("sessionTickets")) {
1555 frontend
->d_enableTickets
= boost::get
<bool>((*vars
)["sessionTickets"]);
1558 if (vars
->count("numberOfStoredSessions")) {
1559 auto value
= boost::get
<int>((*vars
)["numberOfStoredSessions"]);
1561 errlog("Invalid value '%d' for addTLSLocal() parameter 'numberOfStoredSessions', should be >= 0, dismissing", value
);
1562 g_outputBuffer
="Invalid value '" + std::to_string(value
) + "' for addTLSLocal() parameter 'numberOfStoredSessions', should be >= 0, dimissing";
1565 frontend
->d_maxStoredSessions
= value
;
1570 frontend
->d_addr
= ComboAddress(addr
, 853);
1571 vinfolog("Loading TLS provider %s", frontend
->d_provider
);
1572 g_tlslocals
.push_back(frontend
); /// only works pre-startup, so no sync necessary
1574 catch(const std::exception
& e
) {
1575 g_outputBuffer
="Error: "+string(e
.what())+"\n";
1578 g_outputBuffer
="DNS over TLS support is not present!\n";
1582 g_lua
.writeFunction("showTLSContexts", []() {
1583 #ifdef HAVE_DNS_OVER_TLS
1584 setLuaNoSideEffect();
1587 boost::format
fmt("%1$-3d %2$-20.20s %|25t|%3$-14d %|40t|%4$-14d %|54t|%5$-21.21s");
1589 ret
<< (fmt
% "#" % "Address" % "# ticket keys" % "Rotation delay" % "Next rotation" ) << endl
;
1591 for (const auto& ctx
: g_tlslocals
) {
1592 ret
<< (fmt
% counter
% ctx
->d_addr
.toStringWithPort() % ctx
->getTicketsKeysCount() % ctx
->getTicketsKeyRotationDelay() % ctx
->getNextTicketsKeyRotation()) << endl
;
1595 g_outputBuffer
= ret
.str();
1597 catch(const std::exception
& e
) {
1598 g_outputBuffer
= e
.what();
1602 g_outputBuffer
="DNS over TLS support is not present!\n";
1606 g_lua
.writeFunction("getTLSContext", [](size_t index
) {
1607 std::shared_ptr
<TLSCtx
> result
= nullptr;
1608 #ifdef HAVE_DNS_OVER_TLS
1609 setLuaNoSideEffect();
1611 if (index
< g_tlslocals
.size()) {
1612 result
= g_tlslocals
.at(index
)->getContext();
1615 errlog("Error: trying to get TLS context with index %zu but we only have %zu\n", index
, g_tlslocals
.size());
1616 g_outputBuffer
="Error: trying to get TLS context with index " + std::to_string(index
) + " but we only have " + std::to_string(g_tlslocals
.size()) + "\n";
1619 catch(const std::exception
& e
) {
1620 g_outputBuffer
="Error: "+string(e
.what())+"\n";
1621 errlog("Error: %s\n", string(e
.what()));
1624 g_outputBuffer
="DNS over TLS support is not present!\n";
1629 g_lua
.writeFunction("getTLSFrontend", [](size_t index
) {
1630 std::shared_ptr
<TLSFrontend
> result
= nullptr;
1631 #ifdef HAVE_DNS_OVER_TLS
1632 setLuaNoSideEffect();
1634 if (index
< g_tlslocals
.size()) {
1635 result
= g_tlslocals
.at(index
);
1638 errlog("Error: trying to get TLS frontend with index %zu but we only have %zu\n", index
, g_tlslocals
.size());
1639 g_outputBuffer
="Error: trying to get TLS frontend with index " + std::to_string(index
) + " but we only have " + std::to_string(g_tlslocals
.size()) + "\n";
1642 catch(const std::exception
& e
) {
1643 g_outputBuffer
="Error: "+string(e
.what())+"\n";
1644 errlog("Error: %s\n", string(e
.what()));
1647 g_outputBuffer
="DNS over TLS support is not present!\n";
1652 g_lua
.registerFunction
<void(std::shared_ptr
<TLSCtx
>::*)()>("rotateTicketsKey", [](std::shared_ptr
<TLSCtx
> ctx
) {
1653 if (ctx
!= nullptr) {
1654 ctx
->rotateTicketsKey(time(nullptr));
1658 g_lua
.registerFunction
<void(std::shared_ptr
<TLSCtx
>::*)(const std::string
&)>("loadTicketsKeys", [](std::shared_ptr
<TLSCtx
> ctx
, const std::string
& file
) {
1659 if (ctx
!= nullptr) {
1660 ctx
->loadTicketsKeys(file
);
1664 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
) {
1665 #ifdef HAVE_DNS_OVER_TLS
1666 if (loadTLSCertificateAndKeys(frontend
, certFiles
, keyFiles
)) {
1667 frontend
->setupTLS();
1673 vector
<std::function
<void(void)>> setupLua(bool client
, const std::string
& config
)
1675 g_launchWork
= new vector
<std::function
<void(void)>>();
1678 setupLuaConfig(client
);
1679 setupLuaBindings(client
);
1680 setupLuaBindingsDNSQuestion();
1681 setupLuaInspection();
1685 std::ifstream
ifs(config
);
1687 warnlog("Unable to read configuration from '%s'", config
);
1689 vinfolog("Read configuration from '%s'", config
);
1691 g_lua
.executeCode(ifs
);
1693 auto ret
= *g_launchWork
;
1694 delete g_launchWork
;
1695 g_launchWork
= nullptr;