]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-console.cc
dnsdist: Add SetNegativeAndSOAAction() and its Lua binding
[thirdparty/pdns.git] / pdns / dnsdist-console.cc
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
b5521206
RG
22
23#include <fstream>
24#include <pwd.h>
25#include <thread>
424bdfb1 26
4d39d7f3 27#if defined (__OpenBSD__) || defined(__NetBSD__)
424bdfb1
PD
28#include <readline/readline.h>
29#include <readline/history.h>
30#else
9c8aa826 31#include <editline/readline.h>
424bdfb1
PD
32#endif
33
7e7a5b71 34#include "ext/json11/json11.hpp"
ffb07158 35
b5521206
RG
36#include "dolog.hh"
37#include "dnsdist.hh"
38#include "dnsdist-console.hh"
39#include "sodcrypto.hh"
519f5484 40#include "threadname.hh"
b5521206
RG
41
42GlobalStateHolder<NetmaskGroup> g_consoleACL;
f758857a 43vector<pair<struct timeval, string> > g_confDelta;
b5521206 44std::string g_consoleKey;
506bb661 45bool g_logConsoleConnections{true};
9c9b4998 46bool g_consoleEnabled{false};
03c05963 47uint32_t g_consoleOutputMsgMaxSize{10000000};
f758857a 48
49// MUST BE CALLED UNDER A LOCK - right now the LuaLock
b5521206 50static void feedConfigDelta(const std::string& line)
f758857a 51{
171fca9a 52 if(line.empty())
53 return;
f758857a 54 struct timeval now;
55 gettimeofday(&now, 0);
56 g_confDelta.push_back({now,line});
57}
58
b5521206 59static string historyFile(const bool &ignoreHOME = false)
93644039
PL
60{
61 string ret;
62
63 struct passwd pwd;
64 struct passwd *result;
65 char buf[16384];
66 getpwuid_r(geteuid(), &pwd, buf, sizeof(buf), &result);
67
68 const char *homedir = getenv("HOME");
69 if (result)
70 ret = string(pwd.pw_dir);
71 if (homedir && !ignoreHOME) // $HOME overrides what the OS tells us
72 ret = string(homedir);
73 if (ret.empty())
74 ret = "."; // CWD if nothing works..
75 ret.append("/.dnsdist_history");
76 return ret;
77}
78
fbe73e6c
RG
79static bool getMsgLen32(int fd, uint32_t* len)
80try
81{
82 uint32_t raw;
83 size_t ret = readn2(fd, &raw, sizeof raw);
84 if(ret != sizeof raw)
85 return false;
86 *len = ntohl(raw);
87 if(*len > g_consoleOutputMsgMaxSize)
88 return false;
89 return true;
90}
91catch(...) {
92 return false;
93}
94
95static bool putMsgLen32(int fd, uint32_t len)
96try
97{
98 uint32_t raw = htonl(len);
99 size_t ret = writen2(fd, &raw, sizeof raw);
100 return ret==sizeof raw;
101}
102catch(...) {
103 return false;
104}
105
9c9b4998
RG
106static bool sendMessageToServer(int fd, const std::string& line, SodiumNonce& readingNonce, SodiumNonce& writingNonce, const bool outputEmptyLine)
107{
108 string msg = sodEncryptSym(line, g_consoleKey, writingNonce);
109 const auto msgLen = msg.length();
110 if (msgLen > std::numeric_limits<uint32_t>::max()) {
3af57c9a 111 cout << "Encrypted message is too long to be sent to the server, "<< std::to_string(msgLen) << " > " << std::numeric_limits<uint32_t>::max() << endl;
9c9b4998
RG
112 return true;
113 }
114
115 putMsgLen32(fd, static_cast<uint32_t>(msgLen));
116
117 if (!msg.empty()) {
9c9b4998
RG
118 writen2(fd, msg);
119 }
120
121 uint32_t len;
122 if(!getMsgLen32(fd, &len)) {
123 cout << "Connection closed by the server." << endl;
124 return false;
125 }
126
127 if (len == 0) {
128 if (outputEmptyLine) {
129 cout << endl;
130 }
131
132 return true;
133 }
134
135 boost::scoped_array<char> resp(new char[len]);
136 readn2(fd, resp.get(), len);
137 msg.assign(resp.get(), len);
138 msg = sodDecryptSym(msg, g_consoleKey, readingNonce);
139 cout << msg;
140 cout.flush();
141
142 return true;
143}
144
ffb07158 145void doClient(ComboAddress server, const std::string& command)
146{
9c9b4998
RG
147 if (!sodIsValidKey(g_consoleKey)) {
148 cerr << "The currently configured console key is not valid, please configure a valid key using the setKey() directive" << endl;
149 return;
150 }
151
152 if(g_verbose) {
9c186303 153 cout<<"Connecting to "<<server.toStringWithPort()<<endl;
9c9b4998 154 }
b5521206 155
ffb07158 156 int fd=socket(server.sin4.sin_family, SOCK_STREAM, 0);
6a62c0e3
RG
157 if (fd < 0) {
158 cerr<<"Unable to connect to "<<server.toStringWithPort()<<endl;
159 return;
160 }
ffb07158 161 SConnect(fd, server);
9c186303 162 setTCPNoDelay(fd);
333ea16e 163 SodiumNonce theirs, ours, readingNonce, writingNonce;
ffb07158 164 ours.init();
165
166 writen2(fd, (const char*)ours.value, sizeof(ours.value));
167 readn2(fd, (char*)theirs.value, sizeof(theirs.value));
333ea16e
RG
168 readingNonce.merge(ours, theirs);
169 writingNonce.merge(theirs, ours);
ffb07158 170
9c9b4998
RG
171 /* try sending an empty message, the server should send an empty
172 one back. If it closes the connection instead, we are probably
173 having a key mismatch issue. */
174 if (!sendMessageToServer(fd, "", readingNonce, writingNonce, false)) {
175 cerr<<"The server closed the connection right away, likely indicating a key mismatch. Please check your setKey() directive."<<endl;
176 close(fd);
177 return;
178 }
179
180 if (!command.empty()) {
181 sendMessageToServer(fd, command, readingNonce, writingNonce, false);
182
6a62c0e3 183 close(fd);
ffb07158 184 return;
185 }
186
93644039 187 string histfile = historyFile();
ffb07158 188 set<string> dupper;
189 {
93644039 190 ifstream history(histfile);
ffb07158 191 string line;
192 while(getline(history, line))
193 add_history(line.c_str());
194 }
93644039 195 ofstream history(histfile, std::ios_base::app);
ffb07158 196 string lastline;
197 for(;;) {
198 char* sline = readline("> ");
199 rl_bind_key('\t',rl_complete);
200 if(!sline)
201 break;
202
203 string line(sline);
204 if(!line.empty() && line != lastline) {
205 add_history(sline);
206 history << sline <<endl;
207 history.flush();
208 }
209 lastline=line;
210 free(sline);
211
212 if(line=="quit")
213 break;
f5c1ce10 214 if(line=="help" || line=="?")
5fbd6dda 215 line="help()";
ffb07158 216
0877da98
RG
217 /* no need to send an empty line to the server */
218 if(line.empty())
219 continue;
220
9c9b4998 221 if (!sendMessageToServer(fd, line, readingNonce, writingNonce, true)) {
ffb07158 222 break;
223 }
ffb07158 224 }
6c1ca990 225 close(fd);
ffb07158 226}
227
228void doConsole()
229{
93644039 230 string histfile = historyFile(true);
ffb07158 231 set<string> dupper;
232 {
93644039 233 ifstream history(histfile);
ffb07158 234 string line;
235 while(getline(history, line))
236 add_history(line.c_str());
237 }
93644039 238 ofstream history(histfile, std::ios_base::app);
ffb07158 239 string lastline;
240 for(;;) {
241 char* sline = readline("> ");
242 rl_bind_key('\t',rl_complete);
243 if(!sline)
244 break;
245
246 string line(sline);
247 if(!line.empty() && line != lastline) {
248 add_history(sline);
249 history << sline <<endl;
250 history.flush();
251 }
252 lastline=line;
253 free(sline);
254
255 if(line=="quit")
256 break;
f5c1ce10 257 if(line=="help" || line=="?")
5fbd6dda 258 line="help()";
ffb07158 259
260 string response;
261 try {
7e7a5b71 262 bool withReturn=true;
263 retry:;
264 try {
265 std::lock_guard<std::mutex> lock(g_luamutex);
266 g_outputBuffer.clear();
267 resetLuaSideEffect();
268 auto ret=g_lua.executeCode<
269 boost::optional<
270 boost::variant<
271 string,
272 shared_ptr<DownstreamState>,
630e3e0a 273 ClientState*,
7e7a5b71 274 std::unordered_map<string, double>
275 >
276 >
277 >(withReturn ? ("return "+line) : line);
7e7a5b71 278 if(ret) {
af619119 279 if (const auto dsValue = boost::get<shared_ptr<DownstreamState>>(&*ret)) {
6d54c50c
CH
280 if (*dsValue) {
281 cout<<(*dsValue)->getName()<<endl;
282 }
7e7a5b71 283 }
630e3e0a 284 else if (const auto csValue = boost::get<ClientState*>(&*ret)) {
6d54c50c
CH
285 if (*csValue) {
286 cout<<(*csValue)->local.toStringWithPort()<<endl;
287 }
630e3e0a 288 }
7e7a5b71 289 else if (const auto strValue = boost::get<string>(&*ret)) {
290 cout<<*strValue<<endl;
291 }
292 else if(const auto um = boost::get<std::unordered_map<string, double> >(&*ret)) {
293 using namespace json11;
294 Json::object o;
295 for(const auto& v : *um)
296 o[v.first]=v.second;
297 Json out = o;
298 cout<<out.dump()<<endl;
299 }
300 }
301 else
7c7b5b60 302 cout << g_outputBuffer << std::flush;
7e7a5b71 303 if(!getLuaNoSideEffect())
304 feedConfigDelta(line);
ffb07158 305 }
7e7a5b71 306 catch(const LuaContext::SyntaxErrorException&) {
307 if(withReturn) {
308 withReturn=false;
309 goto retry;
310 }
311 throw;
312 }
313 }
314 catch(const LuaContext::WrongTypeException& e) {
e2323545 315 std::cerr<<"Command returned an object we can't print: "<<std::string(e.what())<<std::endl;
7e7a5b71 316 // tried to return something we don't understand
ffb07158 317 }
318 catch(const LuaContext::ExecutionErrorException& e) {
98bac6bc 319 if(!strcmp(e.what(),"invalid key to 'next'"))
320 std::cerr<<"Error parsing parameters, did you forget parameter name?";
321 else
322 std::cerr << e.what();
ffb07158 323 try {
324 std::rethrow_if_nested(e);
98bac6bc 325
d96df9ec 326 std::cerr << std::endl;
af619119
RG
327 } catch(const std::exception& ne) {
328 // ne is the exception that was thrown from inside the lambda
329 std::cerr << ": " << ne.what() << std::endl;
ffb07158 330 }
af619119
RG
331 catch(const PDNSException& ne) {
332 // ne is the exception that was thrown from inside the lambda
333 std::cerr << ": " << ne.reason << std::endl;
ffb07158 334 }
335 }
336 catch(const std::exception& e) {
ffb07158 337 std::cerr << e.what() << std::endl;
338 }
339 }
340}
341/**** CARGO CULT CODE AHEAD ****/
ca4252e0
RG
342const std::vector<ConsoleKeyword> g_consoleKeywords{
343 /* keyword, function, parameters, description */
344 { "addACL", true, "netmask", "add to the ACL set who can use this server" },
4d5959e6 345 { "addAction", true, "DNS rule, DNS action [, {uuid=\"UUID\"}]", "add a rule" },
fe77446e 346 { "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" },
b5521206 347 { "addConsoleACL", true, "netmask", "add a netmask to the console ACL" },
9b705b9e 348 { "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" },
1b51c0f3 349 { "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" },
7b925432 350 { "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()`)" },
fe77446e 351 { "addDynBlockSMT", true, "names, msessage[, 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()`)" },
9b705b9e 352 { "addLocal", true, "addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface=\"\", cpus={}}]", "add `addr` to the list of addresses we listen on" },
4d5959e6
RG
353 { "addCacheHitResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a cache hit response rule" },
354 { "addResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a response rule" },
2d4783a8 355 { "addSelfAnsweredResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a self-answered response rule" },
fa974ada 356 { "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" },
ca4252e0 357 { "AllowAction", true, "", "let these packets go through" },
788c3243 358 { "AllowResponseAction", true, "", "let these packets go through" },
ca4252e0
RG
359 { "AllRule", true, "", "matches all traffic" },
360 { "AndRule", true, "list of DNS rules", "matches if all sub-rules matches" },
361 { "benchRule", true, "DNS Rule [, iterations [, suffix]]", "bench the specified DNS rule" },
362 { "carbonServer", true, "serverIP, [ourname], [interval]", "report statistics to serverIP using our hostname, or 'ourname' if provided, every 'interval' seconds" },
363 { "controlSocket", true, "addr", "open a control socket on this address / connect to this address in client mode" },
364 { "clearDynBlocks", true, "", "clear all dynamic blocks" },
786e4d8c 365 { "clearQueryCounters", true, "", "clears the query counter buffer" },
ca4252e0 366 { "clearRules", true, "", "remove all current rules" },
2a28db86 367 { "ContinueAction", true, "action", "execute the specified action and continue the processing of the remaining rules, regardless of the return of the action" },
ca4252e0 368 { "DelayAction", true, "milliseconds", "delay the response by the specified amount of milliseconds (UDP-only)" },
788c3243 369 { "DelayResponseAction", true, "milliseconds", "delay the response by the specified amount of milliseconds (UDP-only)" },
ca4252e0 370 { "delta", true, "", "shows all commands entered that changed the configuration" },
fe77446e 371 { "DisableECSAction", true, "", "Disable the sending of ECS to the backend. Subsequent rules are processed after this action." },
ca4252e0 372 { "DisableValidationAction", true, "", "set the CD bit in the question, let it go through" },
fe77446e 373 { "DNSSECRule", true, "", "matches queries with the DO bit set" },
82a91ddf
CH
374 { "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" },
375 { "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" },
ca4252e0 376 { "DropAction", true, "", "drop these packets" },
788c3243 377 { "DropResponseAction", true, "", "drop these packets" },
34d00713 378 { "DSTPortRule", true, "port", "matches questions received to the destination port specified" },
ca4252e0 379 { "dumpStats", true, "", "print all statistics we gather" },
dc2fd93a 380 { "dynBlockRulesGroup", true, "", "return a new DynBlockRulesGroup object" },
fe77446e
RG
381 { "ECSOverrideAction", true, "override", "Whether an existing EDNS Client Subnet value should be overridden (true) or not (false). Subsequent rules are processed after this action" },
382 { "ECSPrefixLengthAction", true, "v4, v6", "Set the ECS prefix length. Subsequent rules are processed after this action" },
a9613dfe 383 { "EDNSVersionRule", true, "version", "matches queries with the specified EDNS version" },
e7c732b8 384 { "EDNSOptionRule", true, "optcode", "matches queries with the specified EDNS0 option present" },
ece50fe2 385 { "ERCodeAction", true, "ercode", "Reply immediately by turning the query into a response with the specified EDNS extended rcode" },
e7c732b8
RG
386 { "ERCodeRule", true, "rcode", "matches responses with the specified extended rcode (EDNS0)" },
387 { "exceedNXDOMAINs", true, "rate, seconds", "get set of addresses that exceed `rate` NXDOMAIN/s over `seconds` seconds" },
ca4252e0
RG
388 { "exceedQRate", true, "rate, seconds", "get set of address that exceed `rate` queries/s over `seconds` seconds" },
389 { "exceedQTypeRate", true, "type, rate, seconds", "get set of address that exceed `rate` queries/s for queries of type `type` over `seconds` seconds" },
55fcc3d7 390 { "exceedRespByterate", true, "rate, seconds", "get set of addresses that exceeded `rate` bytes/s answers over `seconds` seconds" },
82e55fbd 391 { "exceedServFails", true, "rate, seconds", "get set of addresses that exceed `rate` servfails/s over `seconds` seconds" },
ca4252e0
RG
392 { "firstAvailable", false, "", "picks the server with the lowest `order` that has not exceeded its QPS limit" },
393 { "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" },
394 { "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" },
79500db5 395 { "generateDNSCryptProviderKeys", true, "\"/path/to/providerPublic.key\", \"/path/to/providerPrivate.key\"", "generate a new provider keypair" },
fe77446e 396 { "getAction", true, "n", "Returns the Action associated with rule n" },
ca55cc52 397 { "getBind", true, "n", "returns the listener at index n" },
79500db5 398 { "getDNSCryptBind", true, "n", "return the `DNSCryptContext` object corresponding to the bind `n`" },
1b51c0f3 399 { "getDOHFrontend", true, "n", "returns the DOH frontend with index n" },
5d31a326 400 { "getPool", true, "name", "return the pool named `name`, or \"\" for the default pool" },
ca4252e0 401 { "getPoolServers", true, "pool", "return servers part of this pool" },
786e4d8c 402 { "getQueryCounters", true, "[max=10]", "show current buffer of query counters, limited by 'max' if provided" },
ca4252e0 403 { "getResponseRing", true, "", "return the current content of the response ring" },
fe77446e 404 { "getRespRing", true, "", "return the qname/rcode content of the response ring" },
adfdcc4b 405 { "getServer", true, "id", "returns server with index 'n' or whose uuid matches if 'id' is an UUID string" },
ca4252e0 406 { "getServers", true, "", "returns a table with all defined servers" },
fe77446e 407 { "getStatisticsCounters", true, "", "returns a map of statistic counters" },
a227f47d 408 { "getTLSContext", true, "n", "returns the TLS context with index n" },
8ef43a02 409 { "getTLSFrontend", true, "n", "returns the TLS frontend with index n" },
9aa6ddec 410 { "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" },
9ba32868 411 { "HTTPHeaderRule", true, "name, regex", "matches DoH queries with a HTTP header 'name' whose content matches the regular expression 'regex'"},
56f2845e 412 { "HTTPPathRegexRule", true, "regex", "matches DoH queries whose HTTP path matches 'regex'"},
9ba32868 413 { "HTTPPathRule", true, "path", "matches DoH queries whose HTTP path is an exact match to 'path'"},
13c1fc12 414 { "HTTPStatusAction", true, "status, reason, body", "return an HTTP response"},
6f2a4580 415 { "inClientStartup", true, "", "returns true during console client parsing of configuration" },
9aa6ddec
RG
416 { "includeDirectory", true, "path", "include configuration files from `path`" },
417 { "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" },
418 { "KeyValueLookupKeySourceIP", true, "", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the source IP of the client in network byte-order." },
419 { "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" },
420 { "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" },
421 { "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'" },
422 { "KeyValueStoreLookupRule", true, "kvs, lookupKey", "matches queries if the key is found in the specified Key Value store" },
ca4252e0 423 { "leastOutstanding", false, "", "Send traffic to downstream server with least outstanding queries, with the lowest 'order', and within that the lowest recent latency"},
456fc645 424 { "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." },
b62c160b 425 { "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." },
fe77446e
RG
426 { "LuaAction", true, "function", "Invoke a Lua function that accepts a DNSQuestion" },
427 { "LuaResponseAction", true, "function", "Invoke a Lua function that accepts a DNSResponse" },
428 { "MacAddrAction", 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" },
429 { "makeIPCipherKey", true, "password", "generates a 16-byte key that can be used to pseudonymize IP addresses with IP cipher" },
ca4252e0 430 { "makeKey", true, "", "generate a new server access key, emit configuration line ready for pasting" },
fe77446e 431 { "makeRule", true, "rule", "Make a NetmaskGroupRule() or a SuffixMatchNodeRule(), depending on how it is called" } ,
67ce0bdd 432 { "MaxQPSIPRule", true, "qps, [v4Mask=32 [, v6Mask=64 [, burst=qps [, expiration=300 [, cleanupDelay=60]]]]]", "matches traffic exceeding the qps limit per subnet" },
ca4252e0 433 { "MaxQPSRule", true, "qps", "matches traffic **not** exceeding this qps limit" },
cf48b0ce 434 { "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" },
ca4252e0
RG
435 { "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" },
436 { "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" },
2d4783a8 437 { "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" },
fe77446e
RG
438 { "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" },
439 { "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" },
440 { "newCA", true, "address", "Returns a ComboAddress based on `address`" },
c91bc0c9 441#ifdef HAVE_CDB
9aa6ddec
RG
442 { "newCDBKVStore", true, "fname, refreshDelay", "Return a new KeyValueStore object associated to the corresponding CDB database" },
443#endif
ca4252e0 444 { "newDNSName", true, "name", "make a DNSName based on this .-terminated name" },
fe77446e
RG
445 { "newDNSNameSet", true, "", "returns a new DNSNameSet" },
446 { "newDynBPFFilter", true, "bpf", "Return a new dynamic eBPF filter associated to a given BPF Filter" },
82a91ddf
CH
447 { "newFrameStreamTcpLogger", true, "addr", "create a FrameStream logger object writing to a TCP address (addr should be ip:port), to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" },
448 { "newFrameStreamUnixLogger", true, "socket", "create a FrameStream logger object writing to a local unix socket, to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" },
9aa6ddec
RG
449#ifdef HAVE_LMDB
450 { "newLMDBKVStore", true, "fname, dbName", "Return a new KeyValueStore object associated to the corresponding LMDB database" },
451#endif
fe77446e 452 { "newNMG", true, "", "Returns a NetmaskGroup" },
c1b81381 453 { "newPacketCache", true, "maxEntries[, maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, options={}]", "return a new Packet Cache" },
ca4252e0
RG
454 { "newQPSLimiter", true, "rate, burst", "configure a QPS limiter with that rate and that burst capacity" },
455 { "newRemoteLogger", true, "address:port [, timeout=2, maxQueuedEntries=100, reconnectWaitTime=1]", "create a Remote Logger object, to use with `RemoteLogAction()` and `RemoteLogResponseAction()`" },
4d5959e6 456 { "newRuleAction", true, "DNS rule, DNS action [, {uuid=\"UUID\"}]", "return a pair of DNS Rule and DNS Action, to be used with `setRules()`" },
150105a2 457 { "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}", "instantiate a server" },
ca4252e0
RG
458 { "newServerPolicy", true, "name, function", "create a policy object from a Lua function" },
459 { "newSuffixMatchNode", true, "", "returns a new SuffixMatchNode" },
fe77446e 460 { "NoneAction", true, "", "Does nothing. Subsequent rules are processed after this action" },
ca4252e0 461 { "NoRecurseAction", true, "", "strip RD bit from the question, let it go through" },
fe77446e
RG
462 { "NotRule", true, "selector", "Matches the traffic if the selector rule does not match" },
463 { "OpcodeRule", true, "code", "Matches queries with opcode code. code can be directly specified as an integer, or one of the built-in DNSOpcodes" },
464 { "OrRule", true, "selectors", "Matches the traffic if one or more of the the selectors rules does match" },
ca4252e0 465 { "PoolAction", true, "poolname", "set the packet into the specified pool" },
fe77446e 466 { "PoolAvailableRule", true, "poolname", "Check whether a pool has any servers available to handle queries" },
ca4252e0 467 { "printDNSCryptProviderFingerprint", true, "\"/path/to/providerPublic.key\"", "display the fingerprint of the provided resolver public key" },
fe77446e
RG
468 { "ProbaRule", true, "probability", "Matches queries with a given probability. 1.0 means always" },
469 { "QClassRule", true, "qclass", "Matches queries with the specified qclass. class can be specified as an integer or as one of the built-in DNSClass" },
e7c732b8
RG
470 { "QNameLabelsCountRule", true, "min, max", "matches if the qname has less than `min` or more than `max` labels" },
471 { "QNameRule", true, "qname", "matches queries with the specified qname" },
fe77446e 472 { "QNameSetRule", true, "set", "Matches if the set contains exact qname" },
e7c732b8 473 { "QNameWireLengthRule", true, "min, max", "matches if the qname's length on the wire is less than `min` or more than `max` bytes" },
fe77446e
RG
474 { "QPSAction", true, "maxqps", "Drop a packet if it does exceed the maxqps queries per second limits. Letting the subsequent rules apply otherwise" },
475 { "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" },
e7c732b8 476 { "QTypeRule", true, "qtype", "matches queries with the specified qtype" },
ece50fe2 477 { "RCodeAction", true, "rcode", "Reply immediately by turning the query into a response with the specified rcode" },
e7c732b8 478 { "RCodeRule", true, "rcode", "matches responses with the specified rcode" },
fe77446e
RG
479 { "RDRule", true, "", "Matches queries with the RD flag set" },
480 { "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" },
481 { "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" },
ca4252e0 482 { "RegexRule", true, "regex", "matches the query name against the supplied regex" },
8429ad04 483 { "registerDynBPFFilter", true, "DynBPFFilter", "register this dynamic BPF filter into the web interface so that its counters are displayed" },
bcc62bfb 484 { "reloadAllCertificates", true, "", "reload all DNSCrypt and TLS certificates, along with their associated keys" },
312a09a6
RG
485 { "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." },
486 { "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." },
4d5959e6
RG
487 { "rmCacheHitResponseRule", true, "id", "remove cache hit response rule in position 'id', or whose uuid matches if 'id' is an UUID string" },
488 { "rmResponseRule", true, "id", "remove response rule in position 'id', or whose uuid matches if 'id' is an UUID string" },
489 { "rmRule", true, "id", "remove rule in position 'id', or whose uuid matches if 'id' is an UUID string" },
2d4783a8 490 { "rmSelfAnsweredResponseRule", true, "id", "remove self-answered response rule in position 'id', or whose uuid matches if 'id' is an UUID string" },
adfdcc4b 491 { "rmServer", true, "id", "remove server with index 'id' or whose uuid matches if 'id' is an UUID string" },
ca4252e0 492 { "roundrobin", false, "", "Simple round robin over available servers" },
9f4eb5cc 493 { "sendCustomTrap", true, "str", "send a custom `SNMP` trap from Lua, containing the `str` string"},
ca4252e0 494 { "setACL", true, "{netmask, netmask}", "replace the ACL set with these netmasks. Use `setACL({})` to reset the list, meaning no one can use us" },
e7c732b8 495 { "setAddEDNSToSelfGeneratedResponses", true, "add", "set whether to add EDNS to self-generated responses, provided that the initial query had EDNS" },
5b4e19ad 496 { "setAllowEmptyResponse", true, "allow", "Set to true (defaults to false) to allow empty responses (qdcount=0) with a NoError or NXDomain rcode (default) from backends" },
56d68fad 497 { "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" },
fe77446e
RG
498 { "setCacheCleaningDelay", true, "num", "Set the interval in seconds between two runs of the cache cleaning algorithm, removing expired entries" },
499 { "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" },
2b4287d4 500 { "setConsistentHashingBalancingFactor", true, "factor", "Set the balancing factor for bounded-load consistent hashing" },
b5521206 501 { "setConsoleACL", true, "{netmask, netmask}", "replace the console ACL set with these netmasks" },
506bb661 502 { "setConsoleConnectionsLogging", true, "enabled", "whether to log the opening and closing of console connections" },
03c05963 503 { "setConsoleOutputMaxMsgSize", true, "messageSize", "set console message maximum size in bytes, default is 10 MB" },
fe77446e 504 { "setDefaultBPFFilter", true, "filter", "When used at configuration time, the corresponding BPFFilter will be attached to every bind" },
7b925432 505 { "setDynBlocksAction", true, "action", "set which action is performed when a query is blocked. Only DNSAction.Drop (the default) and DNSAction.Refused are supported" },
fe77446e 506 { "SetECSAction", true, "v4[, v6]", "Set the ECS prefix and prefix length sent to backends to an arbitrary value" },
ca4252e0
RG
507 { "setECSOverride", true, "bool", "whether to override an existing EDNS Client Subnet value in the query" },
508 { "setECSSourcePrefixV4", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv4 queries" },
509 { "setECSSourcePrefixV6", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv6 queries" },
510 { "setKey", true, "key", "set access key to that key" },
9b705b9e 511 { "setLocal", true, "addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface=\"\", cpus={}}]", "reset the list of addresses we listen on to this address" },
ca4252e0 512 { "setMaxTCPClientThreads", true, "n", "set the maximum of TCP client threads, handling TCP connections" },
9396d955
RG
513 { "setMaxTCPConnectionDuration", true, "n", "set the maximum duration of an incoming TCP connection, in seconds. 0 means unlimited" },
514 { "setMaxTCPConnectionsPerClient", true, "n", "set the maximum number of TCP connections per client. 0 means unlimited" },
515 { "setMaxTCPQueriesPerConnection", true, "n", "set the maximum number of queries in an incoming TCP connection. 0 means unlimited" },
ca4252e0 516 { "setMaxTCPQueuedConnections", true, "n", "set the maximum number of TCP connections queued (waiting to be picked up by a client thread)" },
09861a92 517 { "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" },
af9f750c 518 { "SetNegativeAndSOAAction", "true", "nxd, zone, ttl, mname, rname, serial, refresh, retry, expire, minimum", "Turn a query into a NXDomain or NoData answer and sets a SOA record in the additional section" },
8d72bcdd 519 { "setPayloadSizeOnSelfGeneratedAnswers", true, "payloadSize", "set the UDP payload size advertised via EDNS on self-generated responses" },
742c079a 520 { "setPoolServerPolicy", true, "policy, pool", "set the server selection policy for this pool to that policy" },
fe77446e 521 { "setPoolServerPolicyLua", true, "name, func, pool", "set the server selection policy for this pool to one named 'name' and provided by 'function'" },
53c57da7 522 { "setPreserveTrailingData", true, "bool", "set whether trailing data should be preserved while adding ECS or XPF records to incoming queries" },
786e4d8c
RS
523 { "setQueryCount", true, "bool", "set whether queries should be counted" },
524 { "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" },
6d31c8b6 525 { "setRingBuffersLockRetries", true, "n", "set the number of attempts to get a non-blocking lock to a ringbuffer shard before blocking" },
a609acdb 526 { "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`" },
fe77446e 527 { "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" },
ca4252e0 528 { "setRules", true, "list of rules", "replace the current rules with the supplied list of pairs of DNS Rules and DNS Actions (see `newRuleAction()`)" },
5d4e1ef8
RG
529 { "setSecurityPollInterval", true, "n", "set the security polling interval to `n` seconds" },
530 { "setSecurityPollSuffix", true, "suffix", "set the security polling suffix to the specified value" },
ca4252e0
RG
531 { "setServerPolicy", true, "policy", "set server selection policy to that policy" },
532 { "setServerPolicyLua", true, "name, function", "set server selection policy to one named 'name' and provided by 'function'" },
26a3cdb7 533 { "setServFailWhenNoServer", true, "bool", "if set, return a ServFail when no servers are available, instead of the default behaviour of dropping the query" },
9a32faa3 534 { "setStaleCacheEntriesTTL", true, "n", "allows using cache entries expired for at most n seconds when there is no backend available to answer for a query" },
0ca6a67f 535 { "setSyslogFacility", true, "facility", "set the syslog logging facility to 'facility'. Defaults to LOG_DAEMON" },
840ed663 536 { "setTCPDownstreamCleanupInterval", true, "interval", "minimum interval in seconds between two cleanups of the idle TCP downstream connections" },
edbda1ad 537 { "setTCPUseSinglePipe", true, "bool", "whether the incoming TCP connections should be put into a single queue instead of using per-thread queues. Defaults to false" },
ca4252e0
RG
538 { "setTCPRecvTimeout", true, "n", "set the read timeout on TCP connections from the client, in seconds" },
539 { "setTCPSendTimeout", true, "n", "set the write timeout on TCP connections from the client, in seconds" },
0beaa5c8 540 { "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" },
e0b5e49d 541 { "setUDPTimeout", true, "n", "set the maximum time dnsdist will wait for a response from a backend over UDP, in seconds" },
ca4252e0 542 { "setVerboseHealthChecks", true, "bool", "set whether health check errors will be logged" },
32c97b56 543 { "setWebserverConfig", true, "[{password=string, apiKey=string, customHeaders}]", "Updates webserver configuration" },
fe77446e 544 { "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" },
ca4252e0
RG
545 { "show", true, "string", "outputs `string`" },
546 { "showACL", true, "", "show our ACL set" },
ca55cc52 547 { "showBinds", true, "", "show listening addresses (frontends)" },
15bb664c 548 { "showCacheHitResponseRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined cache hit response rules, optionally with their UUIDs and optionally truncated to a given width" },
b5521206 549 { "showConsoleACL", true, "", "show our current console ACL set" },
ca4252e0 550 { "showDNSCryptBinds", true, "", "display the currently configured DNSCrypt binds" },
1b51c0f3 551 { "showDOHFrontends", true, "", "list all the available DOH frontends" },
5bbcbea0 552 { "showDOHResponseCodes", true, "", "show the HTTP response code statistics for the DoH frontends"},
ca4252e0 553 { "showDynBlocks", true, "", "show dynamic blocks in force" },
5d31a326 554 { "showPools", true, "", "show the available pools" },
742c079a 555 { "showPoolServerPolicy", true, "pool", "show server selection policy for this pool" },
ca4252e0 556 { "showResponseLatency", true, "", "show a plot of the response time latency distribution" },
3a5a3376
CHB
557 { "showResponseRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined response rules, optionally with their UUIDs and optionally truncated to a given width" },
558 { "showRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined rules, optionally with their UUIDs and optionally truncated to a given width" },
eb0ca460 559 { "showSecurityStatus", true, "", "Show the security status"},
3a5a3376 560 { "showSelfAnsweredResponseRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined self-answered response rules, optionally with their UUIDs and optionally truncated to a given width" },
ca4252e0 561 { "showServerPolicy", true, "", "show name of currently operational server selection policy" },
c8ea1f7c 562 { "showServers", true, "[{showUUIDs=false}]", "output all servers, optionally with their UUIDs" },
e65ae260 563 { "showTCPStats", true, "", "show some statistics regarding TCP" },
8b25405d 564 { "showTLSContexts", true, "", "list all the available TLS contexts" },
f34fdcc5 565 { "showTLSErrorCounters", true, "", "show metrics about TLS handshake failures" },
ca4252e0
RG
566 { "showVersion", true, "", "show the current version" },
567 { "shutdown", true, "", "shut down `dnsdist`" },
fe77446e 568 { "SkipCacheAction", true, "", "Don’t lookup the cache for this query, don’t store the answer" },
046bac5c 569 { "SNIRule", true, "name", "Create a rule which matches on the incoming TLS SNI value, if any (DoT or DoH)" },
9f4eb5cc
RG
570 { "snmpAgent", true, "enableTraps [, masterSocket]", "enable `SNMP` support. `enableTraps` is a boolean indicating whether traps should be sent and `masterSocket` an optional string specifying how to connect to the master agent"},
571 { "SNMPTrapAction", true, "[reason]", "send an SNMP trap, adding the optional `reason` string as the query description"},
572 { "SNMPTrapResponseAction", true, "[reason]", "send an SNMP trap, adding the optional `reason` string as the response description"},
955b9377
RG
573 { "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" },
574 { "SpoofCNAMEAction", true, "cname [, options]", "Forge a response with the specified CNAME value" },
202c4ab9 575 { "SpoofRawAction", true, "raw [, options]", "Forge a response with the specified record data as raw bytes" },
fe77446e 576 { "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" },
a76b0d63
RG
577 { "TagAction", true, "name, value", "set the tag named 'name' to the given value" },
578 { "TagResponseAction", true, "name, value", "set the tag named 'name' to the given value" },
579 { "TagRule", true, "name [, value]", "matches if the tag named 'name' is present, with the given 'value' matching if any" },
ca4252e0 580 { "TCAction", true, "", "create answer to query with TC and RD bits set, to move to TCP" },
fe77446e 581 { "TCPRule", true, "[tcp]", "Matches question received over TCP if tcp is true, over UDP otherwise" },
a2c4a339
CH
582 { "TeeAction", true, "remote [, addECS]", "send copy of query to remote, optionally adding ECS info" },
583 { "TempFailureCacheTTLAction", true, "ttl", "set packetcache TTL for temporary failure replies" },
ca4252e0 584 { "testCrypto", true, "", "test of the crypto all works" },
01b8149e 585 { "TimedIPSetRule", true, "", "Create a rule which matches a set of IP addresses which expire"},
ca4252e0 586 { "topBandwidth", true, "top", "show top-`top` clients that consume the most bandwidth over length of ringbuffer" },
cf48b0ce 587 { "topCacheHitResponseRule", true, "", "move the last cache hit response rule to the first position" },
ca4252e0
RG
588 { "topClients", true, "n", "show top-`n` clients sending the most queries over length of ringbuffer" },
589 { "topQueries", true, "n[, labels]", "show top 'n' queries, as grouped when optionally cut down to 'labels' labels" },
590 { "topResponses", true, "n, kind[, labels]", "show top 'n' responses with RCODE=kind (0=NO Error, 2=ServFail, 3=ServFail), as grouped when optionally cut down to 'labels' labels" },
591 { "topResponseRule", true, "", "move the last response rule to the first position" },
592 { "topRule", true, "", "move the last rule to the first position" },
2d4783a8 593 { "topSelfAnsweredResponseRule", true, "", "move the last self-answered response rule to the first position" },
ca4252e0 594 { "topSlow", true, "[top][, limit][, labels]", "show `top` queries slower than `limit` milliseconds, grouped by last `labels` labels" },
fe77446e 595 { "TrailingDataRule", true, "", "Matches if the query has trailing data" },
e72fbfc4 596 { "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." },
8429ad04 597 { "unregisterDynBPFFilter", true, "DynBPFFilter", "unregister this dynamic BPF filter" },
ca4252e0
RG
598 { "webserver", true, "address:port, password [, apiKey [, customHeaders ]])", "launch a webserver with stats on that address with that password" },
599 { "whashed", false, "", "Weighted hashed ('sticky') distribution over available servers, based on the server 'weight' parameter" },
1720247e 600 { "chashed", false, "", "Consistent hashed ('sticky') distribution over available servers, also based on the server 'weight' parameter" },
ca4252e0
RG
601 { "wrandom", false, "", "Weighted random over available servers, based on the server 'weight' parameter" },
602};
603
ffb07158 604extern "C" {
605char* my_generator(const char* text, int state)
606{
607 string t(text);
e8130431
RG
608 /* to keep it readable, we try to keep only 4 keywords per line
609 and to start a new line when the first letter changes */
ffb07158 610 static int s_counter=0;
611 int counter=0;
612 if(!state)
613 s_counter=0;
614
ca4252e0
RG
615 for(const auto& keyword : g_consoleKeywords) {
616 if(boost::starts_with(keyword.name, t) && counter++ == s_counter) {
617 std::string value(keyword.name);
ffb07158 618 s_counter++;
ca4252e0
RG
619 if (keyword.function) {
620 value += "(";
621 if (keyword.parameters.empty()) {
622 value += ")";
623 }
624 }
625 return strdup(value.c_str());
ffb07158 626 }
627 }
628 return 0;
629}
630
631char** my_completion( const char * text , int start, int end)
632{
633 char **matches=0;
634 if (start == 0)
635 matches = rl_completion_matches ((char*)text, &my_generator);
d9de8b61
CH
636
637 // skip default filename completion.
638 rl_attempted_completion_over = 1;
639
ffb07158 640 return matches;
641}
642}
643
b5521206 644static void controlClientThread(int fd, ComboAddress client)
ffb07158 645try
646{
519f5484 647 setThreadName("dnsdist/conscli");
9c186303 648 setTCPNoDelay(fd);
333ea16e 649 SodiumNonce theirs, ours, readingNonce, writingNonce;
ffb07158 650 ours.init();
333ea16e 651 readn2(fd, (char*)theirs.value, sizeof(theirs.value));
ffb07158 652 writen2(fd, (char*)ours.value, sizeof(ours.value));
333ea16e
RG
653 readingNonce.merge(ours, theirs);
654 writingNonce.merge(theirs, ours);
ffb07158 655
656 for(;;) {
657 uint32_t len;
658 if(!getMsgLen32(fd, &len))
659 break;
0877da98
RG
660
661 if (len == 0) {
662 /* just ACK an empty message
663 with an empty response */
664 putMsgLen32(fd, 0);
665 continue;
666 }
667
ffb07158 668 boost::scoped_array<char> msg(new char[len]);
669 readn2(fd, msg.get(), len);
9c9b4998 670
ffb07158 671 string line(msg.get(), len);
9c9b4998 672
b5521206 673 line = sodDecryptSym(line, g_consoleKey, readingNonce);
ffb07158 674 // cerr<<"Have decrypted line: "<<line<<endl;
675 string response;
676 try {
7e7a5b71 677 bool withReturn=true;
678 retry:;
679 try {
680 std::lock_guard<std::mutex> lock(g_luamutex);
681
682 g_outputBuffer.clear();
683 resetLuaSideEffect();
684 auto ret=g_lua.executeCode<
685 boost::optional<
686 boost::variant<
687 string,
688 shared_ptr<DownstreamState>,
630e3e0a 689 ClientState*,
7e7a5b71 690 std::unordered_map<string, double>
691 >
692 >
693 >(withReturn ? ("return "+line) : line);
ffb07158 694
695 if(ret) {
630e3e0a 696 if (const auto dsValue = boost::get<shared_ptr<DownstreamState>>(&*ret)) {
6d54c50c
CH
697 if (*dsValue) {
698 response=(*dsValue)->getName()+"\n";
699 } else {
700 response="";
701 }
630e3e0a
CH
702 }
703 else if (const auto csValue = boost::get<ClientState*>(&*ret)) {
6d54c50c
CH
704 if (*csValue) {
705 response=(*csValue)->local.toStringWithPort()+"\n";
706 } else {
707 response="";
708 }
630e3e0a
CH
709 }
710 else if (const auto strValue = boost::get<string>(&*ret)) {
711 response=*strValue+"\n";
712 }
7e7a5b71 713 else if(const auto um = boost::get<std::unordered_map<string, double> >(&*ret)) {
714 using namespace json11;
715 Json::object o;
716 for(const auto& v : *um)
717 o[v.first]=v.second;
718 Json out = o;
719 response=out.dump()+"\n";
720 }
ffb07158 721 }
722 else
723 response=g_outputBuffer;
f758857a 724 if(!getLuaNoSideEffect())
725 feedConfigDelta(line);
7e7a5b71 726 }
727 catch(const LuaContext::SyntaxErrorException&) {
728 if(withReturn) {
729 withReturn=false;
730 goto retry;
731 }
732 throw;
733 }
ffb07158 734 }
628377ec
RG
735 catch(const LuaContext::WrongTypeException& e) {
736 response = "Command returned an object we can't print: " +std::string(e.what()) + "\n";
737 // tried to return something we don't understand
738 }
ffb07158 739 catch(const LuaContext::ExecutionErrorException& e) {
98bac6bc 740 if(!strcmp(e.what(),"invalid key to 'next'"))
741 response = "Error: Parsing function parameters, did you forget parameter name?";
742 else
743 response = "Error: " + string(e.what());
ffb07158 744 try {
745 std::rethrow_if_nested(e);
af619119
RG
746 } catch(const std::exception& ne) {
747 // ne is the exception that was thrown from inside the lambda
748 response+= ": " + string(ne.what());
ffb07158 749 }
af619119
RG
750 catch(const PDNSException& ne) {
751 // ne is the exception that was thrown from inside the lambda
752 response += ": " + string(ne.reason);
ffb07158 753 }
754 }
755 catch(const LuaContext::SyntaxErrorException& e) {
756 response = "Error: " + string(e.what()) + ": ";
757 }
b5521206 758 response = sodEncryptSym(response, g_consoleKey, writingNonce);
ffb07158 759 putMsgLen32(fd, response.length());
760 writen2(fd, response.c_str(), response.length());
761 }
506bb661
RG
762 if (g_logConsoleConnections) {
763 infolog("Closed control connection from %s", client.toStringWithPort());
764 }
ffb07158 765 close(fd);
766 fd=-1;
767}
768catch(std::exception& e)
769{
770 errlog("Got an exception in client connection from %s: %s", client.toStringWithPort(), e.what());
771 if(fd >= 0)
772 close(fd);
773}
774
b5521206
RG
775void controlThread(int fd, ComboAddress local)
776try
777{
519f5484 778 setThreadName("dnsdist/control");
b5521206
RG
779 ComboAddress client;
780 int sock;
781 auto localACL = g_consoleACL.getLocal();
782 infolog("Accepting control connections on %s", local.toStringWithPort());
783
784 while ((sock = SAccept(fd, client)) >= 0) {
785
9c9b4998
RG
786 if (!sodIsValidKey(g_consoleKey)) {
787 vinfolog("Control connection from %s dropped because we don't have a valid key configured, please configure one using setKey()", client.toStringWithPort());
788 close(sock);
789 continue;
790 }
791
b5521206
RG
792 if (!localACL->match(client)) {
793 vinfolog("Control connection from %s dropped because of ACL", client.toStringWithPort());
794 close(sock);
795 continue;
796 }
797
798 if (g_logConsoleConnections) {
799 warnlog("Got control connection from %s", client.toStringWithPort());
800 }
801
802 std::thread t(controlClientThread, sock, client);
803 t.detach();
804 }
805}
806catch(const std::exception& e)
807{
808 close(fd);
809 errlog("Control connection died: %s", e.what());
810}