]>
Commit | Line | Data |
---|---|---|
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 | |
42 | GlobalStateHolder<NetmaskGroup> g_consoleACL; | |
f758857a | 43 | vector<pair<struct timeval, string> > g_confDelta; |
b5521206 | 44 | std::string g_consoleKey; |
506bb661 | 45 | bool g_logConsoleConnections{true}; |
9c9b4998 | 46 | bool g_consoleEnabled{false}; |
03c05963 | 47 | uint32_t g_consoleOutputMsgMaxSize{10000000}; |
f758857a | 48 | |
49 | // MUST BE CALLED UNDER A LOCK - right now the LuaLock | |
b5521206 | 50 | static 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 | 59 | static 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 |
79 | static bool getMsgLen32(int fd, uint32_t* len) |
80 | try | |
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 | } | |
91 | catch(...) { | |
92 | return false; | |
93 | } | |
94 | ||
95 | static bool putMsgLen32(int fd, uint32_t len) | |
96 | try | |
97 | { | |
98 | uint32_t raw = htonl(len); | |
99 | size_t ret = writen2(fd, &raw, sizeof raw); | |
100 | return ret==sizeof raw; | |
101 | } | |
102 | catch(...) { | |
103 | return false; | |
104 | } | |
105 | ||
9c9b4998 RG |
106 | static 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 | 145 | void 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 | ||
228 | void 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 |
342 | const 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" }, |
ca4252e0 RG |
405 | { "getServer", true, "n", "returns server with index n" }, |
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" }, |
9ba32868 | 410 | { "HTTPHeaderRule", true, "name, regex", "matches DoH queries with a HTTP header 'name' whose content matches the regular expression 'regex'"}, |
56f2845e | 411 | { "HTTPPathRegexRule", true, "regex", "matches DoH queries whose HTTP path matches 'regex'"}, |
9ba32868 | 412 | { "HTTPPathRule", true, "path", "matches DoH queries whose HTTP path is an exact match to 'path'"}, |
13c1fc12 | 413 | { "HTTPStatusAction", true, "status, reason, body", "return an HTTP response"}, |
6f2a4580 | 414 | { "inClientStartup", true, "", "returns true during console client parsing of configuration" }, |
fe77446e | 415 | { "includeDirectory", true, "path", "nclude configuration files from `path`" }, |
ca4252e0 RG |
416 | { "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" }, |
417 | { "leastOutstanding", false, "", "Send traffic to downstream server with least outstanding queries, with the lowest 'order', and within that the lowest recent latency"}, | |
456fc645 | 418 | { "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." }, |
fe77446e RG |
419 | { "LuaAction", true, "function", "Invoke a Lua function that accepts a DNSQuestion" }, |
420 | { "LuaResponseAction", true, "function", "Invoke a Lua function that accepts a DNSResponse" }, | |
421 | { "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" }, | |
422 | { "makeIPCipherKey", true, "password", "generates a 16-byte key that can be used to pseudonymize IP addresses with IP cipher" }, | |
ca4252e0 | 423 | { "makeKey", true, "", "generate a new server access key, emit configuration line ready for pasting" }, |
fe77446e | 424 | { "makeRule", true, "rule", "Make a NetmaskGroupRule() or a SuffixMatchNodeRule(), depending on how it is called" } , |
67ce0bdd | 425 | { "MaxQPSIPRule", true, "qps, [v4Mask=32 [, v6Mask=64 [, burst=qps [, expiration=300 [, cleanupDelay=60]]]]]", "matches traffic exceeding the qps limit per subnet" }, |
ca4252e0 | 426 | { "MaxQPSRule", true, "qps", "matches traffic **not** exceeding this qps limit" }, |
cf48b0ce | 427 | { "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 |
428 | { "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" }, |
429 | { "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 | 430 | { "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 |
431 | { "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" }, |
432 | { "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" }, | |
433 | { "newCA", true, "address", "Returns a ComboAddress based on `address`" }, | |
ca4252e0 | 434 | { "newDNSName", true, "name", "make a DNSName based on this .-terminated name" }, |
fe77446e RG |
435 | { "newDNSNameSet", true, "", "returns a new DNSNameSet" }, |
436 | { "newDynBPFFilter", true, "bpf", "Return a new dynamic eBPF filter associated to a given BPF Filter" }, | |
82a91ddf CH |
437 | { "newFrameStreamTcpLogger", true, "addr", "create a FrameStream logger object writing to a TCP address (addr should be ip:port), to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" }, |
438 | { "newFrameStreamUnixLogger", true, "socket", "create a FrameStream logger object writing to a local unix socket, to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" }, | |
fe77446e | 439 | { "newNMG", true, "", "Returns a NetmaskGroup" }, |
c1b81381 | 440 | { "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 |
441 | { "newQPSLimiter", true, "rate, burst", "configure a QPS limiter with that rate and that burst capacity" }, |
442 | { "newRemoteLogger", true, "address:port [, timeout=2, maxQueuedEntries=100, reconnectWaitTime=1]", "create a Remote Logger object, to use with `RemoteLogAction()` and `RemoteLogResponseAction()`" }, | |
4d5959e6 | 443 | { "newRuleAction", true, "DNS rule, DNS action [, {uuid=\"UUID\"}]", "return a pair of DNS Rule and DNS Action, to be used with `setRules()`" }, |
150105a2 | 444 | { "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 |
445 | { "newServerPolicy", true, "name, function", "create a policy object from a Lua function" }, |
446 | { "newSuffixMatchNode", true, "", "returns a new SuffixMatchNode" }, | |
fe77446e | 447 | { "NoneAction", true, "", "Does nothing. Subsequent rules are processed after this action" }, |
ca4252e0 | 448 | { "NoRecurseAction", true, "", "strip RD bit from the question, let it go through" }, |
fe77446e RG |
449 | { "NotRule", true, "selector", "Matches the traffic if the selector rule does not match" }, |
450 | { "OpcodeRule", true, "code", "Matches queries with opcode code. code can be directly specified as an integer, or one of the built-in DNSOpcodes" }, | |
451 | { "OrRule", true, "selectors", "Matches the traffic if one or more of the the selectors rules does match" }, | |
ca4252e0 | 452 | { "PoolAction", true, "poolname", "set the packet into the specified pool" }, |
fe77446e | 453 | { "PoolAvailableRule", true, "poolname", "Check whether a pool has any servers available to handle queries" }, |
ca4252e0 | 454 | { "printDNSCryptProviderFingerprint", true, "\"/path/to/providerPublic.key\"", "display the fingerprint of the provided resolver public key" }, |
fe77446e RG |
455 | { "ProbaRule", true, "probability", "Matches queries with a given probability. 1.0 means always" }, |
456 | { "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 |
457 | { "QNameLabelsCountRule", true, "min, max", "matches if the qname has less than `min` or more than `max` labels" }, |
458 | { "QNameRule", true, "qname", "matches queries with the specified qname" }, | |
fe77446e | 459 | { "QNameSetRule", true, "set", "Matches if the set contains exact qname" }, |
e7c732b8 | 460 | { "QNameWireLengthRule", true, "min, max", "matches if the qname's length on the wire is less than `min` or more than `max` bytes" }, |
fe77446e RG |
461 | { "QPSAction", true, "maxqps", "Drop a packet if it does exceed the maxqps queries per second limits. Letting the subsequent rules apply otherwise" }, |
462 | { "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 | 463 | { "QTypeRule", true, "qtype", "matches queries with the specified qtype" }, |
ece50fe2 | 464 | { "RCodeAction", true, "rcode", "Reply immediately by turning the query into a response with the specified rcode" }, |
e7c732b8 | 465 | { "RCodeRule", true, "rcode", "matches responses with the specified rcode" }, |
fe77446e RG |
466 | { "RDRule", true, "", "Matches queries with the RD flag set" }, |
467 | { "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" }, | |
468 | { "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 | 469 | { "RegexRule", true, "regex", "matches the query name against the supplied regex" }, |
8429ad04 | 470 | { "registerDynBPFFilter", true, "DynBPFFilter", "register this dynamic BPF filter into the web interface so that its counters are displayed" }, |
bcc62bfb | 471 | { "reloadAllCertificates", true, "", "reload all DNSCrypt and TLS certificates, along with their associated keys" }, |
312a09a6 RG |
472 | { "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." }, |
473 | { "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 |
474 | { "rmCacheHitResponseRule", true, "id", "remove cache hit response rule in position 'id', or whose uuid matches if 'id' is an UUID string" }, |
475 | { "rmResponseRule", true, "id", "remove response rule in position 'id', or whose uuid matches if 'id' is an UUID string" }, | |
476 | { "rmRule", true, "id", "remove rule in position 'id', or whose uuid matches if 'id' is an UUID string" }, | |
2d4783a8 | 477 | { "rmSelfAnsweredResponseRule", true, "id", "remove self-answered response rule in position 'id', or whose uuid matches if 'id' is an UUID string" }, |
ca4252e0 RG |
478 | { "rmServer", true, "n", "remove server with index n" }, |
479 | { "roundrobin", false, "", "Simple round robin over available servers" }, | |
9f4eb5cc | 480 | { "sendCustomTrap", true, "str", "send a custom `SNMP` trap from Lua, containing the `str` string"}, |
ca4252e0 | 481 | { "setACL", true, "{netmask, netmask}", "replace the ACL set with these netmasks. Use `setACL({})` to reset the list, meaning no one can use us" }, |
e7c732b8 | 482 | { "setAddEDNSToSelfGeneratedResponses", true, "add", "set whether to add EDNS to self-generated responses, provided that the initial query had EDNS" }, |
5b4e19ad | 483 | { "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 | 484 | { "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 |
485 | { "setCacheCleaningDelay", true, "num", "Set the interval in seconds between two runs of the cache cleaning algorithm, removing expired entries" }, |
486 | { "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" }, | |
b5521206 | 487 | { "setConsoleACL", true, "{netmask, netmask}", "replace the console ACL set with these netmasks" }, |
506bb661 | 488 | { "setConsoleConnectionsLogging", true, "enabled", "whether to log the opening and closing of console connections" }, |
03c05963 | 489 | { "setConsoleOutputMaxMsgSize", true, "messageSize", "set console message maximum size in bytes, default is 10 MB" }, |
fe77446e | 490 | { "setDefaultBPFFilter", true, "filter", "When used at configuration time, the corresponding BPFFilter will be attached to every bind" }, |
7b925432 | 491 | { "setDynBlocksAction", true, "action", "set which action is performed when a query is blocked. Only DNSAction.Drop (the default) and DNSAction.Refused are supported" }, |
fe77446e | 492 | { "SetECSAction", true, "v4[, v6]", "Set the ECS prefix and prefix length sent to backends to an arbitrary value" }, |
ca4252e0 RG |
493 | { "setECSOverride", true, "bool", "whether to override an existing EDNS Client Subnet value in the query" }, |
494 | { "setECSSourcePrefixV4", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv4 queries" }, | |
495 | { "setECSSourcePrefixV6", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv6 queries" }, | |
496 | { "setKey", true, "key", "set access key to that key" }, | |
9b705b9e | 497 | { "setLocal", true, "addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface=\"\", cpus={}}]", "reset the list of addresses we listen on to this address" }, |
ca4252e0 | 498 | { "setMaxTCPClientThreads", true, "n", "set the maximum of TCP client threads, handling TCP connections" }, |
9396d955 RG |
499 | { "setMaxTCPConnectionDuration", true, "n", "set the maximum duration of an incoming TCP connection, in seconds. 0 means unlimited" }, |
500 | { "setMaxTCPConnectionsPerClient", true, "n", "set the maximum number of TCP connections per client. 0 means unlimited" }, | |
501 | { "setMaxTCPQueriesPerConnection", true, "n", "set the maximum number of queries in an incoming TCP connection. 0 means unlimited" }, | |
ca4252e0 RG |
502 | { "setMaxTCPQueuedConnections", true, "n", "set the maximum number of TCP connections queued (waiting to be picked up by a client thread)" }, |
503 | { "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 10240" }, | |
8d72bcdd | 504 | { "setPayloadSizeOnSelfGeneratedAnswers", true, "payloadSize", "set the UDP payload size advertised via EDNS on self-generated responses" }, |
742c079a | 505 | { "setPoolServerPolicy", true, "policy, pool", "set the server selection policy for this pool to that policy" }, |
fe77446e | 506 | { "setPoolServerPolicyLua", true, "name, func, pool", "set the server selection policy for this pool to one named 'name' and provided by 'function'" }, |
53c57da7 | 507 | { "setPreserveTrailingData", true, "bool", "set whether trailing data should be preserved while adding ECS or XPF records to incoming queries" }, |
786e4d8c RS |
508 | { "setQueryCount", true, "bool", "set whether queries should be counted" }, |
509 | { "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 | 510 | { "setRingBuffersLockRetries", true, "n", "set the number of attempts to get a non-blocking lock to a ringbuffer shard before blocking" }, |
a609acdb | 511 | { "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 | 512 | { "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 | 513 | { "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 |
514 | { "setSecurityPollInterval", true, "n", "set the security polling interval to `n` seconds" }, |
515 | { "setSecurityPollSuffix", true, "suffix", "set the security polling suffix to the specified value" }, | |
ca4252e0 RG |
516 | { "setServerPolicy", true, "policy", "set server selection policy to that policy" }, |
517 | { "setServerPolicyLua", true, "name, function", "set server selection policy to one named 'name' and provided by 'function'" }, | |
26a3cdb7 | 518 | { "setServFailWhenNoServer", true, "bool", "if set, return a ServFail when no servers are available, instead of the default behaviour of dropping the query" }, |
9a32faa3 | 519 | { "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 | 520 | { "setSyslogFacility", true, "facility", "set the syslog logging facility to 'facility'. Defaults to LOG_DAEMON" }, |
840ed663 | 521 | { "setTCPDownstreamCleanupInterval", true, "interval", "minimum interval in seconds between two cleanups of the idle TCP downstream connections" }, |
edbda1ad | 522 | { "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 |
523 | { "setTCPRecvTimeout", true, "n", "set the read timeout on TCP connections from the client, in seconds" }, |
524 | { "setTCPSendTimeout", true, "n", "set the write timeout on TCP connections from the client, in seconds" }, | |
0beaa5c8 | 525 | { "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 | 526 | { "setUDPTimeout", true, "n", "set the maximum time dnsdist will wait for a response from a backend over UDP, in seconds" }, |
ca4252e0 | 527 | { "setVerboseHealthChecks", true, "bool", "set whether health check errors will be logged" }, |
32c97b56 | 528 | { "setWebserverConfig", true, "[{password=string, apiKey=string, customHeaders}]", "Updates webserver configuration" }, |
fe77446e | 529 | { "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 |
530 | { "show", true, "string", "outputs `string`" }, |
531 | { "showACL", true, "", "show our ACL set" }, | |
ca55cc52 | 532 | { "showBinds", true, "", "show listening addresses (frontends)" }, |
15bb664c | 533 | { "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 | 534 | { "showConsoleACL", true, "", "show our current console ACL set" }, |
ca4252e0 | 535 | { "showDNSCryptBinds", true, "", "display the currently configured DNSCrypt binds" }, |
1b51c0f3 | 536 | { "showDOHFrontends", true, "", "list all the available DOH frontends" }, |
5bbcbea0 | 537 | { "showDOHResponseCodes", true, "", "show the HTTP response code statistics for the DoH frontends"}, |
ca4252e0 | 538 | { "showDynBlocks", true, "", "show dynamic blocks in force" }, |
5d31a326 | 539 | { "showPools", true, "", "show the available pools" }, |
742c079a | 540 | { "showPoolServerPolicy", true, "pool", "show server selection policy for this pool" }, |
ca4252e0 | 541 | { "showResponseLatency", true, "", "show a plot of the response time latency distribution" }, |
3a5a3376 CHB |
542 | { "showResponseRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined response rules, optionally with their UUIDs and optionally truncated to a given width" }, |
543 | { "showRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined rules, optionally with their UUIDs and optionally truncated to a given width" }, | |
eb0ca460 | 544 | { "showSecurityStatus", true, "", "Show the security status"}, |
3a5a3376 | 545 | { "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 | 546 | { "showServerPolicy", true, "", "show name of currently operational server selection policy" }, |
c8ea1f7c | 547 | { "showServers", true, "[{showUUIDs=false}]", "output all servers, optionally with their UUIDs" }, |
e65ae260 | 548 | { "showTCPStats", true, "", "show some statistics regarding TCP" }, |
8b25405d | 549 | { "showTLSContexts", true, "", "list all the available TLS contexts" }, |
ca4252e0 RG |
550 | { "showVersion", true, "", "show the current version" }, |
551 | { "shutdown", true, "", "shut down `dnsdist`" }, | |
fe77446e | 552 | { "SkipCacheAction", true, "", "Don’t lookup the cache for this query, don’t store the answer" }, |
046bac5c | 553 | { "SNIRule", true, "name", "Create a rule which matches on the incoming TLS SNI value, if any (DoT or DoH)" }, |
9f4eb5cc RG |
554 | { "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"}, |
555 | { "SNMPTrapAction", true, "[reason]", "send an SNMP trap, adding the optional `reason` string as the query description"}, | |
556 | { "SNMPTrapResponseAction", true, "[reason]", "send an SNMP trap, adding the optional `reason` string as the response description"}, | |
ca4252e0 | 557 | { "SpoofAction", true, "{ip, ...} ", "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" }, |
fe77446e RG |
558 | { "SpoofCNAMEAction", true, "cname", "Forge a response with the specified CNAME value" }, |
559 | { "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 |
560 | { "TagAction", true, "name, value", "set the tag named 'name' to the given value" }, |
561 | { "TagResponseAction", true, "name, value", "set the tag named 'name' to the given value" }, | |
562 | { "TagRule", true, "name [, value]", "matches if the tag named 'name' is present, with the given 'value' matching if any" }, | |
ca4252e0 | 563 | { "TCAction", true, "", "create answer to query with TC and RD bits set, to move to TCP" }, |
fe77446e | 564 | { "TCPRule", true, "[tcp]", "Matches question received over TCP if tcp is true, over UDP otherwise" }, |
a2c4a339 CH |
565 | { "TeeAction", true, "remote [, addECS]", "send copy of query to remote, optionally adding ECS info" }, |
566 | { "TempFailureCacheTTLAction", true, "ttl", "set packetcache TTL for temporary failure replies" }, | |
ca4252e0 | 567 | { "testCrypto", true, "", "test of the crypto all works" }, |
01b8149e | 568 | { "TimedIPSetRule", true, "", "Create a rule which matches a set of IP addresses which expire"}, |
ca4252e0 | 569 | { "topBandwidth", true, "top", "show top-`top` clients that consume the most bandwidth over length of ringbuffer" }, |
cf48b0ce | 570 | { "topCacheHitResponseRule", true, "", "move the last cache hit response rule to the first position" }, |
ca4252e0 RG |
571 | { "topClients", true, "n", "show top-`n` clients sending the most queries over length of ringbuffer" }, |
572 | { "topQueries", true, "n[, labels]", "show top 'n' queries, as grouped when optionally cut down to 'labels' labels" }, | |
573 | { "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" }, | |
574 | { "topResponseRule", true, "", "move the last response rule to the first position" }, | |
575 | { "topRule", true, "", "move the last rule to the first position" }, | |
2d4783a8 | 576 | { "topSelfAnsweredResponseRule", true, "", "move the last self-answered response rule to the first position" }, |
ca4252e0 | 577 | { "topSlow", true, "[top][, limit][, labels]", "show `top` queries slower than `limit` milliseconds, grouped by last `labels` labels" }, |
fe77446e | 578 | { "TrailingDataRule", true, "", "Matches if the query has trailing data" }, |
e72fbfc4 | 579 | { "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 | 580 | { "unregisterDynBPFFilter", true, "DynBPFFilter", "unregister this dynamic BPF filter" }, |
ca4252e0 RG |
581 | { "webserver", true, "address:port, password [, apiKey [, customHeaders ]])", "launch a webserver with stats on that address with that password" }, |
582 | { "whashed", false, "", "Weighted hashed ('sticky') distribution over available servers, based on the server 'weight' parameter" }, | |
1720247e | 583 | { "chashed", false, "", "Consistent hashed ('sticky') distribution over available servers, also based on the server 'weight' parameter" }, |
ca4252e0 RG |
584 | { "wrandom", false, "", "Weighted random over available servers, based on the server 'weight' parameter" }, |
585 | }; | |
586 | ||
ffb07158 | 587 | extern "C" { |
588 | char* my_generator(const char* text, int state) | |
589 | { | |
590 | string t(text); | |
e8130431 RG |
591 | /* to keep it readable, we try to keep only 4 keywords per line |
592 | and to start a new line when the first letter changes */ | |
ffb07158 | 593 | static int s_counter=0; |
594 | int counter=0; | |
595 | if(!state) | |
596 | s_counter=0; | |
597 | ||
ca4252e0 RG |
598 | for(const auto& keyword : g_consoleKeywords) { |
599 | if(boost::starts_with(keyword.name, t) && counter++ == s_counter) { | |
600 | std::string value(keyword.name); | |
ffb07158 | 601 | s_counter++; |
ca4252e0 RG |
602 | if (keyword.function) { |
603 | value += "("; | |
604 | if (keyword.parameters.empty()) { | |
605 | value += ")"; | |
606 | } | |
607 | } | |
608 | return strdup(value.c_str()); | |
ffb07158 | 609 | } |
610 | } | |
611 | return 0; | |
612 | } | |
613 | ||
614 | char** my_completion( const char * text , int start, int end) | |
615 | { | |
616 | char **matches=0; | |
617 | if (start == 0) | |
618 | matches = rl_completion_matches ((char*)text, &my_generator); | |
d9de8b61 CH |
619 | |
620 | // skip default filename completion. | |
621 | rl_attempted_completion_over = 1; | |
622 | ||
ffb07158 | 623 | return matches; |
624 | } | |
625 | } | |
626 | ||
b5521206 | 627 | static void controlClientThread(int fd, ComboAddress client) |
ffb07158 | 628 | try |
629 | { | |
519f5484 | 630 | setThreadName("dnsdist/conscli"); |
9c186303 | 631 | setTCPNoDelay(fd); |
333ea16e | 632 | SodiumNonce theirs, ours, readingNonce, writingNonce; |
ffb07158 | 633 | ours.init(); |
333ea16e | 634 | readn2(fd, (char*)theirs.value, sizeof(theirs.value)); |
ffb07158 | 635 | writen2(fd, (char*)ours.value, sizeof(ours.value)); |
333ea16e RG |
636 | readingNonce.merge(ours, theirs); |
637 | writingNonce.merge(theirs, ours); | |
ffb07158 | 638 | |
639 | for(;;) { | |
640 | uint32_t len; | |
641 | if(!getMsgLen32(fd, &len)) | |
642 | break; | |
0877da98 RG |
643 | |
644 | if (len == 0) { | |
645 | /* just ACK an empty message | |
646 | with an empty response */ | |
647 | putMsgLen32(fd, 0); | |
648 | continue; | |
649 | } | |
650 | ||
ffb07158 | 651 | boost::scoped_array<char> msg(new char[len]); |
652 | readn2(fd, msg.get(), len); | |
9c9b4998 | 653 | |
ffb07158 | 654 | string line(msg.get(), len); |
9c9b4998 | 655 | |
b5521206 | 656 | line = sodDecryptSym(line, g_consoleKey, readingNonce); |
ffb07158 | 657 | // cerr<<"Have decrypted line: "<<line<<endl; |
658 | string response; | |
659 | try { | |
7e7a5b71 | 660 | bool withReturn=true; |
661 | retry:; | |
662 | try { | |
663 | std::lock_guard<std::mutex> lock(g_luamutex); | |
664 | ||
665 | g_outputBuffer.clear(); | |
666 | resetLuaSideEffect(); | |
667 | auto ret=g_lua.executeCode< | |
668 | boost::optional< | |
669 | boost::variant< | |
670 | string, | |
671 | shared_ptr<DownstreamState>, | |
630e3e0a | 672 | ClientState*, |
7e7a5b71 | 673 | std::unordered_map<string, double> |
674 | > | |
675 | > | |
676 | >(withReturn ? ("return "+line) : line); | |
ffb07158 | 677 | |
678 | if(ret) { | |
630e3e0a | 679 | if (const auto dsValue = boost::get<shared_ptr<DownstreamState>>(&*ret)) { |
6d54c50c CH |
680 | if (*dsValue) { |
681 | response=(*dsValue)->getName()+"\n"; | |
682 | } else { | |
683 | response=""; | |
684 | } | |
630e3e0a CH |
685 | } |
686 | else if (const auto csValue = boost::get<ClientState*>(&*ret)) { | |
6d54c50c CH |
687 | if (*csValue) { |
688 | response=(*csValue)->local.toStringWithPort()+"\n"; | |
689 | } else { | |
690 | response=""; | |
691 | } | |
630e3e0a CH |
692 | } |
693 | else if (const auto strValue = boost::get<string>(&*ret)) { | |
694 | response=*strValue+"\n"; | |
695 | } | |
7e7a5b71 | 696 | else if(const auto um = boost::get<std::unordered_map<string, double> >(&*ret)) { |
697 | using namespace json11; | |
698 | Json::object o; | |
699 | for(const auto& v : *um) | |
700 | o[v.first]=v.second; | |
701 | Json out = o; | |
702 | response=out.dump()+"\n"; | |
703 | } | |
ffb07158 | 704 | } |
705 | else | |
706 | response=g_outputBuffer; | |
f758857a | 707 | if(!getLuaNoSideEffect()) |
708 | feedConfigDelta(line); | |
7e7a5b71 | 709 | } |
710 | catch(const LuaContext::SyntaxErrorException&) { | |
711 | if(withReturn) { | |
712 | withReturn=false; | |
713 | goto retry; | |
714 | } | |
715 | throw; | |
716 | } | |
ffb07158 | 717 | } |
628377ec RG |
718 | catch(const LuaContext::WrongTypeException& e) { |
719 | response = "Command returned an object we can't print: " +std::string(e.what()) + "\n"; | |
720 | // tried to return something we don't understand | |
721 | } | |
ffb07158 | 722 | catch(const LuaContext::ExecutionErrorException& e) { |
98bac6bc | 723 | if(!strcmp(e.what(),"invalid key to 'next'")) |
724 | response = "Error: Parsing function parameters, did you forget parameter name?"; | |
725 | else | |
726 | response = "Error: " + string(e.what()); | |
ffb07158 | 727 | try { |
728 | std::rethrow_if_nested(e); | |
af619119 RG |
729 | } catch(const std::exception& ne) { |
730 | // ne is the exception that was thrown from inside the lambda | |
731 | response+= ": " + string(ne.what()); | |
ffb07158 | 732 | } |
af619119 RG |
733 | catch(const PDNSException& ne) { |
734 | // ne is the exception that was thrown from inside the lambda | |
735 | response += ": " + string(ne.reason); | |
ffb07158 | 736 | } |
737 | } | |
738 | catch(const LuaContext::SyntaxErrorException& e) { | |
739 | response = "Error: " + string(e.what()) + ": "; | |
740 | } | |
b5521206 | 741 | response = sodEncryptSym(response, g_consoleKey, writingNonce); |
ffb07158 | 742 | putMsgLen32(fd, response.length()); |
743 | writen2(fd, response.c_str(), response.length()); | |
744 | } | |
506bb661 RG |
745 | if (g_logConsoleConnections) { |
746 | infolog("Closed control connection from %s", client.toStringWithPort()); | |
747 | } | |
ffb07158 | 748 | close(fd); |
749 | fd=-1; | |
750 | } | |
751 | catch(std::exception& e) | |
752 | { | |
753 | errlog("Got an exception in client connection from %s: %s", client.toStringWithPort(), e.what()); | |
754 | if(fd >= 0) | |
755 | close(fd); | |
756 | } | |
757 | ||
b5521206 RG |
758 | void controlThread(int fd, ComboAddress local) |
759 | try | |
760 | { | |
519f5484 | 761 | setThreadName("dnsdist/control"); |
b5521206 RG |
762 | ComboAddress client; |
763 | int sock; | |
764 | auto localACL = g_consoleACL.getLocal(); | |
765 | infolog("Accepting control connections on %s", local.toStringWithPort()); | |
766 | ||
767 | while ((sock = SAccept(fd, client)) >= 0) { | |
768 | ||
9c9b4998 RG |
769 | if (!sodIsValidKey(g_consoleKey)) { |
770 | vinfolog("Control connection from %s dropped because we don't have a valid key configured, please configure one using setKey()", client.toStringWithPort()); | |
771 | close(sock); | |
772 | continue; | |
773 | } | |
774 | ||
b5521206 RG |
775 | if (!localACL->match(client)) { |
776 | vinfolog("Control connection from %s dropped because of ACL", client.toStringWithPort()); | |
777 | close(sock); | |
778 | continue; | |
779 | } | |
780 | ||
781 | if (g_logConsoleConnections) { | |
782 | warnlog("Got control connection from %s", client.toStringWithPort()); | |
783 | } | |
784 | ||
785 | std::thread t(controlClientThread, sock, client); | |
786 | t.detach(); | |
787 | } | |
788 | } | |
789 | catch(const std::exception& e) | |
790 | { | |
791 | close(fd); | |
792 | errlog("Control connection died: %s", e.what()); | |
793 | } |