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.
25 // we need this to get the home directory of the current user
30 #if defined (__OpenBSD__) || defined(__NetBSD__)
31 // If this is not undeffed, __attribute__ wil be redefined by /usr/include/readline/rlstdc.h
32 #undef __STRICT_ANSI__
33 #include <readline/readline.h>
34 #include <readline/history.h>
36 #include <editline/readline.h>
38 #endif /* HAVE_LIBEDIT */
40 #include "ext/json11/json11.hpp"
42 #include "connection-management.hh"
45 #include "dnsdist-console.hh"
46 #include "sodcrypto.hh"
47 #include "threadname.hh"
49 GlobalStateHolder
<NetmaskGroup
> g_consoleACL
;
50 vector
<pair
<struct timeval
, string
> > g_confDelta
;
51 std::string g_consoleKey
;
52 bool g_logConsoleConnections
{true};
53 bool g_consoleEnabled
{false};
54 uint32_t g_consoleOutputMsgMaxSize
{10000000};
56 static ConcurrentConnectionManager
s_connManager(100);
58 class ConsoleConnection
61 ConsoleConnection(const ComboAddress
& client
, FDWrapper
&& fd
): d_client(client
), d_fd(std::move(fd
))
63 if (!s_connManager
.registerConnection()) {
64 throw std::runtime_error("Too many concurrent console connections");
67 ConsoleConnection(ConsoleConnection
&& rhs
): d_client(rhs
.d_client
), d_fd(std::move(rhs
.d_fd
))
71 ConsoleConnection(const ConsoleConnection
&) = delete;
72 ConsoleConnection
& operator=(const ConsoleConnection
&) = delete;
76 if (d_fd
.getHandle() != -1) {
77 s_connManager
.releaseConnection();
83 return d_fd
.getHandle();
86 const ComboAddress
& getClient() const
92 ComboAddress d_client
;
96 void setConsoleMaximumConcurrentConnections(size_t max
)
98 s_connManager
.setMaxConcurrentConnections(max
);
101 // MUST BE CALLED UNDER A LOCK - right now the LuaLock
102 static void feedConfigDelta(const std::string
& line
)
107 gettimeofday(&now
, 0);
108 g_confDelta
.emplace_back(now
, line
);
112 static string
historyFile(const bool &ignoreHOME
= false)
117 struct passwd
*result
;
119 getpwuid_r(geteuid(), &pwd
, buf
, sizeof(buf
), &result
);
121 const char *homedir
= getenv("HOME");
123 ret
= string(pwd
.pw_dir
);
124 if (homedir
&& !ignoreHOME
) // $HOME overrides what the OS tells us
125 ret
= string(homedir
);
127 ret
= "."; // CWD if nothing works..
128 ret
.append("/.dnsdist_history");
131 #endif /* HAVE_LIBEDIT */
133 enum class ConsoleCommandResult
: uint8_t {
139 static ConsoleCommandResult
getMsgLen32(int fd
, uint32_t* len
)
143 size_t ret
= readn2(fd
, &raw
, sizeof(raw
));
145 if (ret
!= sizeof raw
) {
146 return ConsoleCommandResult::ConnectionClosed
;
150 if (*len
> g_consoleOutputMsgMaxSize
) {
151 return ConsoleCommandResult::TooLarge
;
154 return ConsoleCommandResult::Valid
;
157 return ConsoleCommandResult::ConnectionClosed
;
161 static bool putMsgLen32(int fd
, uint32_t len
)
165 uint32_t raw
= htonl(len
);
166 size_t ret
= writen2(fd
, &raw
, sizeof raw
);
167 return ret
== sizeof raw
;
174 static ConsoleCommandResult
sendMessageToServer(int fd
, const std::string
& line
, SodiumNonce
& readingNonce
, SodiumNonce
& writingNonce
, const bool outputEmptyLine
)
176 string msg
= sodEncryptSym(line
, g_consoleKey
, writingNonce
);
177 const auto msgLen
= msg
.length();
178 if (msgLen
> std::numeric_limits
<uint32_t>::max()) {
179 cerr
<< "Encrypted message is too long to be sent to the server, "<< std::to_string(msgLen
) << " > " << std::numeric_limits
<uint32_t>::max() << endl
;
180 return ConsoleCommandResult::TooLarge
;
183 putMsgLen32(fd
, static_cast<uint32_t>(msgLen
));
190 auto commandResult
= getMsgLen32(fd
, &len
);
191 if (commandResult
== ConsoleCommandResult::ConnectionClosed
) {
192 cout
<< "Connection closed by the server." << endl
;
193 return commandResult
;
195 else if (commandResult
== ConsoleCommandResult::TooLarge
) {
196 cerr
<< "Received a console message whose length (" << len
<< ") is exceeding the allowed one (" << g_consoleOutputMsgMaxSize
<< "), closing that connection" << endl
;
197 return commandResult
;
201 if (outputEmptyLine
) {
205 return ConsoleCommandResult::Valid
;
210 readn2(fd
, msg
.data(), len
);
211 msg
= sodDecryptSym(msg
, g_consoleKey
, readingNonce
);
215 return ConsoleCommandResult::Valid
;
218 void doClient(ComboAddress server
, const std::string
& command
)
220 if (!sodIsValidKey(g_consoleKey
)) {
221 cerr
<< "The currently configured console key is not valid, please configure a valid key using the setKey() directive" << endl
;
226 cout
<<"Connecting to "<<server
.toStringWithPort()<<endl
;
229 auto fd
= FDWrapper(socket(server
.sin4
.sin_family
, SOCK_STREAM
, 0));
230 if (fd
.getHandle() < 0) {
231 cerr
<<"Unable to connect to "<<server
.toStringWithPort()<<endl
;
234 SConnect(fd
.getHandle(), server
);
235 setTCPNoDelay(fd
.getHandle());
236 SodiumNonce theirs
, ours
, readingNonce
, writingNonce
;
239 writen2(fd
.getHandle(), (const char*)ours
.value
, sizeof(ours
.value
));
240 readn2(fd
.getHandle(), (char*)theirs
.value
, sizeof(theirs
.value
));
241 readingNonce
.merge(ours
, theirs
);
242 writingNonce
.merge(theirs
, ours
);
244 /* try sending an empty message, the server should send an empty
245 one back. If it closes the connection instead, we are probably
246 having a key mismatch issue. */
247 auto commandResult
= sendMessageToServer(fd
.getHandle(), "", readingNonce
, writingNonce
, false);
248 if (commandResult
== ConsoleCommandResult::ConnectionClosed
) {
249 cerr
<<"The server closed the connection right away, likely indicating a key mismatch. Please check your setKey() directive."<<endl
;
252 else if (commandResult
== ConsoleCommandResult::TooLarge
) {
256 if (!command
.empty()) {
257 sendMessageToServer(fd
.getHandle(), command
, readingNonce
, writingNonce
, false);
262 string histfile
= historyFile();
264 ifstream
history(histfile
);
266 while (getline(history
, line
)) {
267 add_history(line
.c_str());
270 ofstream
history(histfile
, std::ios_base::app
);
273 char* sline
= readline("> ");
274 rl_bind_key('\t',rl_complete
);
280 if (!line
.empty() && line
!= lastline
) {
282 history
<< sline
<<endl
;
288 if (line
== "quit") {
291 if (line
== "help" || line
== "?") {
295 /* no need to send an empty line to the server */
300 commandResult
= sendMessageToServer(fd
.getHandle(), line
, readingNonce
, writingNonce
, true);
301 if (commandResult
!= ConsoleCommandResult::Valid
) {
306 errlog("Client mode requested but libedit support is not available");
307 #endif /* HAVE_LIBEDIT */
311 static std::optional
<std::string
> getNextConsoleLine(ofstream
& history
, std::string
& lastline
)
313 char* sline
= readline("> ");
314 rl_bind_key('\t', rl_complete
);
320 if (!line
.empty() && line
!= lastline
) {
322 history
<< sline
<<endl
;
331 #else /* HAVE_LIBEDIT */
332 static std::optional
<std::string
> getNextConsoleLine()
335 if (!std::getline(std::cin
, line
)) {
340 #endif /* HAVE_LIBEDIT */
345 string histfile
= historyFile(true);
347 ifstream
history(histfile
);
349 while (getline(history
, line
)) {
350 add_history(line
.c_str());
353 ofstream
history(histfile
, std::ios_base::app
);
355 #endif /* HAVE_LIBEDIT */
359 auto line
= getNextConsoleLine(history
, lastline
);
360 #else /* HAVE_LIBEDIT */
361 auto line
= getNextConsoleLine();
362 #endif /* HAVE_LIBEDIT */
367 if (*line
== "quit") {
370 if (*line
== "help" || *line
== "?") {
376 bool withReturn
= true;
379 auto lua
= g_lua
.lock();
380 g_outputBuffer
.clear();
381 resetLuaSideEffect();
382 auto ret
= lua
->executeCode
<
386 shared_ptr
<DownstreamState
>,
388 std::unordered_map
<string
, double>
391 >(withReturn
? ("return "+*line
) : *line
);
393 if (const auto dsValue
= boost::get
<shared_ptr
<DownstreamState
>>(&*ret
)) {
395 cout
<<(*dsValue
)->getName()<<endl
;
398 else if (const auto csValue
= boost::get
<ClientState
*>(&*ret
)) {
400 cout
<<(*csValue
)->local
.toStringWithPort()<<endl
;
403 else if (const auto strValue
= boost::get
<string
>(&*ret
)) {
404 cout
<<*strValue
<<endl
;
406 else if (const auto um
= boost::get
<std::unordered_map
<string
, double> >(&*ret
)) {
407 using namespace json11
;
409 for(const auto& v
: *um
)
412 cout
<<out
.dump()<<endl
;
416 cout
<< g_outputBuffer
<< std::flush
;
419 if (!getLuaNoSideEffect()) {
420 feedConfigDelta(*line
);
423 catch (const LuaContext::SyntaxErrorException
&) {
431 catch (const LuaContext::WrongTypeException
& e
) {
432 std::cerr
<<"Command returned an object we can't print: "<<std::string(e
.what())<<std::endl
;
433 // tried to return something we don't understand
435 catch (const LuaContext::ExecutionErrorException
& e
) {
436 if (!strcmp(e
.what(), "invalid key to 'next'")) {
437 std::cerr
<<"Error parsing parameters, did you forget parameter name?";
440 std::cerr
<< e
.what();
444 std::rethrow_if_nested(e
);
446 std::cerr
<< std::endl
;
447 } catch (const std::exception
& ne
) {
448 // ne is the exception that was thrown from inside the lambda
449 std::cerr
<< ": " << ne
.what() << std::endl
;
451 catch (const PDNSException
& ne
) {
452 // ne is the exception that was thrown from inside the lambda
453 std::cerr
<< ": " << ne
.reason
<< std::endl
;
456 catch (const std::exception
& e
) {
457 std::cerr
<< e
.what() << std::endl
;
462 #ifndef DISABLE_COMPLETION
463 /**** CARGO CULT CODE AHEAD ****/
464 const std::vector
<ConsoleKeyword
> g_consoleKeywords
{
465 /* keyword, function, parameters, description */
466 { "addACL", true, "netmask", "add to the ACL set who can use this server" },
467 { "addAction", true, "DNS rule, DNS action [, {uuid=\"UUID\", name=\"name\"}]", "add a rule" },
468 { "addBPFFilterDynBlocks", true, "addresses, dynbpf[[, seconds=10], msg]", "This is the eBPF equivalent of addDynBlocks(), blocking a set of addresses for (optionally) a number of seconds, using an eBPF dynamic filter" },
469 { "addCapabilitiesToRetain", true, "capability or list of capabilities", "Linux capabilities to retain after startup, like CAP_BPF" },
470 { "addConsoleACL", true, "netmask", "add a netmask to the console ACL" },
471 { "addDNSCryptBind", true, "\"127.0.0.1:8443\", \"provider name\", \"/path/to/resolver.cert\", \"/path/to/resolver.key\", {reusePort=false, tcpFastOpenQueueSize=0, interface=\"\", cpus={}}", "listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of `provider name`, using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The fifth optional parameter is a table of parameters" },
472 { "addDOHLocal", true, "addr, certFile, keyFile [, urls [, vars]]", "listen to incoming DNS over HTTPS queries on the specified address using the specified certificate and key. The last two parameters are tables" },
473 { "addDynBlocks", true, "addresses, message[, seconds[, action]]", "block the set of addresses with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" },
474 { "addDynBlockSMT", true, "names, message[, seconds [, action]]", "block the set of names with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" },
475 { "addLocal", true, "addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface=\"\", cpus={}}]", "add `addr` to the list of addresses we listen on" },
476 { "addCacheHitResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\", name=\"name\"}}]", "add a cache hit response rule" },
477 { "addResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\", name=\"name\"}}]", "add a response rule" },
478 { "addSelfAnsweredResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\", name=\"name\"}}]", "add a self-answered response rule" },
479 { "addTLSLocal", true, "addr, certFile(s), keyFile(s) [,params]", "listen to incoming DNS over TLS queries on the specified address using the specified certificate (or list of) and key (or list of). The last parameter is a table" },
480 { "AllowAction", true, "", "let these packets go through" },
481 { "AllowResponseAction", true, "", "let these packets go through" },
482 { "AllRule", true, "", "matches all traffic" },
483 { "AndRule", true, "list of DNS rules", "matches if all sub-rules matches" },
484 { "benchRule", true, "DNS Rule [, iterations [, suffix]]", "bench the specified DNS rule" },
485 { "carbonServer", true, "serverIP, [ourname], [interval]", "report statistics to serverIP using our hostname, or 'ourname' if provided, every 'interval' seconds" },
486 { "clearConsoleHistory", true, "", "clear the internal (in-memory) history of console commands" },
487 { "clearDynBlocks", true, "", "clear all dynamic blocks" },
488 { "clearQueryCounters", true, "", "clears the query counter buffer" },
489 { "clearRules", true, "", "remove all current rules" },
490 { "controlSocket", true, "addr", "open a control socket on this address / connect to this address in client mode" },
491 { "ContinueAction", true, "action", "execute the specified action and continue the processing of the remaining rules, regardless of the return of the action" },
492 { "DelayAction", true, "milliseconds", "delay the response by the specified amount of milliseconds (UDP-only)" },
493 { "DelayResponseAction", true, "milliseconds", "delay the response by the specified amount of milliseconds (UDP-only)" },
494 { "delta", true, "", "shows all commands entered that changed the configuration" },
495 { "DNSSECRule", true, "", "matches queries with the DO bit set" },
496 { "DnstapLogAction", true, "identity, FrameStreamLogger [, alterFunction]", "send the contents of this query to a FrameStreamLogger or RemoteLogger as dnstap. `alterFunction` is a callback, receiving a DNSQuestion and a DnstapMessage, that can be used to modify the dnstap message" },
497 { "DnstapLogResponseAction", true, "identity, FrameStreamLogger [, alterFunction]", "send the contents of this response to a remote or FrameStreamLogger or RemoteLogger as dnstap. `alterFunction` is a callback, receiving a DNSResponse and a DnstapMessage, that can be used to modify the dnstap message" },
498 { "DropAction", true, "", "drop these packets" },
499 { "DropResponseAction", true, "", "drop these packets" },
500 { "DSTPortRule", true, "port", "matches questions received to the destination port specified" },
501 { "dumpStats", true, "", "print all statistics we gather" },
502 { "dynBlockRulesGroup", true, "", "return a new DynBlockRulesGroup object" },
503 { "EDNSVersionRule", true, "version", "matches queries with the specified EDNS version" },
504 { "EDNSOptionRule", true, "optcode", "matches queries with the specified EDNS0 option present" },
505 { "ERCodeAction", true, "ercode", "Reply immediately by turning the query into a response with the specified EDNS extended rcode" },
506 { "ERCodeRule", true, "rcode", "matches responses with the specified extended rcode (EDNS0)" },
507 { "exceedNXDOMAINs", true, "rate, seconds", "get set of addresses that exceed `rate` NXDOMAIN/s over `seconds` seconds" },
508 { "exceedQRate", true, "rate, seconds", "get set of address that exceed `rate` queries/s over `seconds` seconds" },
509 { "exceedQTypeRate", true, "type, rate, seconds", "get set of address that exceed `rate` queries/s for queries of type `type` over `seconds` seconds" },
510 { "exceedRespByterate", true, "rate, seconds", "get set of addresses that exceeded `rate` bytes/s answers over `seconds` seconds" },
511 { "exceedServFails", true, "rate, seconds", "get set of addresses that exceed `rate` servfails/s over `seconds` seconds" },
512 { "firstAvailable", false, "", "picks the server with the lowest `order` that has not exceeded its QPS limit" },
513 { "fixupCase", true, "bool", "if set (default to no), rewrite the first qname of the question part of the answer to match the one from the query. It is only useful when you have a downstream server that messes up the case of the question qname in the answer" },
514 { "generateDNSCryptCertificate", true, "\"/path/to/providerPrivate.key\", \"/path/to/resolver.cert\", \"/path/to/resolver.key\", serial, validFrom, validUntil", "generate a new resolver private key and related certificate, valid from the `validFrom` timestamp until the `validUntil` one, signed with the provider private key" },
515 { "generateDNSCryptProviderKeys", true, "\"/path/to/providerPublic.key\", \"/path/to/providerPrivate.key\"", "generate a new provider keypair" },
516 { "getAction", true, "n", "Returns the Action associated with rule n" },
517 { "getBind", true, "n", "returns the listener at index n" },
518 { "getBindCount", true, "", "returns the number of listeners all kinds" },
519 { "getDNSCryptBind", true, "n", "return the `DNSCryptContext` object corresponding to the bind `n`" },
520 { "getDNSCryptBindCount", true, "", "returns the number of DNSCrypt listeners" },
521 { "getDOHFrontend", true, "n", "returns the DOH frontend with index n" },
522 { "getDOHFrontendCount", true, "", "returns the number of DoH listeners" },
523 { "getListOfAddressesOfNetworkInterface", true, "itf", "returns the list of addresses configured on a given network interface, as strings" },
524 { "getListOfNetworkInterfaces", true, "", "returns the list of network interfaces present on the system, as strings" },
525 { "getOutgoingTLSSessionCacheSize", true, "", "returns the number of TLS sessions (for outgoing connections) currently cached" },
526 { "getPool", true, "name", "return the pool named `name`, or \"\" for the default pool" },
527 { "getPoolServers", true, "pool", "return servers part of this pool" },
528 { "getQueryCounters", true, "[max=10]", "show current buffer of query counters, limited by 'max' if provided" },
529 { "getResponseRing", true, "", "return the current content of the response ring" },
530 { "getRespRing", true, "", "return the qname/rcode content of the response ring" },
531 { "getServer", true, "id", "returns server with index 'n' or whose uuid matches if 'id' is an UUID string" },
532 { "getServers", true, "", "returns a table with all defined servers" },
533 { "getStatisticsCounters", true, "", "returns a map of statistic counters" },
534 { "getTopCacheHitResponseRules", true, "[top]", "return the `top` cache-hit response rules" },
535 { "getTopResponseRules", true, "[top]", "return the `top` response rules" },
536 { "getTopRules", true, "[top]", "return the `top` rules" },
537 { "getTopSelfAnsweredResponseRules", true, "[top]", "return the `top` self-answered response rules" },
538 { "getTLSContext", true, "n", "returns the TLS context with index n" },
539 { "getTLSFrontend", true, "n", "returns the TLS frontend with index n" },
540 { "getTLSFrontendCount", true, "", "returns the number of DoT listeners" },
541 { "grepq", true, "Netmask|DNS Name|100ms|{\"::1\", \"powerdns.com\", \"100ms\"} [, n]", "shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms" },
542 { "hashPassword", true, "password [, workFactor]", "Returns a hashed and salted version of the supplied password, usable with 'setWebserverConfig()'"},
543 { "HTTPHeaderRule", true, "name, regex", "matches DoH queries with a HTTP header 'name' whose content matches the regular expression 'regex'"},
544 { "HTTPPathRegexRule", true, "regex", "matches DoH queries whose HTTP path matches 'regex'"},
545 { "HTTPPathRule", true, "path", "matches DoH queries whose HTTP path is an exact match to 'path'"},
546 { "HTTPStatusAction", true, "status, reason, body", "return an HTTP response"},
547 { "inClientStartup", true, "", "returns true during console client parsing of configuration" },
548 { "includeDirectory", true, "path", "include configuration files from `path`" },
549 { "KeyValueLookupKeyQName", true, "[wireFormat]", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the qname of the query, either in wire format (default) or in plain text if 'wireFormat' is false" },
550 { "KeyValueLookupKeySourceIP", true, "[v4Mask [, v6Mask [, includePort]]]", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the (possibly bitmasked) source IP of the client in network byte-order." },
551 { "KeyValueLookupKeySuffix", true, "[minLabels [,wireFormat]]", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return a vector of keys based on the labels of the qname in DNS wire format or plain text" },
552 { "KeyValueLookupKeyTag", true, "tag", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the value of the corresponding tag for this query, if it exists" },
553 { "KeyValueStoreLookupAction", true, "kvs, lookupKey, destinationTag", "does a lookup into the key value store referenced by 'kvs' using the key returned by 'lookupKey', and storing the result if any into the tag named 'destinationTag'" },
554 { "KeyValueStoreRangeLookupAction", true, "kvs, lookupKey, destinationTag", "does a range-based lookup into the key value store referenced by 'kvs' using the key returned by 'lookupKey', and storing the result if any into the tag named 'destinationTag'" },
555 { "KeyValueStoreLookupRule", true, "kvs, lookupKey", "matches queries if the key is found in the specified Key Value store" },
556 { "KeyValueStoreRangeLookupRule", true, "kvs, lookupKey", "matches queries if the key is found in the specified Key Value store" },
557 { "leastOutstanding", false, "", "Send traffic to downstream server with least outstanding queries, with the lowest 'order', and within that the lowest recent latency"},
558 { "loadTLSEngine", true, "engineName [, defaultString]", "Load the OpenSSL engine named 'engineName', setting the engine default string to 'defaultString' if supplied"},
559 { "LogAction", true, "[filename], [binary], [append], [buffered]", "Log a line for each query, to the specified file if any, to the console (require verbose) otherwise. When logging to a file, the `binary` optional parameter specifies whether we log in binary form (default) or in textual form, the `append` optional parameter specifies whether we open the file for appending or truncate each time (default), and the `buffered` optional parameter specifies whether writes to the file are buffered (default) or not." },
560 { "LogResponseAction", true, "[filename], [append], [buffered]", "Log a line for each response, to the specified file if any, to the console (require verbose) otherwise. The `append` optional parameter specifies whether we open the file for appending or truncate each time (default), and the `buffered` optional parameter specifies whether writes to the file are buffered (default) or not." },
561 { "LuaAction", true, "function", "Invoke a Lua function that accepts a DNSQuestion" },
562 { "LuaFFIAction", true, "function", "Invoke a Lua FFI function that accepts a DNSQuestion" },
563 { "LuaFFIPerThreadAction", true, "function", "Invoke a Lua FFI function that accepts a DNSQuestion, with a per-thread Lua context" },
564 { "LuaFFIPerThreadResponseAction", true, "function", "Invoke a Lua FFI function that accepts a DNSResponse, with a per-thread Lua context" },
565 { "LuaFFIResponseAction", true, "function", "Invoke a Lua FFI function that accepts a DNSResponse" },
566 { "LuaFFIRule", true, "function", "Invoke a Lua FFI function that filters DNS questions" },
567 { "LuaResponseAction", true, "function", "Invoke a Lua function that accepts a DNSResponse" },
568 { "LuaRule", true, "function", "Invoke a Lua function that filters DNS questions" },
570 { "makeIPCipherKey", true, "password", "generates a 16-byte key that can be used to pseudonymize IP addresses with IP cipher" },
571 #endif /* HAVE_IPCIPHER */
572 { "makeKey", true, "", "generate a new server access key, emit configuration line ready for pasting" },
573 { "makeRule", true, "rule", "Make a NetmaskGroupRule() or a SuffixMatchNodeRule(), depending on how it is called" } ,
574 { "MaxQPSIPRule", true, "qps, [v4Mask=32 [, v6Mask=64 [, burst=qps [, expiration=300 [, cleanupDelay=60]]]]]", "matches traffic exceeding the qps limit per subnet" },
575 { "MaxQPSRule", true, "qps", "matches traffic **not** exceeding this qps limit" },
576 { "mvCacheHitResponseRule", true, "from, to", "move cache hit response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" },
577 { "mvCacheHitResponseRuleToTop", true, "", "move the last cache hit response rule to the first position" },
578 { "mvResponseRule", true, "from, to", "move response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" },
579 { "mvResponseRuleToTop", true, "", "move the last response rule to the first position" },
580 { "mvRule", true, "from, to", "move rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule, in which case the rule will be moved to the last position" },
581 { "mvRuleToTop", true, "", "move the last rule to the first position" },
582 { "mvSelfAnsweredResponseRule", true, "from, to", "move self-answered response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" },
583 { "mvSelfAnsweredResponseRuleToTop", true, "", "move the last self-answered response rule to the first position" },
584 { "NetmaskGroupRule", true, "nmg[, src]", "Matches traffic from/to the network range specified in nmg. Set the src parameter to false to match nmg against destination address instead of source address. This can be used to differentiate between clients" },
585 { "newBPFFilter", true, "maxV4, maxV6, maxQNames", "Return a new eBPF socket filter with a maximum of maxV4 IPv4, maxV6 IPv6 and maxQNames qname entries in the block table" },
586 { "newCA", true, "address", "Returns a ComboAddress based on `address`" },
588 { "newCDBKVStore", true, "fname, refreshDelay", "Return a new KeyValueStore object associated to the corresponding CDB database" },
590 { "newDNSName", true, "name", "make a DNSName based on this .-terminated name" },
591 { "newDNSNameSet", true, "", "returns a new DNSNameSet" },
592 { "newDynBPFFilter", true, "bpf", "Return a new dynamic eBPF filter associated to a given BPF Filter" },
593 { "newFrameStreamTcpLogger", true, "addr [, options]", "create a FrameStream logger object writing to a TCP address (addr should be ip:port), to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" },
594 { "newFrameStreamUnixLogger", true, "socket [, options]", "create a FrameStream logger object writing to a local unix socket, to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" },
596 { "newLMDBKVStore", true, "fname, dbName [, noLock]", "Return a new KeyValueStore object associated to the corresponding LMDB database" },
598 { "newNMG", true, "", "Returns a NetmaskGroup" },
599 { "newPacketCache", true, "maxEntries[, maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, options={}]", "return a new Packet Cache" },
600 { "newQPSLimiter", true, "rate, burst", "configure a QPS limiter with that rate and that burst capacity" },
601 { "newRemoteLogger", true, "address:port [, timeout=2, maxQueuedEntries=100, reconnectWaitTime=1]", "create a Remote Logger object, to use with `RemoteLogAction()` and `RemoteLogResponseAction()`" },
602 { "newRuleAction", true, "DNS rule, DNS action [, {uuid=\"UUID\", name=\"name\"}]", "return a pair of DNS Rule and DNS Action, to be used with `setRules()`" },
603 { "newServer", true, "{address=\"ip:port\", qps=1000, order=1, weight=10, pool=\"abuse\", retries=5, tcpConnectTimeout=5, tcpSendTimeout=30, tcpRecvTimeout=30, checkName=\"a.root-servers.net.\", checkType=\"A\", maxCheckFailures=1, mustResolve=false, useClientSubnet=true, source=\"address|interface name|address@interface\", sockets=1, reconnectOnUp=false}", "instantiate a server" },
604 { "newServerPolicy", true, "name, function", "create a policy object from a Lua function" },
605 { "newSuffixMatchNode", true, "", "returns a new SuffixMatchNode" },
606 { "newSVCRecordParameters", true, "priority, target, mandatoryParams, alpns, noDefaultAlpn [, port [, ech [, ipv4hints [, ipv6hints [, additionalParameters ]]]]]", "return a new SVCRecordParameters object, to use with SpoofSVCAction" },
607 { "NegativeAndSOAAction", true, "nxd, zone, ttl, mname, rname, serial, refresh, retry, expire, minimum [, options]", "Turn a query into a NXDomain or NoData answer and sets a SOA record in the additional section" },
608 { "NoneAction", true, "", "Does nothing. Subsequent rules are processed after this action" },
609 { "NotRule", true, "selector", "Matches the traffic if the selector rule does not match" },
610 { "OpcodeRule", true, "code", "Matches queries with opcode code. code can be directly specified as an integer, or one of the built-in DNSOpcodes" },
611 { "OrRule", true, "selectors", "Matches the traffic if one or more of the the selectors rules does match" },
612 { "PoolAction", true, "poolname", "set the packet into the specified pool" },
613 { "PoolAvailableRule", true, "poolname", "Check whether a pool has any servers available to handle queries" },
614 { "PoolOutstandingRule", true, "poolname, limit", "Check whether a pool has outstanding queries above limit" },
615 { "printDNSCryptProviderFingerprint", true, "\"/path/to/providerPublic.key\"", "display the fingerprint of the provided resolver public key" },
616 { "ProbaRule", true, "probability", "Matches queries with a given probability. 1.0 means always" },
617 { "ProxyProtocolValueRule", true, "type [, value]", "matches queries with a specified Proxy Protocol TLV value of that type, optionally matching the content of the option as well" },
618 { "QClassRule", true, "qclass", "Matches queries with the specified qclass. class can be specified as an integer or as one of the built-in DNSClass" },
619 { "QNameLabelsCountRule", true, "min, max", "matches if the qname has less than `min` or more than `max` labels" },
620 { "QNameRule", true, "qname", "matches queries with the specified qname" },
621 { "QNameSetRule", true, "set", "Matches if the set contains exact qname" },
622 { "QNameWireLengthRule", true, "min, max", "matches if the qname's length on the wire is less than `min` or more than `max` bytes" },
623 { "QPSAction", true, "maxqps", "Drop a packet if it does exceed the maxqps queries per second limits. Letting the subsequent rules apply otherwise" },
624 { "QPSPoolAction", true, "maxqps, poolname", "Send the packet into the specified pool only if it does not exceed the maxqps queries per second limits. Letting the subsequent rules apply otherwise" },
625 { "QTypeRule", true, "qtype", "matches queries with the specified qtype" },
626 { "RCodeAction", true, "rcode", "Reply immediately by turning the query into a response with the specified rcode" },
627 { "RCodeRule", true, "rcode", "matches responses with the specified rcode" },
628 { "RDRule", true, "", "Matches queries with the RD flag set" },
629 { "RecordsCountRule", true, "section, minCount, maxCount", "Matches if there is at least minCount and at most maxCount records in the section section. section can be specified as an integer or as a DNS Packet Sections" },
630 { "RecordsTypeCountRule", true, "section, qtype, minCount, maxCount", "Matches if there is at least minCount and at most maxCount records of type type in the section section" },
631 { "RegexRule", true, "regex", "matches the query name against the supplied regex" },
632 { "registerDynBPFFilter", true, "DynBPFFilter", "register this dynamic BPF filter into the web interface so that its counters are displayed" },
633 { "reloadAllCertificates", true, "", "reload all DNSCrypt and TLS certificates, along with their associated keys" },
634 { "RemoteLogAction", true, "RemoteLogger [, alterFunction [, serverID]]", "send the content of this query to a remote logger via Protocol Buffer. `alterFunction` is a callback, receiving a DNSQuestion and a DNSDistProtoBufMessage, that can be used to modify the Protocol Buffer content, for example for anonymization purposes. `serverID` is the server identifier." },
635 { "RemoteLogResponseAction", true, "RemoteLogger [,alterFunction [,includeCNAME [, serverID]]]", "send the content of this response to a remote logger via Protocol Buffer. `alterFunction` is the same callback than the one in `RemoteLogAction` and `includeCNAME` indicates whether CNAME records inside the response should be parsed and exported. The default is to only exports A and AAAA records. `serverID` is the server identifier." },
636 { "requestTCPStatesDump", true, "", "Request a dump of the TCP states (incoming connections, outgoing connections) during the next scan. Useful for debugging purposes only" },
637 { "rmACL", true, "netmask", "remove netmask from ACL" },
638 { "rmCacheHitResponseRule", true, "id", "remove cache hit response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID" },
639 { "rmResponseRule", true, "id", "remove response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID" },
640 { "rmRule", true, "id", "remove rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID" },
641 { "rmSelfAnsweredResponseRule", true, "id", "remove self-answered response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID" },
642 { "rmServer", true, "id", "remove server with index 'id' or whose uuid matches if 'id' is an UUID string" },
643 { "roundrobin", false, "", "Simple round robin over available servers" },
644 { "sendCustomTrap", true, "str", "send a custom `SNMP` trap from Lua, containing the `str` string"},
645 { "setACL", true, "{netmask, netmask}", "replace the ACL set with these netmasks. Use `setACL({})` to reset the list, meaning no one can use us" },
646 { "setACLFromFile", true, "file", "replace the ACL set with netmasks in this file" },
647 { "setAddEDNSToSelfGeneratedResponses", true, "add", "set whether to add EDNS to self-generated responses, provided that the initial query had EDNS" },
648 { "setAllowEmptyResponse", true, "allow", "Set to true (defaults to false) to allow empty responses (qdcount=0) with a NoError or NXDomain rcode (default) from backends" },
649 { "setAPIWritable", true, "bool, dir", "allow modifications via the API. if `dir` is set, it must be a valid directory where the configuration files will be written by the API" },
650 { "setCacheCleaningDelay", true, "num", "Set the interval in seconds between two runs of the cache cleaning algorithm, removing expired entries" },
651 { "setCacheCleaningPercentage", true, "num", "Set the percentage of the cache that the cache cleaning algorithm will try to free by removing expired entries. By default (100), all expired entries are remove" },
652 { "setConsistentHashingBalancingFactor", true, "factor", "Set the balancing factor for bounded-load consistent hashing" },
653 { "setConsoleACL", true, "{netmask, netmask}", "replace the console ACL set with these netmasks" },
654 { "setConsoleConnectionsLogging", true, "enabled", "whether to log the opening and closing of console connections" },
655 { "setConsoleMaximumConcurrentConnections", true, "max", "Set the maximum number of concurrent console connections" },
656 { "setConsoleOutputMaxMsgSize", true, "messageSize", "set console message maximum size in bytes, default is 10 MB" },
657 { "setDefaultBPFFilter", true, "filter", "When used at configuration time, the corresponding BPFFilter will be attached to every bind" },
658 { "setDoHDownstreamCleanupInterval", true, "interval", "minimum interval in seconds between two cleanups of the idle DoH downstream connections" },
659 { "setDoHDownstreamMaxIdleTime", true, "time", "Maximum time in seconds that a downstream DoH connection to a backend might stay idle" },
660 { "setDynBlocksAction", true, "action", "set which action is performed when a query is blocked. Only DNSAction.Drop (the default) and DNSAction.Refused are supported" },
661 { "setDynBlocksPurgeInterval", true, "sec", "set how often the expired dynamic block entries should be removed" },
662 { "setDropEmptyQueries", true, "drop", "Whether to drop empty queries right away instead of sending a NOTIMP response" },
663 { "setECSOverride", true, "bool", "whether to override an existing EDNS Client Subnet value in the query" },
664 { "setECSSourcePrefixV4", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv4 queries" },
665 { "setECSSourcePrefixV6", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv6 queries" },
666 { "setKey", true, "key", "set access key to that key" },
667 { "setLocal", true, "addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface=\"\", cpus={}}]", "reset the list of addresses we listen on to this address" },
668 { "setMaxCachedDoHConnectionsPerDownstream", true, "max", "Set the maximum number of inactive DoH connections to a backend cached by each worker DoH thread" },
669 { "setMaxCachedTCPConnectionsPerDownstream", true, "max", "Set the maximum number of inactive TCP connections to a backend cached by each worker TCP thread" },
670 { "setMaxTCPClientThreads", true, "n", "set the maximum of TCP client threads, handling TCP connections" },
671 { "setMaxTCPConnectionDuration", true, "n", "set the maximum duration of an incoming TCP connection, in seconds. 0 means unlimited" },
672 { "setMaxTCPConnectionsPerClient", true, "n", "set the maximum number of TCP connections per client. 0 means unlimited" },
673 { "setMaxTCPQueriesPerConnection", true, "n", "set the maximum number of queries in an incoming TCP connection. 0 means unlimited" },
674 { "setMaxTCPQueuedConnections", true, "n", "set the maximum number of TCP connections queued (waiting to be picked up by a client thread)" },
675 { "setMaxUDPOutstanding", true, "n", "set the maximum number of outstanding UDP queries to a given backend server. This can only be set at configuration time and defaults to 65535" },
676 { "setPayloadSizeOnSelfGeneratedAnswers", true, "payloadSize", "set the UDP payload size advertised via EDNS on self-generated responses" },
677 { "setPoolServerPolicy", true, "policy, pool", "set the server selection policy for this pool to that policy" },
678 { "setPoolServerPolicyLua", true, "name, function, pool", "set the server selection policy for this pool to one named 'name' and provided by 'function'" },
679 { "setPoolServerPolicyLuaFFI", true, "name, function, pool", "set the server selection policy for this pool to one named 'name' and provided by 'function'" },
680 { "setPoolServerPolicyLuaFFIPerThread", true, "name, code", "set server selection policy for this pool to one named 'name' and returned by the Lua FFI code passed in 'code'" },
681 { "setProxyProtocolACL", true, "{netmask, netmask}", "Set the netmasks who are allowed to send Proxy Protocol headers in front of queries/connections" },
682 { "setProxyProtocolApplyACLToProxiedClients", true, "apply", "Whether the general ACL should be applied to the source IP address gathered from a Proxy Protocol header, in addition to being first applied to the source address seen by dnsdist" },
683 { "setProxyProtocolMaximumPayloadSize", true, "max", "Set the maximum size of a Proxy Protocol payload, in bytes" },
684 { "setQueryCount", true, "bool", "set whether queries should be counted" },
685 { "setQueryCountFilter", true, "func", "filter queries that would be counted, where `func` is a function with parameter `dq` which decides whether a query should and how it should be counted" },
686 { "setRingBuffersLockRetries", true, "n", "set the number of attempts to get a non-blocking lock to a ringbuffer shard before blocking" },
687 { "setRingBuffersSize", true, "n [, numberOfShards]", "set the capacity of the ringbuffers used for live traffic inspection to `n`, and optionally the number of shards to use to `numberOfShards`" },
688 { "setRoundRobinFailOnNoServer", true, "value", "By default the roundrobin load-balancing policy will still try to select a backend even if all backends are currently down. Setting this to true will make the policy fail and return that no server is available instead" },
689 { "setRules", true, "list of rules", "replace the current rules with the supplied list of pairs of DNS Rules and DNS Actions (see `newRuleAction()`)" },
690 { "setSecurityPollInterval", true, "n", "set the security polling interval to `n` seconds" },
691 { "setSecurityPollSuffix", true, "suffix", "set the security polling suffix to the specified value" },
692 { "setServerPolicy", true, "policy", "set server selection policy to that policy" },
693 { "setServerPolicyLua", true, "name, function", "set server selection policy to one named 'name' and provided by 'function'" },
694 { "setServerPolicyLuaFFI", true, "name, function", "set server selection policy to one named 'name' and provided by the Lua FFI 'function'" },
695 { "setServerPolicyLuaFFIPerThread", true, "name, code", "set server selection policy to one named 'name' and returned by the Lua FFI code passed in 'code'" },
696 { "setServFailWhenNoServer", true, "bool", "if set, return a ServFail when no servers are available, instead of the default behaviour of dropping the query" },
697 { "setStaleCacheEntriesTTL", true, "n", "allows using cache entries expired for at most n seconds when there is no backend available to answer for a query" },
698 { "setSyslogFacility", true, "facility", "set the syslog logging facility to 'facility'. Defaults to LOG_DAEMON" },
699 { "setTCPDownstreamCleanupInterval", true, "interval", "minimum interval in seconds between two cleanups of the idle TCP downstream connections" },
700 { "setTCPFastOpenKey", true, "string", "TCP Fast Open Key" },
701 { "setTCPDownstreamMaxIdleTime", true, "time", "Maximum time in seconds that a downstream TCP connection to a backend might stay idle" },
702 { "setTCPInternalPipeBufferSize", true, "size", "Set the size in bytes of the internal buffer of the pipes used internally to distribute connections to TCP (and DoT) workers threads" },
703 { "setTCPRecvTimeout", true, "n", "set the read timeout on TCP connections from the client, in seconds" },
704 { "setTCPSendTimeout", true, "n", "set the write timeout on TCP connections from the client, in seconds" },
705 { "setUDPMultipleMessagesVectorSize", true, "n", "set the size of the vector passed to recvmmsg() to receive UDP messages. Default to 1 which means that the feature is disabled and recvmsg() is used instead" },
706 { "setUDPSocketBufferSizes", true, "recv, send", "Set the size of the receive (SO_RCVBUF) and send (SO_SNDBUF) buffers for incoming UDP sockets" },
707 { "setUDPTimeout", true, "n", "set the maximum time dnsdist will wait for a response from a backend over UDP, in seconds" },
708 { "setVerbose", true, "bool", "set whether log messages at the verbose level will be logged" },
709 { "setVerboseHealthChecks", true, "bool", "set whether health check errors will be logged" },
710 { "setWebserverConfig", true, "[{password=string, apiKey=string, customHeaders, statsRequireAuthentication}]", "Updates webserver configuration" },
711 { "setWeightedBalancingFactor", true, "factor", "Set the balancing factor for bounded-load weighted policies (whashed, wrandom)" },
712 { "setWHashedPertubation", true, "value", "Set the hash perturbation value to be used in the whashed policy instead of a random one, allowing to have consistent whashed results on different instance" },
713 { "show", true, "string", "outputs `string`" },
714 { "showACL", true, "", "show our ACL set" },
715 { "showBinds", true, "", "show listening addresses (frontends)" },
716 { "showCacheHitResponseRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined cache hit response rules, optionally with their UUIDs and optionally truncated to a given width" },
717 { "showConsoleACL", true, "", "show our current console ACL set" },
718 { "showDNSCryptBinds", true, "", "display the currently configured DNSCrypt binds" },
719 { "showDOHFrontends", true, "", "list all the available DOH frontends" },
720 { "showDOHResponseCodes", true, "", "show the HTTP response code statistics for the DoH frontends"},
721 { "showDynBlocks", true, "", "show dynamic blocks in force" },
722 { "showPools", true, "", "show the available pools" },
723 { "showPoolServerPolicy", true, "pool", "show server selection policy for this pool" },
724 { "showResponseLatency", true, "", "show a plot of the response time latency distribution" },
725 { "showResponseRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined response rules, optionally with their UUIDs and optionally truncated to a given width" },
726 { "showRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined rules, optionally with their UUIDs and optionally truncated to a given width" },
727 { "showSecurityStatus", true, "", "Show the security status"},
728 { "showSelfAnsweredResponseRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined self-answered response rules, optionally with their UUIDs and optionally truncated to a given width" },
729 { "showServerPolicy", true, "", "show name of currently operational server selection policy" },
730 { "showServers", true, "[{showUUIDs=false}]", "output all servers, optionally with their UUIDs" },
731 { "showTCPStats", true, "", "show some statistics regarding TCP" },
732 { "showTLSContexts", true, "", "list all the available TLS contexts" },
733 { "showTLSErrorCounters", true, "", "show metrics about TLS handshake failures" },
734 { "showVersion", true, "", "show the current version" },
735 { "showWebserverConfig", true, "", "Show the current webserver configuration" },
736 { "shutdown", true, "", "shut down `dnsdist`" },
737 { "snmpAgent", true, "enableTraps [, daemonSocket]", "enable `SNMP` support. `enableTraps` is a boolean indicating whether traps should be sent and `daemonSocket` an optional string specifying how to connect to the daemon agent"},
738 { "SetAdditionalProxyProtocolValueAction", true, "type, value", "Add a Proxy Protocol TLV value of this type" },
739 { "SetDisableECSAction", true, "", "Disable the sending of ECS to the backend. Subsequent rules are processed after this action." },
740 { "SetDisableValidationAction", true, "", "set the CD bit in the question, let it go through" },
741 { "SetECSAction", true, "v4[, v6]", "Set the ECS prefix and prefix length sent to backends to an arbitrary value" },
742 { "SetECSOverrideAction", true, "override", "Whether an existing EDNS Client Subnet value should be overridden (true) or not (false). Subsequent rules are processed after this action" },
743 { "SetECSPrefixLengthAction", true, "v4, v6", "Set the ECS prefix length. Subsequent rules are processed after this action" },
744 { "SetMacAddrAction", true, "option", "Add the source MAC address to the query as EDNS0 option option. This action is currently only supported on Linux. Subsequent rules are processed after this action" },
745 { "SetEDNSOptionAction", true, "option, data", "Add arbitrary EDNS option and data to the query. Subsequent rules are processed after this action" },
746 { "SetNoRecurseAction", true, "", "strip RD bit from the question, let it go through" },
747 { "setOutgoingDoHWorkerThreads", true, "n", "Number of outgoing DoH worker threads" },
748 { "SetProxyProtocolValuesAction", true, "values", "Set the Proxy-Protocol values for this queries to 'values'" },
749 { "SetSkipCacheAction", true, "", "Don’t lookup the cache for this query, don’t store the answer" },
750 { "SetSkipCacheResponseAction", true, "", "Don’t store this response into the cache" },
751 { "SetTagAction", true, "name, value", "set the tag named 'name' to the given value" },
752 { "SetTagResponseAction", true, "name, value", "set the tag named 'name' to the given value" },
753 { "SetTempFailureCacheTTLAction", true, "ttl", "set packetcache TTL for temporary failure replies" },
754 { "SNIRule", true, "name", "Create a rule which matches on the incoming TLS SNI value, if any (DoT or DoH)" },
755 { "SNMPTrapAction", true, "[reason]", "send an SNMP trap, adding the optional `reason` string as the query description"},
756 { "SNMPTrapResponseAction", true, "[reason]", "send an SNMP trap, adding the optional `reason` string as the response description"},
757 { "SpoofAction", true, "ip|list of ips [, options]", "forge a response with the specified IPv4 (for an A query) or IPv6 (for an AAAA). If you specify multiple addresses, all that match the query type (A, AAAA or ANY) will get spoofed in" },
758 { "SpoofCNAMEAction", true, "cname [, options]", "Forge a response with the specified CNAME value" },
759 { "SpoofRawAction", true, "raw|list of raws [, options]", "Forge a response with the specified record data as raw bytes. If you specify multiple raws (it is assumed they match the query type), all will get spoofed in" },
760 { "SpoofSVCAction", true, "list of svcParams [, options]", "Forge a response with the specified SVC record data" } ,
761 { "SuffixMatchNodeRule", true, "smn[, quiet]", "Matches based on a group of domain suffixes for rapid testing of membership. Pass true as second parameter to prevent listing of all domains matched" },
762 { "TagRule", true, "name [, value]", "matches if the tag named 'name' is present, with the given 'value' matching if any" },
763 { "TCAction", true, "", "create answer to query with TC and RD bits set, to move to TCP" },
764 { "TCPRule", true, "[tcp]", "Matches question received over TCP if tcp is true, over UDP otherwise" },
765 { "TeeAction", true, "remote [, addECS]", "send copy of query to remote, optionally adding ECS info" },
766 { "testCrypto", true, "", "test of the crypto all works" },
767 { "TimedIPSetRule", true, "", "Create a rule which matches a set of IP addresses which expire"},
768 { "topBandwidth", true, "top", "show top-`top` clients that consume the most bandwidth over length of ringbuffer" },
769 { "topCacheHitResponseRules", true, "[top][, vars]", "show `top` cache-hit response rules" },
770 { "topClients", true, "n", "show top-`n` clients sending the most queries over length of ringbuffer" },
771 { "topQueries", true, "n[, labels]", "show top 'n' queries, as grouped when optionally cut down to 'labels' labels" },
772 { "topResponses", true, "n, kind[, labels]", "show top 'n' responses with RCODE=kind (0=NO Error, 2=ServFail, 3=NXDomain), as grouped when optionally cut down to 'labels' labels" },
773 { "topResponseRules", true, "[top][, vars]", "show `top` response rules" },
774 { "topRules", true, "[top][, vars]", "show `top` rules" },
775 { "topSelfAnsweredResponseRules", true, "[top][, vars]", "show `top` self-answered response rules" },
776 { "topSlow", true, "[top][, limit][, labels]", "show `top` queries slower than `limit` milliseconds, grouped by last `labels` labels" },
777 { "TrailingDataRule", true, "", "Matches if the query has trailing data" },
778 { "truncateTC", true, "bool", "if set (defaults to no starting with dnsdist 1.2.0) truncate TC=1 answers so they are actually empty. Fixes an issue for PowerDNS Authoritative Server 2.9.22. Note: turning this on breaks compatibility with RFC 6891." },
779 { "unregisterDynBPFFilter", true, "DynBPFFilter", "unregister this dynamic BPF filter" },
780 { "webserver", true, "address:port", "launch a webserver with stats on that address" },
781 { "whashed", false, "", "Weighted hashed ('sticky') distribution over available servers, based on the server 'weight' parameter" },
782 { "chashed", false, "", "Consistent hashed ('sticky') distribution over available servers, also based on the server 'weight' parameter" },
783 { "wrandom", false, "", "Weighted random over available servers, based on the server 'weight' parameter" },
786 #if defined(HAVE_LIBEDIT)
788 static char* my_generator(const char* text
, int state
)
791 /* to keep it readable, we try to keep only 4 keywords per line
792 and to start a new line when the first letter changes */
793 static int s_counter
= 0;
799 for (const auto& keyword
: g_consoleKeywords
) {
800 if (boost::starts_with(keyword
.name
, t
) && counter
++ == s_counter
) {
801 std::string
value(keyword
.name
);
803 if (keyword
.function
) {
805 if (keyword
.parameters
.empty()) {
809 return strdup(value
.c_str());
815 char** my_completion( const char * text
, int start
, int end
)
819 matches
= rl_completion_matches ((char*)text
, &my_generator
);
822 // skip default filename completion.
823 rl_attempted_completion_over
= 1;
828 #endif /* HAVE_LIBEDIT */
829 #endif /* DISABLE_COMPLETION */
831 static void controlClientThread(ConsoleConnection
&& conn
)
834 setThreadName("dnsdist/conscli");
836 setTCPNoDelay(conn
.getFD());
838 SodiumNonce theirs
, ours
, readingNonce
, writingNonce
;
840 readn2(conn
.getFD(), (char*)theirs
.value
, sizeof(theirs
.value
));
841 writen2(conn
.getFD(), (char*)ours
.value
, sizeof(ours
.value
));
842 readingNonce
.merge(ours
, theirs
);
843 writingNonce
.merge(theirs
, ours
);
847 if (getMsgLen32(conn
.getFD(), &len
) != ConsoleCommandResult::Valid
) {
852 /* just ACK an empty message
853 with an empty response */
854 putMsgLen32(conn
.getFD(), 0);
860 readn2(conn
.getFD(), line
.data(), len
);
862 line
= sodDecryptSym(line
, g_consoleKey
, readingNonce
);
866 bool withReturn
= true;
869 auto lua
= g_lua
.lock();
871 g_outputBuffer
.clear();
872 resetLuaSideEffect();
873 auto ret
= lua
->executeCode
<
877 shared_ptr
<DownstreamState
>,
879 std::unordered_map
<string
, double>
882 >(withReturn
? ("return "+line
) : line
);
885 if (const auto dsValue
= boost::get
<shared_ptr
<DownstreamState
>>(&*ret
)) {
887 response
= (*dsValue
)->getName()+"\n";
892 else if (const auto csValue
= boost::get
<ClientState
*>(&*ret
)) {
894 response
= (*csValue
)->local
.toStringWithPort()+"\n";
899 else if (const auto strValue
= boost::get
<string
>(&*ret
)) {
900 response
= *strValue
+"\n";
902 else if (const auto um
= boost::get
<std::unordered_map
<string
, double> >(&*ret
)) {
903 using namespace json11
;
905 for(const auto& v
: *um
) {
906 o
[v
.first
] = v
.second
;
909 response
= out
.dump()+"\n";
913 response
= g_outputBuffer
;
915 if (!getLuaNoSideEffect()) {
916 feedConfigDelta(line
);
919 catch (const LuaContext::SyntaxErrorException
&) {
927 catch(const LuaContext::WrongTypeException
& e
) {
928 response
= "Command returned an object we can't print: " +std::string(e
.what()) + "\n";
929 // tried to return something we don't understand
931 catch (const LuaContext::ExecutionErrorException
& e
) {
932 if (!strcmp(e
.what(),"invalid key to 'next'")) {
933 response
= "Error: Parsing function parameters, did you forget parameter name?";
936 response
= "Error: " + string(e
.what());
940 std::rethrow_if_nested(e
);
941 } catch (const std::exception
& ne
) {
942 // ne is the exception that was thrown from inside the lambda
943 response
+= ": " + string(ne
.what());
945 catch (const PDNSException
& ne
) {
946 // ne is the exception that was thrown from inside the lambda
947 response
+= ": " + string(ne
.reason
);
950 catch (const LuaContext::SyntaxErrorException
& e
) {
951 response
= "Error: " + string(e
.what()) + ": ";
953 response
= sodEncryptSym(response
, g_consoleKey
, writingNonce
);
954 putMsgLen32(conn
.getFD(), response
.length());
955 writen2(conn
.getFD(), response
.c_str(), response
.length());
957 if (g_logConsoleConnections
) {
958 infolog("Closed control connection from %s", conn
.getClient().toStringWithPort());
961 catch (const std::exception
& e
) {
962 errlog("Got an exception in client connection from %s: %s", conn
.getClient().toStringWithPort(), e
.what());
966 void controlThread(int fd
, ComboAddress local
)
968 FDWrapper
acceptFD(fd
);
971 setThreadName("dnsdist/control");
974 auto localACL
= g_consoleACL
.getLocal();
975 infolog("Accepting control connections on %s", local
.toStringWithPort());
977 while ((sock
= SAccept(acceptFD
.getHandle(), client
)) >= 0) {
979 FDWrapper
socket(sock
);
980 if (!sodIsValidKey(g_consoleKey
)) {
981 vinfolog("Control connection from %s dropped because we don't have a valid key configured, please configure one using setKey()", client
.toStringWithPort());
985 if (!localACL
->match(client
)) {
986 vinfolog("Control connection from %s dropped because of ACL", client
.toStringWithPort());
991 ConsoleConnection
conn(client
, std::move(socket
));
992 if (g_logConsoleConnections
) {
993 warnlog("Got control connection from %s", client
.toStringWithPort());
996 std::thread
t(controlClientThread
, std::move(conn
));
999 catch (const std::exception
& e
) {
1000 errlog("Control connection died: %s", e
.what());
1004 catch (const std::exception
& e
) {
1005 errlog("Control thread died: %s", e
.what());
1009 void clearConsoleHistory()
1013 #endif /* HAVE_LIBEDIT */
1014 g_confDelta
.clear();