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-healthchecks.hh"
38 #include "dnsdist-lua.hh"
40 #include "dnsdist-lua-ffi.hh"
41 #endif /* LUAJIT_VERSION */
42 #include "dnsdist-rings.hh"
43 #include "dnsdist-secpoll.hh"
44 #include "dnsdist-web.hh"
47 #include "dnswriter.hh"
50 #include "protobuf.hh"
51 #include "sodcrypto.hh"
57 #include <boost/logic/tribool.hpp>
58 #include <boost/lexical_cast.hpp>
61 #include <systemd/sd-daemon.h>
66 static vector
<std::function
<void(void)>>* g_launchWork
= nullptr;
68 boost::tribool g_noLuaSideEffect
;
69 static bool g_included
{false};
71 /* this is a best effort way to prevent logging calls with no side-effects in the output of delta()
72 Functions can declare setLuaNoSideEffect() and if nothing else does declare a side effect, or nothing
73 has done so before on this invocation, this call won't be part of delta() output */
74 void setLuaNoSideEffect()
76 if(g_noLuaSideEffect
==false) // there has been a side effect already
78 g_noLuaSideEffect
=true;
81 void setLuaSideEffect()
83 g_noLuaSideEffect
=false;
86 bool getLuaNoSideEffect()
88 if (g_noLuaSideEffect
) {
94 void resetLuaSideEffect()
96 g_noLuaSideEffect
= boost::logic::indeterminate
;
99 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
;
101 static void parseLocalBindVars(boost::optional
<localbind_t
> vars
, bool& reusePort
, int& tcpFastOpenQueueSize
, std::string
& interface
, std::set
<int>& cpus
, int& tcpListenQueueSize
)
104 if (vars
->count("reusePort")) {
105 reusePort
= boost::get
<bool>((*vars
)["reusePort"]);
107 if (vars
->count("tcpFastOpenQueueSize")) {
108 tcpFastOpenQueueSize
= boost::get
<int>((*vars
)["tcpFastOpenQueueSize"]);
110 if (vars
->count("tcpListenQueueSize")) {
111 tcpListenQueueSize
= boost::get
<int>((*vars
)["tcpListenQueueSize"]);
113 if (vars
->count("interface")) {
114 interface
= boost::get
<std::string
>((*vars
)["interface"]);
116 if (vars
->count("cpus")) {
117 for (const auto& cpu
: boost::get
<std::vector
<std::pair
<int,int>>>((*vars
)["cpus"])) {
118 cpus
.insert(cpu
.second
);
124 #if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
125 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
)
127 if (certFiles
.type() == typeid(std::string
) && keyFiles
.type() == typeid(std::string
)) {
128 auto certFile
= boost::get
<std::string
>(certFiles
);
129 auto keyFile
= boost::get
<std::string
>(keyFiles
);
131 pairs
.push_back({certFile
, keyFile
});
133 else if (certFiles
.type() == typeid(std::vector
<std::pair
<int,std::string
>>) && keyFiles
.type() == typeid(std::vector
<std::pair
<int,std::string
>>))
135 auto certFilesVect
= boost::get
<std::vector
<std::pair
<int,std::string
>>>(certFiles
);
136 auto keyFilesVect
= boost::get
<std::vector
<std::pair
<int,std::string
>>>(keyFiles
);
137 if (certFilesVect
.size() == keyFilesVect
.size()) {
139 for (size_t idx
= 0; idx
< certFilesVect
.size(); idx
++) {
140 pairs
.push_back({certFilesVect
.at(idx
).second
, keyFilesVect
.at(idx
).second
});
144 errlog("Error, mismatching number of certificates and keys in call to %s()!", context
);
145 g_outputBuffer
="Error, mismatching number of certificates and keys in call to " + context
+ "()!";
150 errlog("Error, mismatching number of certificates and keys in call to %s()!", context
);
151 g_outputBuffer
="Error, mismatching number of certificates and keys in call to " + context
+ "()!";
158 static void parseTLSConfig(TLSConfig
& config
, const std::string
& context
, boost::optional
<localbind_t
> vars
)
160 if (vars
->count("ciphers")) {
161 config
.d_ciphers
= boost::get
<const string
>((*vars
)["ciphers"]);
164 if (vars
->count("ciphersTLS13")) {
165 config
.d_ciphers13
= boost::get
<const string
>((*vars
)["ciphersTLS13"]);
169 if (vars
->count("minTLSVersion")) {
170 config
.d_minTLSVersion
= libssl_tls_version_from_string(boost::get
<const string
>((*vars
)["minTLSVersion"]));
172 #endif /* HAVE_LIBSSL */
174 if (vars
->count("ticketKeyFile")) {
175 config
.d_ticketKeyFile
= boost::get
<const string
>((*vars
)["ticketKeyFile"]);
178 if (vars
->count("ticketsKeysRotationDelay")) {
179 config
.d_ticketsKeyRotationDelay
= boost::get
<int>((*vars
)["ticketsKeysRotationDelay"]);
182 if (vars
->count("numberOfTicketsKeys")) {
183 config
.d_numberOfTicketsKeys
= boost::get
<int>((*vars
)["numberOfTicketsKeys"]);
186 if (vars
->count("preferServerCiphers")) {
187 config
.d_preferServerCiphers
= boost::get
<bool>((*vars
)["preferServerCiphers"]);
190 if (vars
->count("sessionTimeout")) {
191 config
.d_sessionTimeout
= boost::get
<int>((*vars
)["sessionTimeout"]);
194 if (vars
->count("sessionTickets")) {
195 config
.d_enableTickets
= boost::get
<bool>((*vars
)["sessionTickets"]);
198 if (vars
->count("numberOfStoredSessions")) {
199 auto value
= boost::get
<int>((*vars
)["numberOfStoredSessions"]);
201 errlog("Invalid value '%d' for %s() parameter 'numberOfStoredSessions', should be >= 0, dismissing", value
, context
);
202 g_outputBuffer
="Invalid value '" + std::to_string(value
) + "' for " + context
+ "() parameter 'numberOfStoredSessions', should be >= 0, dimissing";
204 config
.d_maxStoredSessions
= value
;
207 if (vars
->count("ocspResponses")) {
208 auto files
= boost::get
<std::vector
<std::pair
<int, std::string
>>>((*vars
)["ocspResponses"]);
209 for (const auto& file
: files
) {
210 config
.d_ocspFiles
.push_back(file
.second
);
214 if (vars
->count("keyLogFile")) {
215 #ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
216 config
.d_keyLogFile
= boost::get
<const string
>((*vars
)["keyLogFile"]);
218 errlog("TLS Key logging has been enabled using the 'keyLogFile' parameter to %s(), but this version of OpenSSL does not support it", context
);
219 g_outputBuffer
= "TLS Key logging has been enabled using the 'keyLogFile' parameter to " + context
+ "(), but this version of OpenSSL does not support it";
224 #endif // defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
226 static void setupLuaConfig(bool client
, bool configCheck
)
228 typedef std::unordered_map
<std::string
, boost::variant
<bool, std::string
, vector
<pair
<int, std::string
> >, DownstreamState::checkfunc_t
> > newserver_t
;
229 g_lua
.writeFunction("inClientStartup", [client
]() {
230 return client
&& !g_configurationDone
;
233 g_lua
.writeFunction("inConfigCheck", [configCheck
]() {
237 g_lua
.writeFunction("newServer",
238 [client
, configCheck
](boost::variant
<string
,newserver_t
> pvars
, boost::optional
<int> qps
) {
241 std::shared_ptr
<DownstreamState
> ret
= std::make_shared
<DownstreamState
>(ComboAddress());
244 ComboAddress serverAddr
;
245 std::string serverAddressStr
;
246 if(auto addrStr
= boost::get
<string
>(&pvars
)) {
247 serverAddressStr
= *addrStr
;
249 vars
["qps"] = std::to_string(*qps
);
252 vars
= boost::get
<newserver_t
>(pvars
);
253 serverAddressStr
= boost::get
<string
>(vars
["address"]);
257 serverAddr
= ComboAddress(serverAddressStr
, 53);
259 catch(const PDNSException
& e
) {
260 g_outputBuffer
="Error creating new server: "+string(e
.reason
);
261 errlog("Error creating new server with address %s: %s", serverAddressStr
, e
.reason
);
264 catch(std::exception
& e
) {
265 g_outputBuffer
="Error creating new server: "+string(e
.what());
266 errlog("Error creating new server with address %s: %s", serverAddressStr
, e
.what());
270 if(IsAnyAddress(serverAddr
)) {
271 g_outputBuffer
="Error creating new server: invalid address for a downstream server.";
272 errlog("Error creating new server: %s is not a valid address for a downstream server", serverAddressStr
);
276 ComboAddress sourceAddr
;
277 std::string sourceItfName
;
278 unsigned int sourceItf
= 0;
279 size_t numberOfSockets
= 1;
282 if(vars
.count("source")) {
283 /* handle source in the following forms:
284 - v4 address ("192.0.2.1")
285 - v6 address ("2001:DB8::1")
286 - interface name ("eth0")
287 - v4 address and interface name ("192.0.2.1@eth0")
288 - v6 address and interface name ("2001:DB8::1@eth0")
290 const string source
= boost::get
<string
>(vars
["source"]);
292 std::string::size_type pos
= source
.find("@");
293 if (pos
== std::string::npos
) {
294 /* no '@', try to parse that as a valid v4/v6 address */
296 sourceAddr
= ComboAddress(source
);
306 /* try to parse as interface name, or v4/v6@itf */
307 sourceItfName
= source
.substr(pos
== std::string::npos
? 0 : pos
+ 1);
308 unsigned int itfIdx
= if_nametoindex(sourceItfName
.c_str());
311 if (pos
== 0 || pos
== std::string::npos
) {
312 /* "eth0" or "@eth0" */
316 /* "192.0.2.1@eth0" */
317 sourceAddr
= ComboAddress(source
.substr(0, pos
));
320 #ifdef SO_BINDTODEVICE
321 /* we need to retain CAP_NET_RAW to be able to set SO_BINDTODEVICE in the health checks */
322 g_capabilitiesToRetain
.insert("CAP_NET_RAW");
327 warnlog("Dismissing source %s because '%s' is not a valid interface name", source
, sourceItfName
);
332 if (vars
.count("sockets")) {
333 numberOfSockets
= std::stoul(boost::get
<string
>(vars
["sockets"]));
334 if (numberOfSockets
== 0) {
335 warnlog("Dismissing invalid number of sockets '%s', using 1 instead", boost::get
<string
>(vars
["sockets"]));
340 // create but don't connect the socket in client or check-config modes
341 ret
=std::make_shared
<DownstreamState
>(serverAddr
, sourceAddr
, sourceItf
, sourceItfName
, numberOfSockets
, !(client
|| configCheck
));
342 if (!(client
|| configCheck
)) {
343 infolog("Added downstream server %s", serverAddr
.toStringWithPort());
346 if(vars
.count("qps")) {
347 int qpsVal
=std::stoi(boost::get
<string
>(vars
["qps"]));
348 ret
->qps
=QPSLimiter(qpsVal
, qpsVal
);
351 if(vars
.count("order")) {
352 ret
->order
=std::stoi(boost::get
<string
>(vars
["order"]));
355 if(vars
.count("weight")) {
357 int weightVal
=std::stoi(boost::get
<string
>(vars
["weight"]));
360 errlog("Error creating new server: downstream weight value must be greater than 0.");
364 ret
->setWeight(weightVal
);
366 catch(std::exception
& e
) {
367 // std::stoi will throw an exception if the string isn't in a value int range
368 errlog("Error creating new server: downstream weight value must be between %s and %s", 1, std::numeric_limits
<int>::max());
373 if(vars
.count("retries")) {
374 ret
->retries
=std::stoi(boost::get
<string
>(vars
["retries"]));
377 if(vars
.count("checkInterval")) {
378 ret
->checkInterval
=static_cast<unsigned int>(std::stoul(boost::get
<string
>(vars
["checkInterval"])));
381 if(vars
.count("tcpConnectTimeout")) {
382 ret
->tcpConnectTimeout
=std::stoi(boost::get
<string
>(vars
["tcpConnectTimeout"]));
385 if(vars
.count("tcpSendTimeout")) {
386 ret
->tcpSendTimeout
=std::stoi(boost::get
<string
>(vars
["tcpSendTimeout"]));
389 if(vars
.count("tcpRecvTimeout")) {
390 ret
->tcpRecvTimeout
=std::stoi(boost::get
<string
>(vars
["tcpRecvTimeout"]));
393 if(vars
.count("tcpFastOpen")) {
394 bool fastOpen
= boost::get
<bool>(vars
["tcpFastOpen"]);
397 ret
->tcpFastOpen
=true;
399 warnlog("TCP Fast Open has been configured on downstream server %s but is not supported", boost::get
<string
>(vars
["address"]));
404 if(vars
.count("name")) {
405 ret
->setName(boost::get
<string
>(vars
["name"]));
408 if (vars
.count("id")) {
409 ret
->setId(boost::lexical_cast
<boost::uuids::uuid
>(boost::get
<string
>(vars
["id"])));
412 if(vars
.count("checkName")) {
413 ret
->checkName
=DNSName(boost::get
<string
>(vars
["checkName"]));
416 if(vars
.count("checkType")) {
417 ret
->checkType
=boost::get
<string
>(vars
["checkType"]);
420 if(vars
.count("checkClass")) {
421 ret
->checkClass
=std::stoi(boost::get
<string
>(vars
["checkClass"]));
424 if(vars
.count("checkFunction")) {
425 ret
->checkFunction
= boost::get
<DownstreamState::checkfunc_t
>(vars
["checkFunction"]);
428 if(vars
.count("checkTimeout")) {
429 ret
->checkTimeout
= std::stoi(boost::get
<string
>(vars
["checkTimeout"]));
432 if(vars
.count("setCD")) {
433 ret
->setCD
=boost::get
<bool>(vars
["setCD"]);
436 if(vars
.count("mustResolve")) {
437 ret
->mustResolve
=boost::get
<bool>(vars
["mustResolve"]);
440 if(vars
.count("useClientSubnet")) {
441 ret
->useECS
=boost::get
<bool>(vars
["useClientSubnet"]);
444 if(vars
.count("useProxyProtocol")) {
445 ret
->useProxyProtocol
= boost::get
<bool>(vars
["useProxyProtocol"]);
448 if(vars
.count("disableZeroScope")) {
449 ret
->disableZeroScope
=boost::get
<bool>(vars
["disableZeroScope"]);
452 if(vars
.count("ipBindAddrNoPort")) {
453 ret
->ipBindAddrNoPort
=boost::get
<bool>(vars
["ipBindAddrNoPort"]);
456 if(vars
.count("addXPF")) {
457 ret
->xpfRRCode
=std::stoi(boost::get
<string
>(vars
["addXPF"]));
460 if(vars
.count("maxCheckFailures")) {
461 ret
->maxCheckFailures
=std::stoi(boost::get
<string
>(vars
["maxCheckFailures"]));
464 if(vars
.count("rise")) {
465 ret
->minRiseSuccesses
=std::stoi(boost::get
<string
>(vars
["rise"]));
468 if(vars
.count("cpus")) {
469 for (const auto& cpu
: boost::get
<vector
<pair
<int,string
>>>(vars
["cpus"])) {
470 cpus
.insert(std::stoi(cpu
.second
));
474 /* this needs to be done _AFTER_ the order has been set,
475 since the server are kept ordered inside the pool */
476 auto localPools
= g_pools
.getCopy();
477 if(vars
.count("pool")) {
478 if(auto* pool
= boost::get
<string
>(&vars
["pool"])) {
479 ret
->pools
.insert(*pool
);
482 auto pools
= boost::get
<vector
<pair
<int, string
> > >(vars
["pool"]);
483 for(auto& p
: pools
) {
484 ret
->pools
.insert(p
.second
);
487 for(const auto& poolName
: ret
->pools
) {
488 addServerToPool(localPools
, poolName
, ret
);
492 addServerToPool(localPools
, "", ret
);
494 g_pools
.setState(localPools
);
496 if (ret
->connected
) {
497 ret
->threadStarted
.test_and_set();
500 g_launchWork
->push_back([ret
,cpus
]() {
501 ret
->tid
= thread(responderThread
, ret
);
503 mapThreadToCPUList(ret
->tid
.native_handle(), cpus
);
508 ret
->tid
= thread(responderThread
, ret
);
510 mapThreadToCPUList(ret
->tid
.native_handle(), cpus
);
515 auto states
= g_dstates
.getCopy();
516 states
.push_back(ret
);
517 std::stable_sort(states
.begin(), states
.end(), [](const decltype(ret
)& a
, const decltype(ret
)& b
) {
518 return a
->order
< b
->order
;
520 g_dstates
.setState(states
);
524 g_lua
.writeFunction("rmServer",
525 [](boost::variant
<std::shared_ptr
<DownstreamState
>, int, std::string
> var
)
528 shared_ptr
<DownstreamState
> server
= nullptr;
529 auto states
= g_dstates
.getCopy();
530 if (auto* rem
= boost::get
<shared_ptr
<DownstreamState
>>(&var
)) {
533 else if (auto str
= boost::get
<std::string
>(&var
)) {
534 const auto uuid
= getUniqueID(*str
);
535 for (auto& state
: states
) {
536 if (state
->id
== uuid
) {
542 int idx
= boost::get
<int>(var
);
543 server
= states
.at(idx
);
546 throw std::runtime_error("unable to locate the requested server");
548 auto localPools
= g_pools
.getCopy();
549 for (const string
& poolName
: server
->pools
) {
550 removeServerFromPool(localPools
, poolName
, server
);
552 /* the server might also be in the default pool */
553 removeServerFromPool(localPools
, "", server
);
554 g_pools
.setState(localPools
);
555 states
.erase(remove(states
.begin(), states
.end(), server
), states
.end());
556 g_dstates
.setState(states
);
559 g_lua
.writeFunction("truncateTC", [](bool tc
) { setLuaSideEffect(); g_truncateTC
=tc
; });
560 g_lua
.writeFunction("fixupCase", [](bool fu
) { setLuaSideEffect(); g_fixupCase
=fu
; });
562 g_lua
.writeFunction("addACL", [](const std::string
& domain
) {
564 g_ACL
.modify([domain
](NetmaskGroup
& nmg
) { nmg
.addMask(domain
); });
567 g_lua
.writeFunction("rmACL", [](const std::string
& netmask
) {
569 g_ACL
.modify([netmask
](NetmaskGroup
& nmg
) { nmg
.deleteMask(netmask
); });
572 g_lua
.writeFunction("setLocal", [client
](const std::string
& addr
, boost::optional
<localbind_t
> vars
) {
576 if (g_configurationDone
) {
577 g_outputBuffer
="setLocal cannot be used at runtime!\n";
580 bool reusePort
= false;
581 int tcpFastOpenQueueSize
= 0;
582 int tcpListenQueueSize
= 0;
583 std::string interface
;
586 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
);
589 ComboAddress
loc(addr
, 53);
590 for (auto it
= g_frontends
.begin(); it
!= g_frontends
.end(); ) {
591 /* DoH, DoT and DNSCrypt frontends are separate */
592 if ((*it
)->tlsFrontend
== nullptr && (*it
)->dnscryptCtx
== nullptr && (*it
)->dohFrontend
== nullptr) {
593 it
= g_frontends
.erase(it
);
600 // only works pre-startup, so no sync necessary
601 g_frontends
.push_back(std::unique_ptr
<ClientState
>(new ClientState(loc
, false, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
)));
602 auto tcpCS
= std::unique_ptr
<ClientState
>(new ClientState(loc
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
));
603 if (tcpListenQueueSize
> 0) {
604 tcpCS
->tcpListenQueueSize
= tcpListenQueueSize
;
606 g_frontends
.push_back(std::move(tcpCS
));
608 catch(const std::exception
& e
) {
609 g_outputBuffer
="Error: "+string(e
.what())+"\n";
613 g_lua
.writeFunction("addLocal", [client
](const std::string
& addr
, boost::optional
<localbind_t
> vars
) {
617 if (g_configurationDone
) {
618 g_outputBuffer
="addLocal cannot be used at runtime!\n";
621 bool reusePort
= false;
622 int tcpFastOpenQueueSize
= 0;
623 int tcpListenQueueSize
= 0;
624 std::string interface
;
627 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
);
630 ComboAddress
loc(addr
, 53);
631 // only works pre-startup, so no sync necessary
632 g_frontends
.push_back(std::unique_ptr
<ClientState
>(new ClientState(loc
, false, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
)));
633 auto tcpCS
= std::unique_ptr
<ClientState
>(new ClientState(loc
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
));
634 if (tcpListenQueueSize
> 0) {
635 tcpCS
->tcpListenQueueSize
= tcpListenQueueSize
;
637 g_frontends
.push_back(std::move(tcpCS
));
639 catch(std::exception
& e
) {
640 g_outputBuffer
="Error: "+string(e
.what())+"\n";
641 errlog("Error while trying to listen on %s: %s\n", addr
, string(e
.what()));
645 g_lua
.writeFunction("setACL", [](boost::variant
<string
,vector
<pair
<int, string
>>> inp
) {
648 if(auto str
= boost::get
<string
>(&inp
)) {
651 else for(const auto& p
: boost::get
<vector
<pair
<int,string
>>>(inp
)) {
652 nmg
.addMask(p
.second
);
657 g_lua
.writeFunction("showACL", []() {
658 setLuaNoSideEffect();
661 g_ACL
.getLocal()->toStringVector(&vec
);
663 for(const auto& s
: vec
)
664 g_outputBuffer
+=s
+"\n";
668 g_lua
.writeFunction("shutdown", []() {
670 sd_notify(0, "STOPPING=1");
671 #endif /* HAVE_SYSTEMD */
673 // Useful for debugging leaks, but might lead to race under load
674 // since other threads are still runing.
675 for(auto& frontend
: g_tlslocals
) {
680 google::protobuf::ShutdownProtobufLibrary();
681 #endif /* HAVE_PROTOBUF */
686 typedef std::unordered_map
<std::string
, boost::variant
<bool, std::string
> > showserversopts_t
;
688 g_lua
.writeFunction("showServers", [](boost::optional
<showserversopts_t
> vars
) {
689 setLuaNoSideEffect();
690 bool showUUIDs
= false;
692 if (vars
->count("showUUIDs")) {
693 showUUIDs
= boost::get
<bool>((*vars
)["showUUIDs"]);
700 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%" );
701 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
702 ret
<< (fmt
% "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools" % "UUID") << endl
;
704 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%" );
705 ret
<< (fmt
% "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools") << endl
;
708 uint64_t totQPS
{0}, totQueries
{0}, totDrops
{0};
710 auto states
= g_dstates
.getLocal();
711 for(const auto& s
: *states
) {
712 string status
= s
->getStatus();
714 for(auto& p
: s
->pools
) {
720 ret
<< (fmt
% counter
% s
->getName() % s
->remote
.toStringWithPort() %
722 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
;
724 ret
<< (fmt
% counter
% s
->getName() % s
->remote
.toStringWithPort() %
726 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
;
728 totQPS
+= s
->queryLoad
;
729 totQueries
+= s
->queries
.load();
730 totDrops
+= s
->reuseds
.load();
734 ret
<< (fmt
% "All" % "" % "" % ""
736 (double)totQPS
% "" % "" % "" % totQueries
% totDrops
% "" % "" % "" % "" % "" ) << endl
;
738 ret
<< (fmt
% "All" % "" % "" % ""
740 (double)totQPS
% "" % "" % "" % totQueries
% totDrops
% "" % "" % "" % "" ) << endl
;
743 g_outputBuffer
=ret
.str();
744 } catch(std::exception
& e
) {
745 g_outputBuffer
=e
.what();
750 g_lua
.writeFunction("getServers", []() {
751 setLuaNoSideEffect();
752 vector
<pair
<int, std::shared_ptr
<DownstreamState
> > > ret
;
754 for(const auto& s
: g_dstates
.getCopy()) {
755 ret
.push_back(make_pair(count
++, s
));
760 g_lua
.writeFunction("getPoolServers", [](string pool
) {
761 return getDownstreamCandidates(g_pools
.getCopy(), pool
);
764 g_lua
.writeFunction("getServer", [client
](boost::variant
<unsigned int, std::string
> i
) {
766 return std::make_shared
<DownstreamState
>(ComboAddress());
768 auto states
= g_dstates
.getCopy();
769 if (auto str
= boost::get
<std::string
>(&i
)) {
770 const auto uuid
= getUniqueID(*str
);
771 for (auto& state
: states
) {
772 if (state
->id
== uuid
) {
777 else if (auto pos
= boost::get
<unsigned int>(&i
)) {
778 return states
.at(*pos
);
781 g_outputBuffer
= "Error: no rule matched\n";
782 return std::shared_ptr
<DownstreamState
>(nullptr);
785 g_lua
.writeFunction("carbonServer", [](const std::string
& address
, boost::optional
<string
> ourName
,
786 boost::optional
<unsigned int> interval
, boost::optional
<string
> namespace_name
,
787 boost::optional
<string
> instance_name
) {
789 auto ours
= g_carbon
.getCopy();
791 ComboAddress(address
, 2003),
792 (namespace_name
&& !namespace_name
->empty()) ? *namespace_name
: "dnsdist",
793 ourName
? *ourName
: "",
794 (instance_name
&& !instance_name
->empty()) ? *instance_name
: "main" ,
795 interval
? *interval
: 30
797 g_carbon
.setState(ours
);
800 g_lua
.writeFunction("webserver", [client
,configCheck
](const std::string
& address
, const std::string
& password
, const boost::optional
<std::string
> apiKey
, const boost::optional
<std::map
<std::string
, std::string
> > customHeaders
, const boost::optional
<std::string
> acl
) {
804 local
= ComboAddress(address
);
806 catch (const PDNSException
& e
) {
807 throw std::runtime_error(std::string("Error parsing the bind address for the webserver: ") + e
.reason
);
810 if (client
|| configCheck
) {
815 int sock
= SSocket(local
.sin4
.sin_family
, SOCK_STREAM
, 0);
816 SSetsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, 1);
819 auto launch
=[sock
, local
, password
, apiKey
, customHeaders
, acl
]() {
820 setWebserverPassword(password
);
821 setWebserverAPIKey(apiKey
);
822 setWebserverCustomHeaders(customHeaders
);
824 setWebserverACL(*acl
);
826 thread
t(dnsdistWebserverThread
, sock
, local
);
830 g_launchWork
->push_back(launch
);
834 catch(std::exception
& e
) {
835 g_outputBuffer
="Unable to bind to webserver socket on " + local
.toStringWithPort() + ": " + e
.what();
836 errlog("Unable to bind to webserver socket on %s: %s", local
.toStringWithPort(), e
.what());
841 typedef std::unordered_map
<std::string
, boost::variant
<std::string
, std::map
<std::string
, std::string
>> > webserveropts_t
;
843 g_lua
.writeFunction("setWebserverConfig", [](boost::optional
<webserveropts_t
> vars
) {
849 if(vars
->count("password")) {
850 const std::string password
= boost::get
<std::string
>(vars
->at("password"));
852 setWebserverPassword(password
);
854 if(vars
->count("apiKey")) {
855 const std::string apiKey
= boost::get
<std::string
>(vars
->at("apiKey"));
857 setWebserverAPIKey(apiKey
);
859 if (vars
->count("acl")) {
860 const std::string acl
= boost::get
<std::string
>(vars
->at("acl"));
862 setWebserverACL(acl
);
864 if(vars
->count("customHeaders")) {
865 const boost::optional
<std::map
<std::string
, std::string
> > headers
= boost::get
<std::map
<std::string
, std::string
> >(vars
->at("customHeaders"));
867 setWebserverCustomHeaders(headers
);
871 g_lua
.writeFunction("controlSocket", [client
,configCheck
](const std::string
& str
) {
873 ComboAddress
local(str
, 5199);
875 if(client
|| configCheck
) {
876 g_serverControl
= local
;
880 g_consoleEnabled
= true;
881 #ifdef HAVE_LIBSODIUM
882 if (g_configurationDone
&& g_consoleKey
.empty()) {
883 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");
888 int sock
= SSocket(local
.sin4
.sin_family
, SOCK_STREAM
, 0);
889 SSetsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, 1);
892 auto launch
=[sock
, local
]() {
893 thread
t(controlThread
, sock
, local
);
897 g_launchWork
->push_back(launch
);
902 catch(std::exception
& e
) {
903 g_outputBuffer
="Unable to bind to control socket on " + local
.toStringWithPort() + ": " + e
.what();
904 errlog("Unable to bind to control socket on %s: %s", local
.toStringWithPort(), e
.what());
908 g_lua
.writeFunction("addConsoleACL", [](const std::string
& netmask
) {
910 #ifndef HAVE_LIBSODIUM
911 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
914 g_consoleACL
.modify([netmask
](NetmaskGroup
& nmg
) { nmg
.addMask(netmask
); });
917 g_lua
.writeFunction("setConsoleACL", [](boost::variant
<string
,vector
<pair
<int, string
>>> inp
) {
920 #ifndef HAVE_LIBSODIUM
921 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
925 if(auto str
= boost::get
<string
>(&inp
)) {
928 else for(const auto& p
: boost::get
<vector
<pair
<int,string
>>>(inp
)) {
929 nmg
.addMask(p
.second
);
931 g_consoleACL
.setState(nmg
);
934 g_lua
.writeFunction("showConsoleACL", []() {
935 setLuaNoSideEffect();
937 #ifndef HAVE_LIBSODIUM
938 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
942 g_consoleACL
.getLocal()->toStringVector(&vec
);
944 for(const auto& s
: vec
) {
945 g_outputBuffer
+= s
+ "\n";
949 g_lua
.writeFunction("clearQueryCounters", []() {
950 unsigned int size
{0};
952 WriteLock
wl(&g_qcount
.queryLock
);
953 size
= g_qcount
.records
.size();
954 g_qcount
.records
.clear();
957 boost::format
fmt("%d records cleared from query counter buffer\n");
958 g_outputBuffer
= (fmt
% size
).str();
961 g_lua
.writeFunction("getQueryCounters", [](boost::optional
<unsigned int> optMax
) {
962 setLuaNoSideEffect();
963 ReadLock
rl(&g_qcount
.queryLock
);
964 g_outputBuffer
= "query counting is currently: ";
965 g_outputBuffer
+= g_qcount
.enabled
? "enabled" : "disabled";
966 g_outputBuffer
+= (boost::format(" (%d records in buffer)\n") % g_qcount
.records
.size()).str();
968 boost::format
fmt("%-3d %s: %d request(s)\n");
969 QueryCountRecords::iterator it
;
970 unsigned int max
= optMax
? *optMax
: 10;
971 unsigned int index
{1};
972 for(it
= g_qcount
.records
.begin(); it
!= g_qcount
.records
.end() && index
<= max
; ++it
, ++index
) {
973 g_outputBuffer
+= (fmt
% index
% it
->first
% it
->second
).str();
977 g_lua
.writeFunction("setQueryCount", [](bool enabled
) { g_qcount
.enabled
=enabled
; });
979 g_lua
.writeFunction("setQueryCountFilter", [](QueryCountFilter func
) {
980 g_qcount
.filter
= func
;
983 g_lua
.writeFunction("makeKey", []() {
984 setLuaNoSideEffect();
985 g_outputBuffer
="setKey("+newKey()+")\n";
988 g_lua
.writeFunction("setKey", [](const std::string
& key
) {
989 if(!g_configurationDone
&& ! g_consoleKey
.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
990 return; // but later setKeys() trump the -k value again
992 #ifndef HAVE_LIBSODIUM
993 warnlog("Calling setKey() while libsodium support has not been enabled is not secure, and will result in cleartext communications");
998 if(B64Decode(key
, newkey
) < 0) {
999 g_outputBuffer
=string("Unable to decode ")+key
+" as Base64";
1000 errlog("%s", g_outputBuffer
);
1003 g_consoleKey
=newkey
;
1006 g_lua
.writeFunction("testCrypto", [](boost::optional
<string
> optTestMsg
)
1008 setLuaNoSideEffect();
1009 #ifdef HAVE_LIBSODIUM
1014 testmsg
= *optTestMsg
;
1017 testmsg
= "testStringForCryptoTests";
1020 SodiumNonce sn
, sn2
;
1023 string encrypted
= sodEncryptSym(testmsg
, g_consoleKey
, sn
);
1024 string decrypted
= sodDecryptSym(encrypted
, g_consoleKey
, sn2
);
1029 encrypted
= sodEncryptSym(testmsg
, g_consoleKey
, sn
);
1030 decrypted
= sodDecryptSym(encrypted
, g_consoleKey
, sn2
);
1032 if(testmsg
== decrypted
)
1033 g_outputBuffer
="Everything is ok!\n";
1035 g_outputBuffer
="Crypto failed.. (the decoded value does not match the cleartext one)\n";
1037 catch(const std::exception
& e
) {
1038 g_outputBuffer
="Crypto failed: "+std::string(e
.what())+"\n";
1041 g_outputBuffer
="Crypto failed..\n";
1044 g_outputBuffer
="Crypto not available.\n";
1048 g_lua
.writeFunction("setTCPRecvTimeout", [](int timeout
) { g_tcpRecvTimeout
=timeout
; });
1050 g_lua
.writeFunction("setTCPSendTimeout", [](int timeout
) { g_tcpSendTimeout
=timeout
; });
1052 g_lua
.writeFunction("setUDPTimeout", [](int timeout
) { g_udpTimeout
=timeout
; });
1054 g_lua
.writeFunction("setMaxUDPOutstanding", [](uint16_t max
) {
1055 if (!g_configurationDone
) {
1056 g_maxOutstanding
= max
;
1058 g_outputBuffer
="Max UDP outstanding cannot be altered at runtime!\n";
1062 g_lua
.writeFunction("setMaxTCPClientThreads", [](uint64_t max
) {
1063 if (!g_configurationDone
) {
1064 g_maxTCPClientThreads
= max
;
1066 g_outputBuffer
="Maximum TCP client threads count cannot be altered at runtime!\n";
1070 g_lua
.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max
) {
1071 if (!g_configurationDone
) {
1072 g_maxTCPQueuedConnections
= max
;
1074 g_outputBuffer
="The maximum number of queued TCP connections cannot be altered at runtime!\n";
1078 g_lua
.writeFunction("setMaxTCPQueriesPerConnection", [](size_t max
) {
1079 if (!g_configurationDone
) {
1080 g_maxTCPQueriesPerConn
= max
;
1082 g_outputBuffer
="The maximum number of queries per TCP connection cannot be altered at runtime!\n";
1086 g_lua
.writeFunction("setMaxTCPConnectionsPerClient", [](size_t max
) {
1087 if (!g_configurationDone
) {
1088 g_maxTCPConnectionsPerClient
= max
;
1090 g_outputBuffer
="The maximum number of TCP connection per client cannot be altered at runtime!\n";
1094 g_lua
.writeFunction("setMaxTCPConnectionDuration", [](size_t max
) {
1095 if (!g_configurationDone
) {
1096 g_maxTCPConnectionDuration
= max
;
1098 g_outputBuffer
="The maximum duration of a TCP connection cannot be altered at runtime!\n";
1102 g_lua
.writeFunction("setCacheCleaningDelay", [](uint32_t delay
) { g_cacheCleaningDelay
= delay
; });
1104 g_lua
.writeFunction("setCacheCleaningPercentage", [](uint16_t percentage
) { if (percentage
< 100) g_cacheCleaningPercentage
= percentage
; else g_cacheCleaningPercentage
= 100; });
1106 g_lua
.writeFunction("setECSSourcePrefixV4", [](uint16_t prefix
) { g_ECSSourcePrefixV4
=prefix
; });
1108 g_lua
.writeFunction("setECSSourcePrefixV6", [](uint16_t prefix
) { g_ECSSourcePrefixV6
=prefix
; });
1110 g_lua
.writeFunction("setECSOverride", [](bool override
) { g_ECSOverride
=override
; });
1112 g_lua
.writeFunction("setPreserveTrailingData", [](bool preserve
) { g_preserveTrailingData
= preserve
; });
1114 g_lua
.writeFunction("showDynBlocks", []() {
1115 setLuaNoSideEffect();
1116 auto slow
= g_dynblockNMG
.getCopy();
1117 struct timespec now
;
1119 boost::format
fmt("%-24s %8d %8d %-10s %-20s %s\n");
1120 g_outputBuffer
= (fmt
% "What" % "Seconds" % "Blocks" % "Warning" % "Action" % "Reason").str();
1121 for(const auto& e
: slow
) {
1122 if(now
< e
.second
.until
)
1123 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();
1125 auto slow2
= g_dynblockSMT
.getCopy();
1126 slow2
.visit([&now
, &fmt
](const SuffixMatchTree
<DynBlock
>& node
) {
1127 if(now
<node
.d_value
.until
) {
1128 string
dom("empty");
1129 if(!node
.d_value
.domain
.empty())
1130 dom
= node
.d_value
.domain
.toString();
1131 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();
1137 g_lua
.writeFunction("clearDynBlocks", []() {
1140 g_dynblockNMG
.setState(nmg
);
1141 SuffixMatchTree
<DynBlock
> smt
;
1142 g_dynblockSMT
.setState(smt
);
1145 g_lua
.writeFunction("addDynBlocks",
1146 [](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
) {
1151 auto slow
= g_dynblockNMG
.getCopy();
1152 struct timespec until
, now
;
1155 int actualSeconds
= seconds
? *seconds
: 10;
1156 until
.tv_sec
+= actualSeconds
;
1157 for(const auto& capair
: m
) {
1158 unsigned int count
= 0;
1159 auto got
= slow
.lookup(Netmask(capair
.first
));
1162 if(until
< got
->second
.until
) // had a longer policy
1164 if(now
< got
->second
.until
) // only inherit count on fresh query we are extending
1165 count
=got
->second
.blocks
;
1169 DynBlock db
{msg
,until
,DNSName(),(action
? *action
: DNSAction::Action::None
)};
1172 warnlog("Inserting dynamic block for %s for %d seconds: %s", capair
.first
.toString(), actualSeconds
, msg
);
1173 slow
.insert(Netmask(capair
.first
)).second
=db
;
1175 g_dynblockNMG
.setState(slow
);
1178 g_lua
.writeFunction("addDynBlockSMT",
1179 [](const vector
<pair
<unsigned int, string
> >&names
, const std::string
& msg
, boost::optional
<int> seconds
, boost::optional
<DNSAction::Action
> action
) {
1180 if (names
.empty()) {
1184 auto slow
= g_dynblockSMT
.getCopy();
1185 struct timespec until
, now
;
1188 int actualSeconds
= seconds
? *seconds
: 10;
1189 until
.tv_sec
+= actualSeconds
;
1191 for(const auto& capair
: names
) {
1192 unsigned int count
= 0;
1193 DNSName
domain(capair
.second
);
1194 domain
.makeUsLowerCase();
1195 auto got
= slow
.lookup(domain
);
1198 if(until
< got
->until
) // had a longer policy
1200 if(now
< got
->until
) // only inherit count on fresh query we are extending
1206 DynBlock db
{msg
,until
,domain
,(action
? *action
: DNSAction::Action::None
)};
1209 warnlog("Inserting dynamic block for %s for %d seconds: %s", domain
, actualSeconds
, msg
);
1210 slow
.add(domain
, db
);
1212 g_dynblockSMT
.setState(slow
);
1215 g_lua
.writeFunction("setDynBlocksAction", [](DNSAction::Action action
) {
1216 if (!g_configurationDone
) {
1217 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
) {
1218 g_dynBlockAction
= action
;
1221 errlog("Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!");
1222 g_outputBuffer
="Dynamic blocks action can only be Drop, NoOp, NXDomain, Refused, Truncate or NoRecurse!\n";
1225 g_outputBuffer
="Dynamic blocks action cannot be altered at runtime!\n";
1229 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
) {
1230 if (g_configurationDone
) {
1231 g_outputBuffer
="addDNSCryptBind cannot be used at runtime!\n";
1234 #ifdef HAVE_DNSCRYPT
1235 bool reusePort
= false;
1236 int tcpFastOpenQueueSize
= 0;
1237 int tcpListenQueueSize
= 0;
1238 std::string interface
;
1240 std::vector
<DNSCryptContext::CertKeyPaths
> certKeys
;
1242 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
);
1244 if (certFiles
.type() == typeid(std::string
) && keyFiles
.type() == typeid(std::string
)) {
1245 auto certFile
= boost::get
<std::string
>(certFiles
);
1246 auto keyFile
= boost::get
<std::string
>(keyFiles
);
1247 certKeys
.push_back({certFile
, keyFile
});
1249 else if (certFiles
.type() == typeid(std::vector
<std::pair
<int,std::string
>>) && keyFiles
.type() == typeid(std::vector
<std::pair
<int,std::string
>>)) {
1250 auto certFilesVect
= boost::get
<std::vector
<std::pair
<int,std::string
>>>(certFiles
);
1251 auto keyFilesVect
= boost::get
<std::vector
<std::pair
<int,std::string
>>>(keyFiles
);
1252 if (certFilesVect
.size() == keyFilesVect
.size()) {
1253 for (size_t idx
= 0; idx
< certFilesVect
.size(); idx
++) {
1254 certKeys
.push_back({certFilesVect
.at(idx
).second
, keyFilesVect
.at(idx
).second
});
1258 errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind!");
1259 g_outputBuffer
="Error, mismatching number of certificates and keys in call to addDNSCryptBind()!";
1264 errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind()!");
1265 g_outputBuffer
="Error, mismatching number of certificates and keys in call to addDNSCryptBind()!";
1270 auto ctx
= std::make_shared
<DNSCryptContext
>(providerName
, certKeys
);
1273 auto cs
= std::unique_ptr
<ClientState
>(new ClientState(ComboAddress(addr
, 443), false, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
));
1274 cs
->dnscryptCtx
= ctx
;
1275 g_dnsCryptLocals
.push_back(ctx
);
1276 g_frontends
.push_back(std::move(cs
));
1279 cs
= std::unique_ptr
<ClientState
>(new ClientState(ComboAddress(addr
, 443), true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
));
1280 cs
->dnscryptCtx
= ctx
;
1281 if (tcpListenQueueSize
> 0) {
1282 cs
->tcpListenQueueSize
= tcpListenQueueSize
;
1285 g_frontends
.push_back(std::move(cs
));
1287 catch(std::exception
& e
) {
1289 g_outputBuffer
="Error: "+string(e
.what())+"\n";
1292 g_outputBuffer
="Error: DNSCrypt support is not enabled.\n";
1297 g_lua
.writeFunction("showDNSCryptBinds", []() {
1298 setLuaNoSideEffect();
1299 #ifdef HAVE_DNSCRYPT
1301 boost::format
fmt("%1$-3d %2% %|25t|%3$-20.20s");
1302 ret
<< (fmt
% "#" % "Address" % "Provider Name") << endl
;
1305 std::unordered_set
<std::shared_ptr
<DNSCryptContext
>> contexts
;
1306 for (const auto& frontend
: g_frontends
) {
1307 const std::shared_ptr
<DNSCryptContext
> ctx
= frontend
->dnscryptCtx
;
1308 if (!ctx
|| contexts
.count(ctx
) != 0) {
1311 contexts
.insert(ctx
);
1312 ret
<< (fmt
% idx
% frontend
->local
.toStringWithPort() % ctx
->getProviderName()) << endl
;
1316 g_outputBuffer
=ret
.str();
1318 g_outputBuffer
="Error: DNSCrypt support is not enabled.\n";
1322 g_lua
.writeFunction("getDNSCryptBind", [](size_t idx
) {
1323 setLuaNoSideEffect();
1324 #ifdef HAVE_DNSCRYPT
1325 std::shared_ptr
<DNSCryptContext
> ret
= nullptr;
1326 if (idx
< g_dnsCryptLocals
.size()) {
1327 ret
= g_dnsCryptLocals
.at(idx
);
1331 g_outputBuffer
="Error: DNSCrypt support is not enabled.\n";
1335 g_lua
.writeFunction("getDNSCryptBindCount", []() {
1336 setLuaNoSideEffect();
1337 return g_dnsCryptLocals
.size();
1340 g_lua
.writeFunction("generateDNSCryptProviderKeys", [client
](const std::string
& publicKeyFile
, const std::string privateKeyFile
) {
1341 setLuaNoSideEffect();
1342 #ifdef HAVE_DNSCRYPT
1346 unsigned char publicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
];
1347 unsigned char privateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
];
1348 sodium_mlock(privateKey
, sizeof(privateKey
));
1351 DNSCryptContext::generateProviderKeys(publicKey
, privateKey
);
1353 ofstream
pubKStream(publicKeyFile
);
1354 pubKStream
.write((char*) publicKey
, sizeof(publicKey
));
1357 ofstream
privKStream(privateKeyFile
);
1358 privKStream
.write((char*) privateKey
, sizeof(privateKey
));
1359 privKStream
.close();
1361 g_outputBuffer
="Provider fingerprint is: " + DNSCryptContext::getProviderFingerprint(publicKey
) + "\n";
1363 catch(std::exception
& e
) {
1365 g_outputBuffer
="Error: "+string(e
.what())+"\n";
1368 sodium_memzero(privateKey
, sizeof(privateKey
));
1369 sodium_munlock(privateKey
, sizeof(privateKey
));
1371 g_outputBuffer
="Error: DNSCrypt support is not enabled.\n";
1375 g_lua
.writeFunction("printDNSCryptProviderFingerprint", [](const std::string
& publicKeyFile
) {
1376 setLuaNoSideEffect();
1377 #ifdef HAVE_DNSCRYPT
1378 unsigned char publicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
];
1381 ifstream
file(publicKeyFile
);
1382 file
.read((char *) &publicKey
, sizeof(publicKey
));
1385 throw std::runtime_error("Invalid dnscrypt provider public key file " + publicKeyFile
);
1388 g_outputBuffer
="Provider fingerprint is: " + DNSCryptContext::getProviderFingerprint(publicKey
) + "\n";
1390 catch(std::exception
& e
) {
1392 g_outputBuffer
="Error: "+string(e
.what())+"\n";
1395 g_outputBuffer
="Error: DNSCrypt support is not enabled.\n";
1399 #ifdef HAVE_DNSCRYPT
1400 g_lua
.writeFunction("generateDNSCryptCertificate", [client
](const std::string
& providerPrivateKeyFile
, const std::string
& certificateFile
, const std::string privateKeyFile
, uint32_t serial
, time_t begin
, time_t end
, boost::optional
<DNSCryptExchangeVersion
> version
) {
1401 setLuaNoSideEffect();
1405 DNSCryptPrivateKey privateKey
;
1409 if (generateDNSCryptCertificate(providerPrivateKeyFile
, serial
, begin
, end
, version
? *version
: DNSCryptExchangeVersion::VERSION1
, cert
, privateKey
)) {
1410 privateKey
.saveToFile(privateKeyFile
);
1411 DNSCryptContext::saveCertFromFile(cert
, certificateFile
);
1414 catch(const std::exception
& e
) {
1416 g_outputBuffer
="Error: "+string(e
.what())+"\n";
1421 g_lua
.writeFunction("showPools", []() {
1422 setLuaNoSideEffect();
1425 boost::format
fmt("%1$-20.20s %|25t|%2$20s %|25t|%3$20s %|50t|%4%" );
1427 ret
<< (fmt
% "Name" % "Cache" % "ServerPolicy" % "Servers" ) << endl
;
1429 const auto localPools
= g_pools
.getCopy();
1430 for (const auto& entry
: localPools
) {
1431 const string
& name
= entry
.first
;
1432 const std::shared_ptr
<ServerPool
> pool
= entry
.second
;
1433 string cache
= pool
->packetCache
!= nullptr ? pool
->packetCache
->toString() : "";
1434 string policy
= g_policy
.getLocal()->name
;
1435 if (pool
->policy
!= nullptr) {
1436 policy
= pool
->policy
->name
;
1440 for (const auto& server
: pool
->getServers()) {
1441 if (!servers
.empty()) {
1444 if (!server
.second
->getName().empty()) {
1445 servers
+= server
.second
->getName();
1448 servers
+= server
.second
->remote
.toStringWithPort();
1451 ret
<< (fmt
% name
% cache
% policy
% servers
) << endl
;
1453 g_outputBuffer
=ret
.str();
1454 }catch(std::exception
& e
) { g_outputBuffer
=e
.what(); throw; }
1457 g_lua
.writeFunction("getPool", [client
](const string
& poolName
) {
1459 return std::make_shared
<ServerPool
>();
1461 auto localPools
= g_pools
.getCopy();
1462 std::shared_ptr
<ServerPool
> pool
= createPoolIfNotExists(localPools
, poolName
);
1463 g_pools
.setState(localPools
);
1467 g_lua
.writeFunction("setVerboseHealthChecks", [](bool verbose
) { g_verboseHealthChecks
=verbose
; });
1468 g_lua
.writeFunction("setStaleCacheEntriesTTL", [](uint32_t ttl
) { g_staleCacheEntriesTTL
= ttl
; });
1470 g_lua
.writeFunction("showBinds", []() {
1471 setLuaNoSideEffect();
1474 boost::format
fmt("%1$-3d %2$-20.20s %|35t|%3$-20.20s %|57t|%4%" );
1476 ret
<< (fmt
% "#" % "Address" % "Protocol" % "Queries" ) << endl
;
1479 for (const auto& front
: g_frontends
) {
1480 ret
<< (fmt
% counter
% front
->local
.toStringWithPort() % front
->getType() % front
->queries
) << endl
;
1483 g_outputBuffer
=ret
.str();
1484 }catch(std::exception
& e
) { g_outputBuffer
=e
.what(); throw; }
1487 g_lua
.writeFunction("getBind", [](size_t num
) {
1488 setLuaNoSideEffect();
1489 ClientState
* ret
= nullptr;
1490 if(num
< g_frontends
.size()) {
1491 ret
=g_frontends
[num
].get();
1496 g_lua
.writeFunction("getBindCount", []() {
1497 setLuaNoSideEffect();
1498 return g_frontends
.size();
1501 g_lua
.writeFunction("help", [](boost::optional
<std::string
> command
) {
1502 setLuaNoSideEffect();
1503 g_outputBuffer
= "";
1504 for (const auto& keyword
: g_consoleKeywords
) {
1506 g_outputBuffer
+= keyword
.toString() + "\n";
1508 else if (keyword
.name
== command
) {
1509 g_outputBuffer
= keyword
.toString() + "\n";
1514 g_outputBuffer
= "Nothing found for " + *command
+ "\n";
1518 g_lua
.writeFunction("showVersion", []() {
1519 setLuaNoSideEffect();
1520 g_outputBuffer
= "dnsdist " + std::string(VERSION
) + "\n";
1523 g_lua
.writeFunction("showSecurityStatus", []() {
1524 setLuaNoSideEffect();
1525 g_outputBuffer
= std::to_string(g_stats
.securityStatus
) + "\n";
1529 g_lua
.writeFunction("setDefaultBPFFilter", [](std::shared_ptr
<BPFFilter
> bpf
) {
1530 if (g_configurationDone
) {
1531 g_outputBuffer
="setDefaultBPFFilter() cannot be used at runtime!\n";
1534 g_defaultBPFFilter
= bpf
;
1537 g_lua
.writeFunction("registerDynBPFFilter", [](std::shared_ptr
<DynBPFFilter
> dbpf
) {
1539 g_dynBPFFilters
.push_back(dbpf
);
1543 g_lua
.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr
<DynBPFFilter
> dbpf
) {
1545 for (auto it
= g_dynBPFFilters
.begin(); it
!= g_dynBPFFilters
.end(); it
++) {
1547 g_dynBPFFilters
.erase(it
);
1554 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
) {
1556 struct timespec until
, now
;
1557 clock_gettime(CLOCK_MONOTONIC
, &now
);
1559 int actualSeconds
= seconds
? *seconds
: 10;
1560 until
.tv_sec
+= actualSeconds
;
1561 for(const auto& capair
: m
) {
1562 if (dynbpf
->block(capair
.first
, until
)) {
1563 warnlog("Inserting eBPF dynamic block for %s for %d seconds: %s", capair
.first
.toString(), actualSeconds
, msg
? *msg
: "");
1568 #endif /* HAVE_EBPF */
1570 g_lua
.writeFunction
<std::unordered_map
<string
,uint64_t>()>("getStatisticsCounters", []() {
1571 setLuaNoSideEffect();
1572 std::unordered_map
<string
,uint64_t> res
;
1573 for(const auto& entry
: g_stats
.entries
) {
1574 if(const auto& val
= boost::get
<DNSDistStats::stat_t
*>(&entry
.second
))
1575 res
[entry
.first
] = (*val
)->load();
1580 g_lua
.writeFunction("includeDirectory", [](const std::string
& dirname
) {
1581 if (g_configurationDone
) {
1582 errlog("includeDirectory() cannot be used at runtime!");
1583 g_outputBuffer
="includeDirectory() cannot be used at runtime!\n";
1588 errlog("includeDirectory() cannot be used recursively!");
1589 g_outputBuffer
="includeDirectory() cannot be used recursively!\n";
1595 if (stat(dirname
.c_str(), &st
)) {
1596 errlog("The included directory %s does not exist!", dirname
.c_str());
1597 g_outputBuffer
="The included directory " + dirname
+ " does not exist!";
1601 if (!S_ISDIR(st
.st_mode
)) {
1602 errlog("The included directory %s is not a directory!", dirname
.c_str());
1603 g_outputBuffer
="The included directory " + dirname
+ " is not a directory!";
1609 std::list
<std::string
> files
;
1610 if (!(dirp
= opendir(dirname
.c_str()))) {
1611 errlog("Error opening the included directory %s!", dirname
.c_str());
1612 g_outputBuffer
="Error opening the included directory " + dirname
+ "!";
1616 while((ent
= readdir(dirp
)) != NULL
) {
1617 if (ent
->d_name
[0] == '.') {
1621 if (boost::ends_with(ent
->d_name
, ".conf")) {
1622 std::ostringstream namebuf
;
1623 namebuf
<< dirname
.c_str() << "/" << ent
->d_name
;
1625 if (stat(namebuf
.str().c_str(), &st
) || !S_ISREG(st
.st_mode
)) {
1629 files
.push_back(namebuf
.str());
1636 for (auto file
= files
.begin(); file
!= files
.end(); ++file
) {
1637 std::ifstream
ifs(*file
);
1639 warnlog("Unable to read configuration from '%s'", *file
);
1641 vinfolog("Read configuration from '%s'", *file
);
1644 g_lua
.executeCode(ifs
);
1650 g_lua
.writeFunction("setAPIWritable", [](bool writable
, boost::optional
<std::string
> apiConfigDir
) {
1652 g_apiReadWrite
= writable
;
1654 if (!(*apiConfigDir
).empty()) {
1655 g_apiConfigDirectory
= *apiConfigDir
;
1658 errlog("The API configuration directory value cannot be empty!");
1659 g_outputBuffer
="The API configuration directory value cannot be empty!";
1664 g_lua
.writeFunction("setServFailWhenNoServer", [](bool servfail
) {
1666 g_servFailOnNoPolicy
= servfail
;
1669 g_lua
.writeFunction("setRoundRobinFailOnNoServer", [](bool fail
) {
1671 g_roundrobinFailOnNoServer
= fail
;
1674 g_lua
.writeFunction("setConsistentHashingBalancingFactor", [](double factor
) {
1676 if (factor
>= 1.0) {
1677 g_consistentHashBalancingFactor
= factor
;
1680 errlog("Invalid value passed to setConsistentHashingBalancingFactor()!");
1681 g_outputBuffer
="Invalid value passed to setConsistentHashingBalancingFactor()!\n";
1686 g_lua
.writeFunction("setWeightedBalancingFactor", [](double factor
) {
1688 if (factor
>= 1.0) {
1689 g_weightedBalancingFactor
= factor
;
1692 errlog("Invalid value passed to setWeightedBalancingFactor()!");
1693 g_outputBuffer
="Invalid value passed to setWeightedBalancingFactor()!\n";
1698 g_lua
.writeFunction("setRingBuffersSize", [](size_t capacity
, boost::optional
<size_t> numberOfShards
) {
1700 if (g_configurationDone
) {
1701 errlog("setRingBuffersSize() cannot be used at runtime!");
1702 g_outputBuffer
="setRingBuffersSize() cannot be used at runtime!\n";
1705 g_rings
.setCapacity(capacity
, numberOfShards
? *numberOfShards
: 1);
1708 g_lua
.writeFunction("setRingBuffersLockRetries", [](size_t retries
) {
1710 g_rings
.setNumberOfLockRetries(retries
);
1713 g_lua
.writeFunction("setWHashedPertubation", [](uint32_t pertub
) {
1715 g_hashperturb
= pertub
;
1718 g_lua
.writeFunction("setTCPUseSinglePipe", [](bool flag
) {
1719 if (g_configurationDone
) {
1720 g_outputBuffer
="setTCPUseSinglePipe() cannot be used at runtime!\n";
1724 g_useTCPSinglePipe
= flag
;
1727 g_lua
.writeFunction("snmpAgent", [client
,configCheck
](bool enableTraps
, boost::optional
<std::string
> masterSocket
) {
1728 if(client
|| configCheck
)
1730 #ifdef HAVE_NET_SNMP
1731 if (g_configurationDone
) {
1732 errlog("snmpAgent() cannot be used at runtime!");
1733 g_outputBuffer
="snmpAgent() cannot be used at runtime!\n";
1737 if (g_snmpEnabled
) {
1738 errlog("snmpAgent() cannot be used twice!");
1739 g_outputBuffer
="snmpAgent() cannot be used twice!\n";
1743 g_snmpEnabled
= true;
1744 g_snmpTrapsEnabled
= enableTraps
;
1745 g_snmpAgent
= new DNSDistSNMPAgent("dnsdist", masterSocket
? *masterSocket
: std::string());
1747 errlog("NET SNMP support is required to use snmpAgent()");
1748 g_outputBuffer
="NET SNMP support is required to use snmpAgent()\n";
1749 #endif /* HAVE_NET_SNMP */
1752 g_lua
.writeFunction("sendCustomTrap", [](const std::string
& str
) {
1753 if (g_snmpAgent
&& g_snmpTrapsEnabled
) {
1754 g_snmpAgent
->sendCustomTrap(str
);
1758 g_lua
.writeFunction("setServerPolicy", [](ServerPolicy policy
) {
1760 g_policy
.setState(policy
);
1763 g_lua
.writeFunction("setServerPolicyLua", [](string name
, ServerPolicy::policyfunc_t policy
) {
1765 g_policy
.setState(ServerPolicy
{name
, policy
, true});
1768 g_lua
.writeFunction("setServerPolicyLuaFFI", [](string name
, ServerPolicy::ffipolicyfunc_t policy
) {
1770 auto pol
= ServerPolicy(name
, policy
);
1771 g_policy
.setState(std::move(pol
));
1774 g_lua
.writeFunction("showServerPolicy", []() {
1776 g_outputBuffer
=g_policy
.getLocal()->name
+"\n";
1779 g_lua
.writeFunction("setPoolServerPolicy", [](ServerPolicy policy
, string pool
) {
1781 auto localPools
= g_pools
.getCopy();
1782 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(policy
));
1783 g_pools
.setState(localPools
);
1786 g_lua
.writeFunction("setPoolServerPolicyLua", [](string name
, ServerPolicy::policyfunc_t policy
, string pool
) {
1788 auto localPools
= g_pools
.getCopy();
1789 setPoolPolicy(localPools
, pool
, std::make_shared
<ServerPolicy
>(ServerPolicy
{name
, policy
, true}));
1790 g_pools
.setState(localPools
);
1793 g_lua
.writeFunction("showPoolServerPolicy", [](string pool
) {
1795 auto localPools
= g_pools
.getCopy();
1796 auto poolObj
= getPool(localPools
, pool
);
1797 if (poolObj
->policy
== nullptr) {
1798 g_outputBuffer
=g_policy
.getLocal()->name
+"\n";
1800 g_outputBuffer
=poolObj
->policy
->name
+"\n";
1804 g_lua
.writeFunction("setTCPDownstreamCleanupInterval", [](uint16_t interval
) {
1806 g_downstreamTCPCleanupInterval
= interval
;
1809 g_lua
.writeFunction("setConsoleConnectionsLogging", [](bool enabled
) {
1810 g_logConsoleConnections
= enabled
;
1813 g_lua
.writeFunction("setConsoleOutputMaxMsgSize", [](uint32_t size
) {
1814 g_consoleOutputMsgMaxSize
= size
;
1817 g_lua
.writeFunction("setUDPMultipleMessagesVectorSize", [](size_t vSize
) {
1818 if (g_configurationDone
) {
1819 errlog("setUDPMultipleMessagesVectorSize() cannot be used at runtime!");
1820 g_outputBuffer
="setUDPMultipleMessagesVectorSize() cannot be used at runtime!\n";
1823 #if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
1825 g_udpVectorSize
= vSize
;
1827 errlog("recvmmsg() support is not available!");
1828 g_outputBuffer
="recvmmsg support is not available!\n";
1832 g_lua
.writeFunction("setAddEDNSToSelfGeneratedResponses", [](bool add
) {
1833 g_addEDNSToSelfGeneratedResponses
= add
;
1836 g_lua
.writeFunction("setPayloadSizeOnSelfGeneratedAnswers", [](uint16_t payloadSize
) {
1837 if (payloadSize
< 512) {
1838 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!");
1839 g_outputBuffer
="setPayloadSizeOnSelfGeneratedAnswers() is set too low, using 512 instead!";
1842 if (payloadSize
> s_udpIncomingBufferSize
) {
1843 warnlog("setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to %d instead!", s_udpIncomingBufferSize
);
1844 g_outputBuffer
="setPayloadSizeOnSelfGeneratedAnswers() is set too high, capping to " + std::to_string(s_udpIncomingBufferSize
) + " instead";
1845 payloadSize
= s_udpIncomingBufferSize
;
1847 g_PayloadSizeSelfGenAnswers
= payloadSize
;
1850 g_lua
.writeFunction("setSecurityPollSuffix", [](const std::string
& suffix
) {
1851 if (g_configurationDone
) {
1852 g_outputBuffer
="setSecurityPollSuffix() cannot be used at runtime!\n";
1856 g_secPollSuffix
= suffix
;
1859 g_lua
.writeFunction("setSecurityPollInterval", [](time_t newInterval
) {
1860 if (newInterval
<= 0) {
1861 warnlog("setSecurityPollInterval() should be > 0, skipping");
1862 g_outputBuffer
="setSecurityPollInterval() should be > 0, skipping";
1865 g_secPollInterval
= newInterval
;
1868 g_lua
.writeFunction("setSyslogFacility", [](int facility
) {
1870 if (g_configurationDone
) {
1871 g_outputBuffer
="setSyslogFacility cannot be used at runtime!\n";
1874 setSyslogFacility(facility
);
1877 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
) {
1881 #ifdef HAVE_DNS_OVER_HTTPS
1883 if (g_configurationDone
) {
1884 g_outputBuffer
="addDOHLocal cannot be used at runtime!\n";
1887 auto frontend
= std::make_shared
<DOHFrontend
>();
1889 if (certFiles
&& !certFiles
->empty() && keyFiles
&& !keyFiles
->empty()) {
1890 if (!loadTLSCertificateAndKeys("addDOHLocal", frontend
->d_tlsConfig
.d_certKeyPairs
, *certFiles
, *keyFiles
)) {
1894 frontend
->d_local
= ComboAddress(addr
, 443);
1897 frontend
->d_local
= ComboAddress(addr
, 80);
1898 infolog("No certificate provided for DoH endpoint %s, running in DNS over HTTP mode instead of DNS over HTTPS", frontend
->d_local
.toStringWithPort());
1902 if (urls
->type() == typeid(std::string
)) {
1903 frontend
->d_urls
.push_back(boost::get
<std::string
>(*urls
));
1905 else if (urls
->type() == typeid(std::vector
<std::pair
<int,std::string
>>)) {
1906 auto urlsVect
= boost::get
<std::vector
<std::pair
<int,std::string
>>>(*urls
);
1907 for(const auto& p
: urlsVect
) {
1908 frontend
->d_urls
.push_back(p
.second
);
1913 frontend
->d_urls
= {"/dns-query"};
1916 bool reusePort
= false;
1917 int tcpFastOpenQueueSize
= 0;
1918 int tcpListenQueueSize
= 0;
1919 std::string interface
;
1923 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
);
1925 if (vars
->count("idleTimeout")) {
1926 frontend
->d_idleTimeout
= boost::get
<int>((*vars
)["idleTimeout"]);
1929 if (vars
->count("serverTokens")) {
1930 frontend
->d_serverTokens
= boost::get
<const string
>((*vars
)["serverTokens"]);
1933 if (vars
->count("customResponseHeaders")) {
1934 for (auto const& headerMap
: boost::get
<std::map
<std::string
,std::string
>>((*vars
)["customResponseHeaders"])) {
1935 std::pair
<std::string
,std::string
> headerResponse
= std::make_pair(boost::to_lower_copy(headerMap
.first
), headerMap
.second
);
1936 frontend
->d_customResponseHeaders
.push_back(headerResponse
);
1940 if (vars
->count("sendCacheControlHeaders")) {
1941 frontend
->d_sendCacheControlHeaders
= boost::get
<bool>((*vars
)["sendCacheControlHeaders"]);
1944 if (vars
->count("trustForwardedForHeader")) {
1945 frontend
->d_trustForwardedForHeader
= boost::get
<bool>((*vars
)["trustForwardedForHeader"]);
1948 if (vars
->count("internalPipeBufferSize")) {
1949 frontend
->d_internalPipeBufferSize
= boost::get
<int>((*vars
)["internalPipeBufferSize"]);
1952 parseTLSConfig(frontend
->d_tlsConfig
, "addDOHLocal", vars
);
1954 g_dohlocals
.push_back(frontend
);
1955 auto cs
= std::unique_ptr
<ClientState
>(new ClientState(frontend
->d_local
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
));
1956 cs
->dohFrontend
= frontend
;
1957 if (tcpListenQueueSize
> 0) {
1958 cs
->tcpListenQueueSize
= tcpListenQueueSize
;
1961 g_frontends
.push_back(std::move(cs
));
1963 throw std::runtime_error("addDOHLocal() called but DNS over HTTPS support is not present!");
1967 g_lua
.writeFunction("showDOHFrontends", []() {
1968 #ifdef HAVE_DNS_OVER_HTTPS
1969 setLuaNoSideEffect();
1972 boost::format
fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d %-15d");
1973 ret
<< (fmt
% "#" % "Address" % "HTTP" % "HTTP/1" % "HTTP/2" % "GET" % "POST" % "Bad" % "Errors" % "Redirects" % "Valid" % "# ticket keys" % "Rotation delay" % "Next rotation") << endl
;
1975 for (const auto& ctx
: g_dohlocals
) {
1976 ret
<< (fmt
% counter
% ctx
->d_local
.toStringWithPort() % ctx
->d_httpconnects
% ctx
->d_http1Stats
.d_nbQueries
% ctx
->d_http2Stats
.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
;
1979 g_outputBuffer
= ret
.str();
1981 catch(const std::exception
& e
) {
1982 g_outputBuffer
= e
.what();
1986 g_outputBuffer
="DNS over HTTPS support is not present!\n";
1990 g_lua
.writeFunction("showDOHResponseCodes", []() {
1991 #ifdef HAVE_DNS_OVER_HTTPS
1992 setLuaNoSideEffect();
1995 boost::format
fmt("%-3d %-20.20s %-15d %-15d %-15d %-15d %-15d %-15d");
1996 g_outputBuffer
= "\n- HTTP/1:\n\n";
1997 ret
<< (fmt
% "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others" ) << endl
;
1999 for (const auto& ctx
: g_dohlocals
) {
2000 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
;
2003 g_outputBuffer
+= ret
.str();
2006 g_outputBuffer
+= "\n- HTTP/2:\n\n";
2007 ret
<< (fmt
% "#" % "Address" % "200" % "400" % "403" % "500" % "502" % "Others" ) << endl
;
2009 for (const auto& ctx
: g_dohlocals
) {
2010 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
;
2013 g_outputBuffer
+= ret
.str();
2015 catch(const std::exception
& e
) {
2016 g_outputBuffer
= e
.what();
2020 g_outputBuffer
="DNS over HTTPS support is not present!\n";
2024 g_lua
.writeFunction("getDOHFrontend", [client
](size_t index
) {
2025 std::shared_ptr
<DOHFrontend
> result
= nullptr;
2029 #ifdef HAVE_DNS_OVER_HTTPS
2030 setLuaNoSideEffect();
2032 if (index
< g_dohlocals
.size()) {
2033 result
= g_dohlocals
.at(index
);
2036 errlog("Error: trying to get DOH frontend with index %zu but we only have %zu frontend(s)\n", index
, g_dohlocals
.size());
2037 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";
2040 catch(const std::exception
& e
) {
2041 g_outputBuffer
="Error while trying to get DOH frontend with index " + std::to_string(index
) + ": "+string(e
.what())+"\n";
2042 errlog("Error while trying to get DOH frontend with index %zu: %s\n", index
, string(e
.what()));
2045 g_outputBuffer
="DNS over HTTPS support is not present!\n";
2050 g_lua
.writeFunction("getDOHFrontendCount", []() {
2051 setLuaNoSideEffect();
2052 return g_dohlocals
.size();
2055 g_lua
.registerFunction
<void(std::shared_ptr
<DOHFrontend
>::*)()>("reloadCertificates", [](std::shared_ptr
<DOHFrontend
> frontend
) {
2056 if (frontend
!= nullptr) {
2057 frontend
->reloadCertificates();
2061 g_lua
.registerFunction
<void(std::shared_ptr
<DOHFrontend
>::*)()>("rotateTicketsKey", [](std::shared_ptr
<DOHFrontend
> frontend
) {
2062 if (frontend
!= nullptr) {
2063 frontend
->rotateTicketsKey(time(nullptr));
2067 g_lua
.registerFunction
<void(std::shared_ptr
<DOHFrontend
>::*)(const std::string
&)>("loadTicketsKeys", [](std::shared_ptr
<DOHFrontend
> frontend
, const std::string
& file
) {
2068 if (frontend
!= nullptr) {
2069 frontend
->loadTicketsKeys(file
);
2073 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
) {
2074 if (frontend
!= nullptr) {
2075 std::vector
<std::shared_ptr
<DOHResponseMapEntry
>> newMap
;
2076 newMap
.reserve(map
.size());
2078 for (const auto& entry
: map
) {
2079 newMap
.push_back(entry
.second
);
2082 frontend
->d_responsesMap
= std::move(newMap
);
2086 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
) {
2090 #ifdef HAVE_DNS_OVER_TLS
2092 if (g_configurationDone
) {
2093 g_outputBuffer
="addTLSLocal cannot be used at runtime!\n";
2096 shared_ptr
<TLSFrontend
> frontend
= std::make_shared
<TLSFrontend
>();
2098 if (!loadTLSCertificateAndKeys("addTLSLocal", frontend
->d_tlsConfig
.d_certKeyPairs
, certFiles
, keyFiles
)) {
2102 bool reusePort
= false;
2103 int tcpFastOpenQueueSize
= 0;
2104 int tcpListenQueueSize
= 0;
2105 std::string interface
;
2109 parseLocalBindVars(vars
, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
, tcpListenQueueSize
);
2111 if (vars
->count("provider")) {
2112 frontend
->d_provider
= boost::get
<const string
>((*vars
)["provider"]);
2113 boost::algorithm::to_lower(frontend
->d_provider
);
2116 parseTLSConfig(frontend
->d_tlsConfig
, "addTLSLocal", vars
);
2120 frontend
->d_addr
= ComboAddress(addr
, 853);
2121 if (!frontend
->d_provider
.empty()) {
2122 vinfolog("Loading TLS provider '%s'", frontend
->d_provider
);
2126 vinfolog("Loading default TLS provider 'openssl'");
2128 vinfolog("Loading default TLS provider 'gnutls'");
2131 // only works pre-startup, so no sync necessary
2132 auto cs
= std::unique_ptr
<ClientState
>(new ClientState(frontend
->d_addr
, true, reusePort
, tcpFastOpenQueueSize
, interface
, cpus
));
2133 cs
->tlsFrontend
= frontend
;
2134 if (tcpListenQueueSize
> 0) {
2135 cs
->tcpListenQueueSize
= tcpListenQueueSize
;
2137 g_tlslocals
.push_back(cs
->tlsFrontend
);
2138 g_frontends
.push_back(std::move(cs
));
2140 catch(const std::exception
& e
) {
2141 g_outputBuffer
="Error: "+string(e
.what())+"\n";
2144 throw std::runtime_error("addTLSLocal() called but DNS over TLS support is not present!");
2148 g_lua
.writeFunction("showTLSContexts", []() {
2149 #ifdef HAVE_DNS_OVER_TLS
2150 setLuaNoSideEffect();
2153 boost::format
fmt("%1$-3d %2$-20.20s %|25t|%3$-14d %|40t|%4$-14d %|54t|%5$-21.21s");
2155 ret
<< (fmt
% "#" % "Address" % "# ticket keys" % "Rotation delay" % "Next rotation" ) << endl
;
2157 for (const auto& ctx
: g_tlslocals
) {
2158 ret
<< (fmt
% counter
% ctx
->d_addr
.toStringWithPort() % ctx
->getTicketsKeysCount() % ctx
->getTicketsKeyRotationDelay() % ctx
->getNextTicketsKeyRotation()) << endl
;
2161 g_outputBuffer
= ret
.str();
2163 catch(const std::exception
& e
) {
2164 g_outputBuffer
= e
.what();
2168 g_outputBuffer
="DNS over TLS support is not present!\n";
2172 g_lua
.writeFunction("getTLSContext", [](size_t index
) {
2173 std::shared_ptr
<TLSCtx
> result
= nullptr;
2174 #ifdef HAVE_DNS_OVER_TLS
2175 setLuaNoSideEffect();
2177 if (index
< g_tlslocals
.size()) {
2178 result
= g_tlslocals
.at(index
)->getContext();
2181 errlog("Error: trying to get TLS context with index %zu but we only have %zu context(s)\n", index
, g_tlslocals
.size());
2182 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";
2185 catch(const std::exception
& e
) {
2186 g_outputBuffer
="Error while trying to get TLS context with index " + std::to_string(index
) + ": "+string(e
.what())+"\n";
2187 errlog("Error while trying to get TLS context with index %zu: %s\n", index
, string(e
.what()));
2190 g_outputBuffer
="DNS over TLS support is not present!\n";
2195 g_lua
.writeFunction("getTLSFrontend", [](size_t index
) {
2196 std::shared_ptr
<TLSFrontend
> result
= nullptr;
2197 #ifdef HAVE_DNS_OVER_TLS
2198 setLuaNoSideEffect();
2200 if (index
< g_tlslocals
.size()) {
2201 result
= g_tlslocals
.at(index
);
2204 errlog("Error: trying to get TLS frontend with index %zu but we only have %zu frontends\n", index
, g_tlslocals
.size());
2205 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";
2208 catch(const std::exception
& e
) {
2209 g_outputBuffer
="Error while trying to get TLS frontend with index " + std::to_string(index
) + ": "+string(e
.what())+"\n";
2210 errlog("Error while trying to get TLS frontend with index %zu: %s\n", index
, string(e
.what()));
2213 g_outputBuffer
="DNS over TLS support is not present!\n";
2218 g_lua
.writeFunction("getTLSFrontendCount", []() {
2219 setLuaNoSideEffect();
2220 return g_tlslocals
.size();
2223 g_lua
.registerFunction
<void(std::shared_ptr
<TLSCtx
>::*)()>("rotateTicketsKey", [](std::shared_ptr
<TLSCtx
> ctx
) {
2224 if (ctx
!= nullptr) {
2225 ctx
->rotateTicketsKey(time(nullptr));
2229 g_lua
.registerFunction
<void(std::shared_ptr
<TLSCtx
>::*)(const std::string
&)>("loadTicketsKeys", [](std::shared_ptr
<TLSCtx
> ctx
, const std::string
& file
) {
2230 if (ctx
!= nullptr) {
2231 ctx
->loadTicketsKeys(file
);
2235 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
) {
2236 #ifdef HAVE_DNS_OVER_TLS
2237 if (loadTLSCertificateAndKeys("loadNewCertificatesAndKeys", frontend
->d_tlsConfig
.d_certKeyPairs
, certFiles
, keyFiles
)) {
2238 frontend
->setupTLS();
2243 g_lua
.writeFunction("reloadAllCertificates", []() {
2244 for (auto& frontend
: g_frontends
) {
2249 #ifdef HAVE_DNSCRYPT
2250 if (frontend
->dnscryptCtx
) {
2251 frontend
->dnscryptCtx
->reloadCertificates();
2253 #endif /* HAVE_DNSCRYPT */
2254 #ifdef HAVE_DNS_OVER_TLS
2255 if (frontend
->tlsFrontend
) {
2256 frontend
->tlsFrontend
->setupTLS();
2258 #endif /* HAVE_DNS_OVER_TLS */
2259 #ifdef HAVE_DNS_OVER_HTTPS
2260 if (frontend
->dohFrontend
) {
2261 frontend
->dohFrontend
->reloadCertificates();
2263 #endif /* HAVE_DNS_OVER_HTTPS */
2265 catch(const std::exception
& e
) {
2266 errlog("Error reloading certificates for frontend %s: %s", frontend
->local
.toStringWithPort(), e
.what());
2271 g_lua
.writeFunction("setAllowEmptyResponse", [](bool allow
) { g_allowEmptyResponse
=allow
; });
2273 #if defined(HAVE_LIBSSL) && defined(HAVE_OCSP_BASIC_SIGN)
2274 g_lua
.writeFunction("generateOCSPResponse", [client
](const std::string
& certFile
, const std::string
& caCert
, const std::string
& caKey
, const std::string
& outFile
, int ndays
, int nmin
) {
2279 libssl_generate_ocsp_response(certFile
, caCert
, caKey
, outFile
, ndays
, nmin
);
2281 #endif /* HAVE_LIBSSL && HAVE_OCSP_BASIC_SIGN*/
2284 vector
<std::function
<void(void)>> setupLua(bool client
, bool configCheck
, const std::string
& config
)
2286 g_launchWork
= new vector
<std::function
<void(void)>>();
2289 setupLuaConfig(client
, configCheck
);
2290 setupLuaBindings(client
);
2291 setupLuaBindingsDNSCrypt();
2292 setupLuaBindingsDNSQuestion();
2293 setupLuaBindingsKVS(client
);
2294 setupLuaBindingsPacketCache();
2295 setupLuaBindingsProtoBuf(client
, configCheck
);
2296 setupLuaInspection();
2300 #ifdef LUAJIT_VERSION
2301 g_lua
.executeCode(getLuaFFIWrappers());
2304 std::ifstream
ifs(config
);
2306 warnlog("Unable to read configuration from '%s'", config
);
2308 vinfolog("Read configuration from '%s'", config
);
2310 g_lua
.executeCode(ifs
);
2312 auto ret
= *g_launchWork
;
2313 delete g_launchWork
;
2314 g_launchWork
= nullptr;