]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-lua.cc
Merge pull request #6413 from Habbie/tinydns-hash
[thirdparty/pdns.git] / pdns / dnsdist-lua.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 */
6bb38cd6
RG
22
23#include <dirent.h>
24#include <fstream>
25#include <net/if.h>
9ef9b494
PD
26#include <sys/types.h>
27#include <sys/socket.h>
6bb38cd6 28#include <sys/stat.h>
df111b53 29#include <thread>
6bb38cd6
RG
30
31#include "dnsdist.hh"
b5521206 32#include "dnsdist-console.hh"
6bb38cd6
RG
33#include "dnsdist-lua.hh"
34
df111b53 35#include "base64.hh"
b7860997 36#include "dnswriter.hh"
6bb38cd6 37#include "dolog.hh"
0e41337b 38#include "lock.hh"
a227f47d 39#include "protobuf.hh"
6bb38cd6
RG
40#include "sodcrypto.hh"
41
42#include <boost/logic/tribool.hpp>
df111b53 43
6ab65223
PL
44#ifdef HAVE_SYSTEMD
45#include <systemd/sd-daemon.h>
46#endif
47
df111b53 48using std::thread;
49
6bb38cd6 50static vector<std::function<void(void)>>* g_launchWork = nullptr;
2e72cc0e 51
6bb38cd6
RG
52boost::tribool g_noLuaSideEffect;
53static bool g_included{false};
d8d85a30 54
6bb38cd6
RG
55/* this is a best effort way to prevent logging calls with no side-effects in the output of delta()
56 Functions can declare setLuaNoSideEffect() and if nothing else does declare a side effect, or nothing
57 has done so before on this invocation, this call won't be part of delta() output */
58void setLuaNoSideEffect()
153d5065 59{
6bb38cd6
RG
60 if(g_noLuaSideEffect==false) // there has been a side effect already
61 return;
62 g_noLuaSideEffect=true;
63}
153d5065 64
6bb38cd6 65void setLuaSideEffect()
d8d85a30 66{
6bb38cd6
RG
67 g_noLuaSideEffect=false;
68}
f850b032 69
6bb38cd6
RG
70bool getLuaNoSideEffect()
71{
72 return g_noLuaSideEffect==true;
d8d85a30 73}
74
6bb38cd6 75void resetLuaSideEffect()
2d11d1b2 76{
6bb38cd6 77 g_noLuaSideEffect = boost::logic::indeterminate;
2d11d1b2 78}
79
6bb38cd6
RG
80typedef std::unordered_map<std::string, boost::variant<bool, int, std::string, std::vector<std::pair<int,int> > > > localbind_t;
81
82static void parseLocalBindVars(boost::optional<localbind_t> vars, bool& doTCP, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface, std::set<int>& cpus)
efd35aa8
RG
83{
84 if (vars) {
85 if (vars->count("doTCP")) {
86 doTCP = boost::get<bool>((*vars)["doTCP"]);
87 }
88 if (vars->count("reusePort")) {
89 reusePort = boost::get<bool>((*vars)["reusePort"]);
90 }
91 if (vars->count("tcpFastOpenQueueSize")) {
92 tcpFastOpenQueueSize = boost::get<int>((*vars)["tcpFastOpenQueueSize"]);
93 }
94 if (vars->count("interface")) {
95 interface = boost::get<std::string>((*vars)["interface"]);
96 }
f0e4dcba
RG
97 if (vars->count("cpus")) {
98 for (const auto cpu : boost::get<std::vector<std::pair<int,int>>>((*vars)["cpus"])) {
99 cpus.insert(cpu.second);
100 }
101 }
efd35aa8
RG
102 }
103}
104
6bb38cd6 105void setupLuaConfig(bool client)
df111b53 106{
a6e02424 107 typedef std::unordered_map<std::string, boost::variant<bool, std::string, vector<pair<int, std::string> > > > newserver_t;
e48090d1 108
6f2a4580 109 g_lua.writeFunction("inClientStartup", [client]() {
541dd48f 110 return client && !g_configurationDone;
6f2a4580
PD
111 });
112
6bb38cd6 113 g_lua.writeFunction("newServer",
a5526b57
CH
114 [client](boost::variant<string,newserver_t> pvars, boost::optional<int> qps) {
115 setLuaSideEffect();
116
117 std::shared_ptr<DownstreamState> ret = std::make_shared<DownstreamState>(ComboAddress());
118 newserver_t vars;
119
120 ComboAddress serverAddr;
121 std::string serverAddressStr;
122 if(auto addrStr = boost::get<string>(&pvars)) {
123 serverAddressStr = *addrStr;
124 if(qps) {
125 vars["qps"] = std::to_string(*qps);
126 }
127 } else {
128 vars = boost::get<newserver_t>(pvars);
129 serverAddressStr = boost::get<string>(vars["address"]);
130 }
2e72cc0e 131
a5526b57
CH
132 try {
133 serverAddr = ComboAddress(serverAddressStr, 53);
134 }
135 catch(const PDNSException& e) {
136 g_outputBuffer="Error creating new server: "+string(e.reason);
137 errlog("Error creating new server with address %s: %s", serverAddressStr, e.reason);
138 return ret;
139 }
140 catch(std::exception& e) {
141 g_outputBuffer="Error creating new server: "+string(e.what());
142 errlog("Error creating new server with address %s: %s", serverAddressStr, e.what());
143 return ret;
144 }
2e72cc0e 145
a5526b57
CH
146 if(IsAnyAddress(serverAddr)) {
147 g_outputBuffer="Error creating new server: invalid address for a downstream server.";
148 errlog("Error creating new server: %s is not a valid address for a downstream server", serverAddressStr);
149 return ret;
150 }
151
152 ComboAddress sourceAddr;
153 unsigned int sourceItf = 0;
154 size_t numberOfSockets = 1;
155 std::set<int> cpus;
fbe2a2e0
RG
156
157 if(vars.count("source")) {
158 /* handle source in the following forms:
159 - v4 address ("192.0.2.1")
160 - v6 address ("2001:DB8::1")
161 - interface name ("eth0")
162 - v4 address and interface name ("192.0.2.1@eth0")
163 - v6 address and interface name ("2001:DB8::1@eth0")
164 */
165 const string source = boost::get<string>(vars["source"]);
166 bool parsed = false;
167 std::string::size_type pos = source.find("@");
168 if (pos == std::string::npos) {
169 /* no '@', try to parse that as a valid v4/v6 address */
170 try {
171 sourceAddr = ComboAddress(source);
172 parsed = true;
173 }
174 catch(...)
175 {
176 }
177 }
178
179 if (parsed == false)
180 {
181 /* try to parse as interface name, or v4/v6@itf */
182 string itfName = source.substr(pos == std::string::npos ? 0 : pos + 1);
183 unsigned int itfIdx = if_nametoindex(itfName.c_str());
184
185 if (itfIdx != 0) {
186 if (pos == 0 || pos == std::string::npos) {
187 /* "eth0" or "@eth0" */
188 sourceItf = itfIdx;
189 }
190 else {
191 /* "192.0.2.1@eth0" */
192 sourceAddr = ComboAddress(source.substr(0, pos));
193 sourceItf = itfIdx;
194 }
195 }
196 else
197 {
198 warnlog("Dismissing source %s because '%s' is not a valid interface name", source, itfName);
199 }
200 }
201 }
202
e998def2
RG
203 if (vars.count("sockets")) {
204 numberOfSockets = std::stoul(boost::get<string>(vars["sockets"]));
205 if (numberOfSockets == 0) {
206 warnlog("Dismissing invalid number of sockets '%s', using 1 instead", boost::get<string>(vars["sockets"]));
207 numberOfSockets = 1;
208 }
209 }
150105a2 210
a5526b57
CH
211 if(client) {
212 // do not construct DownstreamState now, it would try binding sockets.
213 return ret;
214 }
215 ret=std::make_shared<DownstreamState>(serverAddr, sourceAddr, sourceItf, numberOfSockets);
fbe2a2e0 216
df111b53 217 if(vars.count("qps")) {
af619119
RG
218 int qpsVal=std::stoi(boost::get<string>(vars["qps"]));
219 ret->qps=QPSLimiter(qpsVal, qpsVal);
df111b53 220 }
221
df111b53 222 if(vars.count("order")) {
335da0ba 223 ret->order=std::stoi(boost::get<string>(vars["order"]));
df111b53 224 }
225
226 if(vars.count("weight")) {
335da0ba 227 ret->weight=std::stoi(boost::get<string>(vars["weight"]));
df111b53 228 }
229
3f6d07a4 230 if(vars.count("retries")) {
335da0ba 231 ret->retries=std::stoi(boost::get<string>(vars["retries"]));
3f6d07a4
RG
232 }
233
b40cffe7
RG
234 if(vars.count("tcpConnectTimeout")) {
235 ret->tcpConnectTimeout=std::stoi(boost::get<string>(vars["tcpConnectTimeout"]));
236 }
237
3f6d07a4 238 if(vars.count("tcpSendTimeout")) {
335da0ba 239 ret->tcpSendTimeout=std::stoi(boost::get<string>(vars["tcpSendTimeout"]));
3f6d07a4
RG
240 }
241
242 if(vars.count("tcpRecvTimeout")) {
335da0ba 243 ret->tcpRecvTimeout=std::stoi(boost::get<string>(vars["tcpRecvTimeout"]));
3f6d07a4
RG
244 }
245
284d460c 246 if(vars.count("tcpFastOpen")) {
d987f632
RG
247 bool fastOpen = boost::get<bool>(vars["tcpFastOpen"]);
248 if (fastOpen) {
249#ifdef MSG_FASTOPEN
250 ret->tcpFastOpen=true;
251#else
252 warnlog("TCP Fast Open has been configured on downstream server %s but is not supported", boost::get<string>(vars["address"]));
253#endif
254 }
284d460c
RG
255 }
256
18eeccc9
RG
257 if(vars.count("name")) {
258 ret->name=boost::get<string>(vars["name"]);
259 }
260
ad485896
RG
261 if(vars.count("checkName")) {
262 ret->checkName=DNSName(boost::get<string>(vars["checkName"]));
263 }
264
265 if(vars.count("checkType")) {
266 ret->checkType=boost::get<string>(vars["checkType"]);
267 }
268
de9f7157
RG
269 if(vars.count("checkClass")) {
270 ret->checkClass=std::stoi(boost::get<string>(vars["checkClass"]));
271 }
272
21830638
RG
273 if(vars.count("setCD")) {
274 ret->setCD=boost::get<bool>(vars["setCD"]);
275 }
276
a6e02424
RG
277 if(vars.count("mustResolve")) {
278 ret->mustResolve=boost::get<bool>(vars["mustResolve"]);
279 }
280
ca404e94
RG
281 if(vars.count("useClientSubnet")) {
282 ret->useECS=boost::get<bool>(vars["useClientSubnet"]);
283 }
284
5602f131
DM
285 if(vars.count("ipBindAddrNoPort")) {
286 ret->ipBindAddrNoPort=boost::get<bool>(vars["ipBindAddrNoPort"]);
18f707fa 287 }
5cc8371b
RG
288
289 if(vars.count("addXPF")) {
c85f69a8 290 ret->xpfRRCode=std::stoi(boost::get<string>(vars["addXPF"]));
5602f131
DM
291 }
292
9e87dcb8
RG
293 if(vars.count("maxCheckFailures")) {
294 ret->maxCheckFailures=std::stoi(boost::get<string>(vars["maxCheckFailures"]));
295 }
296
3a8ee8bf
RG
297 if(vars.count("cpus")) {
298 for (const auto cpu : boost::get<vector<pair<int,string>>>(vars["cpus"])) {
299 cpus.insert(std::stoi(cpu.second));
300 }
301 }
302
8c82c02d
RG
303 /* this needs to be done _AFTER_ the order has been set,
304 since the server are kept ordered inside the pool */
305 auto localPools = g_pools.getCopy();
306 if(vars.count("pool")) {
307 if(auto* pool = boost::get<string>(&vars["pool"])) {
308 ret->pools.insert(*pool);
309 }
310 else {
311 auto pools = boost::get<vector<pair<int, string> > >(vars["pool"]);
312 for(auto& p : pools) {
313 ret->pools.insert(p.second);
314 }
315 }
316 for(const auto& poolName: ret->pools) {
317 addServerToPool(localPools, poolName, ret);
318 }
319 }
320 else {
321 addServerToPool(localPools, "", ret);
322 }
323 g_pools.setState(localPools);
324
7565f4e6
RG
325 if (ret->connected) {
326 if(g_launchWork) {
3a8ee8bf 327 g_launchWork->push_back([ret,cpus]() {
2717a92f 328 ret->tid = thread(responderThread, ret);
3a8ee8bf
RG
329 if (!cpus.empty()) {
330 mapThreadToCPUList(ret->tid.native_handle(), cpus);
331 }
2e72cc0e 332 });
7565f4e6
RG
333 }
334 else {
2717a92f 335 ret->tid = thread(responderThread, ret);
3a8ee8bf
RG
336 if (!cpus.empty()) {
337 mapThreadToCPUList(ret->tid.native_handle(), cpus);
338 }
7565f4e6 339 }
2e72cc0e 340 }
df111b53 341
ecbe9133 342 auto states = g_dstates.getCopy();
e5a14b2b 343 states.push_back(ret);
344 std::stable_sort(states.begin(), states.end(), [](const decltype(ret)& a, const decltype(ret)& b) {
df111b53 345 return a->order < b->order;
346 });
ecbe9133 347 g_dstates.setState(states);
df111b53 348 return ret;
349 } );
350
6bb38cd6 351 g_lua.writeFunction("rmServer",
df111b53 352 [](boost::variant<std::shared_ptr<DownstreamState>, int> var)
6bb38cd6 353 {
f758857a 354 setLuaSideEffect();
886e2cf2
RG
355 shared_ptr<DownstreamState> server;
356 auto* rem = boost::get<shared_ptr<DownstreamState>>(&var);
357 auto states = g_dstates.getCopy();
358 if(rem) {
359 server = *rem;
360 }
361 else {
362 int idx = boost::get<int>(var);
0f06cd4c 363 server = states.at(idx);
886e2cf2
RG
364 }
365 auto localPools = g_pools.getCopy();
366 for (const string& poolName : server->pools) {
367 removeServerFromPool(localPools, poolName, server);
368 }
0f06cd4c
RG
369 /* the server might also be in the default pool */
370 removeServerFromPool(localPools, "", server);
886e2cf2
RG
371 g_pools.setState(localPools);
372 states.erase(remove(states.begin(), states.end(), server), states.end());
373 g_dstates.setState(states);
df111b53 374 } );
375
df111b53 376 g_lua.writeFunction("setServerPolicy", [](ServerPolicy policy) {
f758857a 377 setLuaSideEffect();
e5a14b2b 378 g_policy.setState(policy);
df111b53 379 });
70a57b05 380 g_lua.writeFunction("setServerPolicyLua", [](string name, policyfunc_t policy) {
f758857a 381 setLuaSideEffect();
a1b1a29d 382 g_policy.setState(ServerPolicy{name, policy, true});
df111b53 383 });
384
385 g_lua.writeFunction("showServerPolicy", []() {
f758857a 386 setLuaSideEffect();
ecbe9133 387 g_outputBuffer=g_policy.getLocal()->name+"\n";
df111b53 388 });
389
f758857a 390 g_lua.writeFunction("truncateTC", [](bool tc) { setLuaSideEffect(); g_truncateTC=tc; });
391 g_lua.writeFunction("fixupCase", [](bool fu) { setLuaSideEffect(); g_fixupCase=fu; });
df111b53 392
df111b53 393 g_lua.writeFunction("addACL", [](const std::string& domain) {
f758857a 394 setLuaSideEffect();
e5a14b2b 395 g_ACL.modify([domain](NetmaskGroup& nmg) { nmg.addMask(domain); });
df111b53 396 });
2e72cc0e 397
efd35aa8 398 g_lua.writeFunction("setLocal", [client](const std::string& addr, boost::optional<localbind_t> vars) {
f758857a 399 setLuaSideEffect();
5949b95b 400 if(client)
401 return;
85e4ce52
RG
402 if (g_configurationDone) {
403 g_outputBuffer="setLocal cannot be used at runtime!\n";
404 return;
405 }
efd35aa8
RG
406 bool doTCP = true;
407 bool reusePort = false;
408 int tcpFastOpenQueueSize = 0;
409 std::string interface;
f0e4dcba 410 std::set<int> cpus;
efd35aa8 411
f0e4dcba 412 parseLocalBindVars(vars, doTCP, reusePort, tcpFastOpenQueueSize, interface, cpus);
efd35aa8 413
5949b95b 414 try {
415 ComboAddress loc(addr, 53);
416 g_locals.clear();
f0e4dcba 417 g_locals.push_back(std::make_tuple(loc, doTCP, reusePort, tcpFastOpenQueueSize, interface, cpus)); /// only works pre-startup, so no sync necessary
5949b95b 418 }
419 catch(std::exception& e) {
420 g_outputBuffer="Error: "+string(e.what())+"\n";
421 }
422 });
423
efd35aa8 424 g_lua.writeFunction("addLocal", [client](const std::string& addr, boost::optional<localbind_t> vars) {
f758857a 425 setLuaSideEffect();
2e72cc0e 426 if(client)
427 return;
85e4ce52
RG
428 if (g_configurationDone) {
429 g_outputBuffer="addLocal cannot be used at runtime!\n";
430 return;
431 }
efd35aa8
RG
432 bool doTCP = true;
433 bool reusePort = false;
434 int tcpFastOpenQueueSize = 0;
435 std::string interface;
f0e4dcba 436 std::set<int> cpus;
efd35aa8 437
f0e4dcba 438 parseLocalBindVars(vars, doTCP, reusePort, tcpFastOpenQueueSize, interface, cpus);
efd35aa8 439
2e72cc0e 440 try {
441 ComboAddress loc(addr, 53);
f0e4dcba 442 g_locals.push_back(std::make_tuple(loc, doTCP, reusePort, tcpFastOpenQueueSize, interface, cpus)); /// only works pre-startup, so no sync necessary
2e72cc0e 443 }
444 catch(std::exception& e) {
445 g_outputBuffer="Error: "+string(e.what())+"\n";
446 }
447 });
6bb38cd6 448
e4944ea0 449 g_lua.writeFunction("setACL", [](boost::variant<string,vector<pair<int, string>>> inp) {
f758857a 450 setLuaSideEffect();
e5a14b2b 451 NetmaskGroup nmg;
e4944ea0 452 if(auto str = boost::get<string>(&inp)) {
453 nmg.addMask(*str);
454 }
455 else for(const auto& p : boost::get<vector<pair<int,string>>>(inp)) {
e5a14b2b 456 nmg.addMask(p.second);
cffde2fd 457 }
458 g_ACL.setState(nmg);
df111b53 459 });
6bb38cd6 460
df111b53 461 g_lua.writeFunction("showACL", []() {
f758857a 462 setLuaNoSideEffect();
df111b53 463 vector<string> vec;
cffde2fd 464
a9c2e4ab 465 g_ACL.getLocal()->toStringVector(&vec);
cffde2fd 466
df111b53 467 for(const auto& s : vec)
cffde2fd 468 g_outputBuffer+=s+"\n";
469
df111b53 470 });
6bb38cd6 471
6ab65223
PL
472 g_lua.writeFunction("shutdown", []() {
473#ifdef HAVE_SYSTEMD
474 sd_notify(0, "STOPPING=1");
a227f47d
RG
475#endif /* HAVE_SYSTEMD */
476#if 0
477 // Useful for debugging leaks, but might lead to race under load
478 // since other threads are still runing.
479 for(auto& frontend : g_tlslocals) {
480 frontend->cleanup();
481 }
482 g_tlslocals.clear();
483#ifdef HAVE_PROTOBUF
484 google::protobuf::ShutdownProtobufLibrary();
485#endif /* HAVE_PROTOBUF */
486#endif /* 0 */
6ab65223
PL
487 _exit(0);
488 } );
df111b53 489
6bb38cd6 490 g_lua.writeFunction("showServers", []() {
f758857a 491 setLuaNoSideEffect();
df111b53 492 try {
493 ostringstream ret;
1287674c
RG
494 boost::format fmt("%1$-3d %2$-20.20s %|25t|%3% %|55t|%4$5s %|51t|%5$7.1f %|66t|%6$7d %|69t|%7$3d %|78t|%8$2d %|80t|%9$10d %|86t|%10$7d %|91t|%11$5.1f %|109t|%12$5.1f %|115t|%13$11d %14%" );
495 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14
496 ret << (fmt % "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools") << endl;
df111b53 497
498 uint64_t totQPS{0}, totQueries{0}, totDrops{0};
499 int counter=0;
a9c2e4ab
RG
500 auto states = g_dstates.getLocal();
501 for(const auto& s : *states) {
9f4eb5cc 502 string status = s->getStatus();
df111b53 503 string pools;
504 for(auto& p : s->pools) {
505 if(!pools.empty())
506 pools+=" ";
507 pools+=p;
508 }
509
18eeccc9 510 ret << (fmt % counter % s->name % s->remote.toStringWithPort() %
6bb38cd6 511 status %
1287674c 512 s->queryLoad % s->qps.getRate() % s->order % s->weight % s->queries.load() % s->reuseds.load() % (s->dropRate) % (s->latencyUsec/1000.0) % s->outstanding.load() % pools) << endl;
df111b53 513
514 totQPS += s->queryLoad;
515 totQueries += s->queries.load();
516 totDrops += s->reuseds.load();
517 ++counter;
518 }
18eeccc9 519 ret<< (fmt % "All" % "" % "" % ""
6bb38cd6 520 %
1287674c 521 (double)totQPS % "" % "" % "" % totQueries % totDrops % "" % "" % "" % "" ) << endl;
df111b53 522
523 g_outputBuffer=ret.str();
524 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
525 });
526
6bb38cd6
RG
527 g_lua.writeFunction("getServers", []() {
528 setLuaNoSideEffect();
529 vector<pair<int, std::shared_ptr<DownstreamState> > > ret;
530 int count=1;
531 for(const auto& s : g_dstates.getCopy()) {
532 ret.push_back(make_pair(count++, s));
533 }
534 return ret;
8499caaf 535 });
e27097e4 536
6bb38cd6
RG
537 g_lua.writeFunction("getPoolServers", [](string pool) {
538 return getDownstreamCandidates(g_pools.getCopy(), pool);
b1bec9f0
RG
539 });
540
6bb38cd6
RG
541 g_lua.writeFunction("getServer", [client](int i) {
542 if (client)
543 return std::make_shared<DownstreamState>(ComboAddress());
544 return g_dstates.getCopy().at(i);
731774a8 545 });
546
6bb38cd6
RG
547 g_lua.writeFunction("carbonServer", [](const std::string& address, boost::optional<string> ourName,
548 boost::optional<unsigned int> interval) {
549 setLuaSideEffect();
550 auto ours = g_carbon.getCopy();
551 ours.push_back({ComboAddress(address, 2003), ourName ? *ourName : "", interval ? *interval : 30});
552 g_carbon.setState(ours);
553 });
87c605c4 554
6bb38cd6 555 g_lua.writeFunction("webserver", [client](const std::string& address, const std::string& password, const boost::optional<std::string> apiKey, const boost::optional<std::map<std::string, std::string> > customHeaders) {
f758857a 556 setLuaSideEffect();
6bb38cd6
RG
557 if(client)
558 return;
559 ComboAddress local(address);
560 try {
561 int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
562 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
563 SBind(sock, local);
564 SListen(sock, 5);
565 auto launch=[sock, local, password, apiKey, customHeaders]() {
566 thread t(dnsdistWebserverThread, sock, local, password, apiKey ? *apiKey : "", customHeaders);
567 t.detach();
568 };
569 if(g_launchWork)
570 g_launchWork->push_back(launch);
571 else
572 launch();
731774a8 573 }
574 catch(std::exception& e) {
6bb38cd6
RG
575 g_outputBuffer="Unable to bind to webserver socket on " + local.toStringWithPort() + ": " + e.what();
576 errlog("Unable to bind to webserver socket on %s: %s", local.toStringWithPort(), e.what());
731774a8 577 }
731774a8 578
579 });
580
6bb38cd6 581 g_lua.writeFunction("controlSocket", [client](const std::string& str) {
87c605c4 582 setLuaSideEffect();
6bb38cd6 583 ComboAddress local(str, 5199);
832c1792 584
6bb38cd6
RG
585 if(client) {
586 g_serverControl = local;
87c605c4
RG
587 return;
588 }
731774a8 589
6bb38cd6
RG
590 try {
591 int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
592 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
593 SBind(sock, local);
594 SListen(sock, 5);
595 auto launch=[sock, local]() {
596 thread t(controlThread, sock, local);
597 t.detach();
598 };
599 if(g_launchWork)
600 g_launchWork->push_back(launch);
601 else
602 launch();
ae3dfa48 603
6bb38cd6
RG
604 }
605 catch(std::exception& e) {
606 g_outputBuffer="Unable to bind to control socket on " + local.toStringWithPort() + ": " + e.what();
607 errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what());
608 }
63beb26d
G
609 });
610
b5521206
RG
611 g_lua.writeFunction("addConsoleACL", [](const std::string& netmask) {
612 setLuaSideEffect();
613#ifndef HAVE_LIBSODIUM
614 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
615#endif
616
617 g_consoleACL.modify([netmask](NetmaskGroup& nmg) { nmg.addMask(netmask); });
618 });
619
620 g_lua.writeFunction("setConsoleACL", [](boost::variant<string,vector<pair<int, string>>> inp) {
621 setLuaSideEffect();
622
623#ifndef HAVE_LIBSODIUM
624 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
625#endif
626
627 NetmaskGroup nmg;
628 if(auto str = boost::get<string>(&inp)) {
629 nmg.addMask(*str);
630 }
631 else for(const auto& p : boost::get<vector<pair<int,string>>>(inp)) {
632 nmg.addMask(p.second);
633 }
634 g_consoleACL.setState(nmg);
635 });
636
637 g_lua.writeFunction("showConsoleACL", []() {
638 setLuaNoSideEffect();
639
640#ifndef HAVE_LIBSODIUM
641 warnlog("Allowing remote access to the console while libsodium support has not been enabled is not secure, and will result in cleartext communications");
642#endif
643
644 vector<string> vec;
645 g_consoleACL.getLocal()->toStringVector(&vec);
646
647 for(const auto& s : vec) {
648 g_outputBuffer += s + "\n";
649 }
650 });
651
6bb38cd6
RG
652 g_lua.writeFunction("clearQueryCounters", []() {
653 unsigned int size{0};
654 {
655 WriteLock wl(&g_qcount.queryLock);
656 size = g_qcount.records.size();
657 g_qcount.records.clear();
658 }
6eecd4c2 659
6bb38cd6
RG
660 boost::format fmt("%d records cleared from query counter buffer\n");
661 g_outputBuffer = (fmt % size).str();
2332b03c 662 });
663
6bb38cd6
RG
664 g_lua.writeFunction("getQueryCounters", [](boost::optional<unsigned int> optMax) {
665 setLuaNoSideEffect();
666 ReadLock rl(&g_qcount.queryLock);
667 g_outputBuffer = "query counting is currently: ";
668 g_outputBuffer+= g_qcount.enabled ? "enabled" : "disabled";
669 g_outputBuffer+= (boost::format(" (%d records in buffer)\n") % g_qcount.records.size()).str();
f39b7598 670
6bb38cd6
RG
671 boost::format fmt("%-3d %s: %d request(s)\n");
672 QueryCountRecords::iterator it;
673 unsigned int max = optMax ? *optMax : 10;
674 unsigned int index{1};
675 for(it = g_qcount.records.begin(); it != g_qcount.records.end() && index <= max; ++it, ++index) {
676 g_outputBuffer += (fmt % index % it->first % it->second).str();
677 }
808c5ef7 678 });
679
6bb38cd6 680 g_lua.writeFunction("setQueryCount", [](bool enabled) { g_qcount.enabled=enabled; });
ae3dfa48 681
6bb38cd6
RG
682 g_lua.writeFunction("setQueryCountFilter", [](QueryCountFilter func) {
683 g_qcount.filter = func;
886e2cf2
RG
684 });
685
6bb38cd6
RG
686 g_lua.writeFunction("makeKey", []() {
687 setLuaNoSideEffect();
688 g_outputBuffer="setKey("+newKey()+")\n";
6bba426c 689 });
690
6bb38cd6 691 g_lua.writeFunction("setKey", [](const std::string& key) {
b5521206 692 if(!g_configurationDone && ! g_consoleKey.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
6bb38cd6
RG
693 return; // but later setKeys() trump the -k value again
694 }
b5521206
RG
695#ifndef HAVE_LIBSODIUM
696 warnlog("Calling setKey() while libsodium support has not been enabled is not secure, and will result in cleartext communications");
697#endif
6bba426c 698
6bb38cd6
RG
699 setLuaSideEffect();
700 string newkey;
701 if(B64Decode(key, newkey) < 0) {
702 g_outputBuffer=string("Unable to decode ")+key+" as Base64";
703 errlog("%s", g_outputBuffer);
704 }
2332b03c 705 else
b5521206 706 g_consoleKey=newkey;
2332b03c 707 });
708
6bb38cd6
RG
709 g_lua.writeFunction("testCrypto", [](boost::optional<string> optTestMsg)
710 {
711 setLuaNoSideEffect();
712#ifdef HAVE_LIBSODIUM
713 try {
714 string testmsg;
2332b03c 715
6bb38cd6
RG
716 if (optTestMsg) {
717 testmsg = *optTestMsg;
718 }
719 else {
720 testmsg = "testStringForCryptoTests";
721 }
6eecd4c2 722
6bb38cd6
RG
723 SodiumNonce sn, sn2;
724 sn.init();
725 sn2=sn;
b5521206
RG
726 string encrypted = sodEncryptSym(testmsg, g_consoleKey, sn);
727 string decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2);
4ed8dfeb 728
6bb38cd6
RG
729 sn.increment();
730 sn2.increment();
b7860997 731
b5521206
RG
732 encrypted = sodEncryptSym(testmsg, g_consoleKey, sn);
733 decrypted = sodDecryptSym(encrypted, g_consoleKey, sn2);
36da3ecd 734
6bb38cd6
RG
735 if(testmsg == decrypted)
736 g_outputBuffer="Everything is ok!\n";
737 else
738 g_outputBuffer="Crypto failed..\n";
b7860997 739
6bb38cd6
RG
740 }
741 catch(...) {
742 g_outputBuffer="Crypto failed..\n";
743 }
744#else
745 g_outputBuffer="Crypto not available.\n";
746#endif
747 });
b7860997 748
6bb38cd6 749 g_lua.writeFunction("setTCPRecvTimeout", [](int timeout) { g_tcpRecvTimeout=timeout; });
89cb6f9a 750
6bb38cd6 751 g_lua.writeFunction("setTCPSendTimeout", [](int timeout) { g_tcpSendTimeout=timeout; });
89cb6f9a 752
6bb38cd6 753 g_lua.writeFunction("setUDPTimeout", [](int timeout) { g_udpTimeout=timeout; });
d3826006 754
6bb38cd6
RG
755 g_lua.writeFunction("setMaxUDPOutstanding", [](uint16_t max) {
756 if (!g_configurationDone) {
757 g_maxOutstanding = max;
758 } else {
759 g_outputBuffer="Max UDP outstanding cannot be altered at runtime!\n";
b7860997 760 }
55baa1f2
RG
761 });
762
6bb38cd6
RG
763 g_lua.writeFunction("setMaxTCPClientThreads", [](uint64_t max) {
764 if (!g_configurationDone) {
765 g_maxTCPClientThreads = max;
766 } else {
767 g_outputBuffer="Maximum TCP client threads count cannot be altered at runtime!\n";
768 }
55baa1f2
RG
769 });
770
6bb38cd6
RG
771 g_lua.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max) {
772 if (!g_configurationDone) {
773 g_maxTCPQueuedConnections = max;
774 } else {
775 g_outputBuffer="The maximum number of queued TCP connections cannot be altered at runtime!\n";
776 }
55baa1f2
RG
777 });
778
6bb38cd6
RG
779 g_lua.writeFunction("setMaxTCPQueriesPerConnection", [](size_t max) {
780 if (!g_configurationDone) {
781 g_maxTCPQueriesPerConn = max;
782 } else {
783 g_outputBuffer="The maximum number of queries per TCP connection cannot be altered at runtime!\n";
784 }
57c61ce9
RG
785 });
786
6bb38cd6
RG
787 g_lua.writeFunction("setMaxTCPConnectionsPerClient", [](size_t max) {
788 if (!g_configurationDone) {
789 g_maxTCPConnectionsPerClient = max;
790 } else {
791 g_outputBuffer="The maximum number of TCP connection per client cannot be altered at runtime!\n";
792 }
57c61ce9
RG
793 });
794
6bb38cd6
RG
795 g_lua.writeFunction("setMaxTCPConnectionDuration", [](size_t max) {
796 if (!g_configurationDone) {
797 g_maxTCPConnectionDuration = max;
798 } else {
799 g_outputBuffer="The maximum duration of a TCP connection cannot be altered at runtime!\n";
800 }
788c3243
RG
801 });
802
6bb38cd6 803 g_lua.writeFunction("setCacheCleaningDelay", [](uint32_t delay) { g_cacheCleaningDelay = delay; });
0570f37c 804
6bb38cd6 805 g_lua.writeFunction("setCacheCleaningPercentage", [](uint16_t percentage) { if (percentage < 100) g_cacheCleaningPercentage = percentage; else g_cacheCleaningPercentage = 100; });
0570f37c 806
6bb38cd6 807 g_lua.writeFunction("setECSSourcePrefixV4", [](uint16_t prefix) { g_ECSSourcePrefixV4=prefix; });
f39b7598 808
6bb38cd6 809 g_lua.writeFunction("setECSSourcePrefixV6", [](uint16_t prefix) { g_ECSSourcePrefixV6=prefix; });
0570f37c 810
6bb38cd6 811 g_lua.writeFunction("setECSOverride", [](bool override) { g_ECSOverride=override; });
df111b53 812
6bb38cd6
RG
813 g_lua.writeFunction("showDynBlocks", []() {
814 setLuaNoSideEffect();
815 auto slow = g_dynblockNMG.getCopy();
816 struct timespec now;
817 gettime(&now);
818 boost::format fmt("%-24s %8d %8d %s\n");
819 g_outputBuffer = (fmt % "What" % "Seconds" % "Blocks" % "Reason").str();
820 for(const auto& e: slow) {
821 if(now < e->second.until)
822 g_outputBuffer+= (fmt % e->first.toString() % (e->second.until.tv_sec - now.tv_sec) % e->second.blocks % e->second.reason).str();
823 }
824 auto slow2 = g_dynblockSMT.getCopy();
825 slow2.visit([&now, &fmt](const SuffixMatchTree<DynBlock>& node) {
826 if(now <node.d_value.until) {
827 string dom("empty");
828 if(!node.d_value.domain.empty())
829 dom = node.d_value.domain.toString();
830 g_outputBuffer+= (fmt % dom % (node.d_value.until.tv_sec - now.tv_sec) % node.d_value.blocks % node.d_value.reason).str();
831 }
832 });
832c1792 833
520eb5a0 834 });
df111b53 835
6bb38cd6 836 g_lua.writeFunction("clearDynBlocks", []() {
f758857a 837 setLuaSideEffect();
6bb38cd6
RG
838 nmts_t nmg;
839 g_dynblockNMG.setState(nmg);
840 SuffixMatchTree<DynBlock> smt;
841 g_dynblockSMT.setState(smt);
842 });
843
844 g_lua.writeFunction("addDynBlocks",
f8327125 845 [](const std::unordered_map<ComboAddress,unsigned int, ComboAddress::addressOnlyHash, ComboAddress::addressOnlyEqual>& m, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
6ddb008a
RG
846 if (m.empty()) {
847 return;
848 }
6bb38cd6
RG
849 setLuaSideEffect();
850 auto slow = g_dynblockNMG.getCopy();
851 struct timespec until, now;
852 gettime(&now);
853 until=now;
854 int actualSeconds = seconds ? *seconds : 10;
855 until.tv_sec += actualSeconds;
856 for(const auto& capair : m) {
857 unsigned int count = 0;
858 auto got = slow.lookup(Netmask(capair.first));
859 bool expired=false;
860 if(got) {
861 if(until < got->second.until) // had a longer policy
862 continue;
863 if(now < got->second.until) // only inherit count on fresh query we are extending
864 count=got->second.blocks;
865 else
866 expired=true;
867 }
868 DynBlock db{msg,until,DNSName(),(action ? *action : DNSAction::Action::None)};
869 db.blocks=count;
870 if(!got || expired)
871 warnlog("Inserting dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg);
872 slow.insert(Netmask(capair.first)).second=db;
873 }
874 g_dynblockNMG.setState(slow);
875 });
876
877 g_lua.writeFunction("addDynBlockSMT",
878 [](const vector<pair<unsigned int, string> >&names, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
6ddb008a
RG
879 if (names.empty()) {
880 return;
881 }
6bb38cd6
RG
882 setLuaSideEffect();
883 auto slow = g_dynblockSMT.getCopy();
884 struct timespec until, now;
885 gettime(&now);
886 until=now;
887 int actualSeconds = seconds ? *seconds : 10;
888 until.tv_sec += actualSeconds;
889
890 for(const auto& capair : names) {
891 unsigned int count = 0;
892 DNSName domain(capair.second);
893 auto got = slow.lookup(domain);
894 bool expired=false;
895 if(got) {
896 if(until < got->until) // had a longer policy
897 continue;
898 if(now < got->until) // only inherit count on fresh query we are extending
899 count=got->blocks;
900 else
901 expired=true;
902 }
903
904 DynBlock db{msg,until,domain,(action ? *action : DNSAction::Action::None)};
905 db.blocks=count;
906 if(!got || expired)
907 warnlog("Inserting dynamic block for %s for %d seconds: %s", domain, actualSeconds, msg);
908 slow.add(domain, db);
909 }
910 g_dynblockSMT.setState(slow);
911 });
912
913 g_lua.writeFunction("setDynBlocksAction", [](DNSAction::Action action) {
914 if (!g_configurationDone) {
915 if (action == DNSAction::Action::Drop || action == DNSAction::Action::Refused || action == DNSAction::Action::Truncate) {
916 g_dynBlockAction = action;
917 }
918 else {
919 errlog("Dynamic blocks action can only be Drop, Refused or Truncate!");
920 g_outputBuffer="Dynamic blocks action can only be Drop, Refused or Truncate!\n";
921 }
922 } else {
923 g_outputBuffer="Dynamic blocks action cannot be altered at runtime!\n";
924 }
df111b53 925 });
832c1792 926
6bb38cd6
RG
927 g_lua.writeFunction("addDNSCryptBind", [](const std::string& addr, const std::string& providerName, const std::string& certFile, const std::string keyFile, boost::optional<localbind_t> vars) {
928 if (g_configurationDone) {
929 g_outputBuffer="addDNSCryptBind cannot be used at runtime!\n";
930 return;
931 }
932#ifdef HAVE_DNSCRYPT
933 bool doTCP = true;
934 bool reusePort = false;
935 int tcpFastOpenQueueSize = 0;
936 std::string interface;
937 std::set<int> cpus;
df111b53 938
6bb38cd6 939 parseLocalBindVars(vars, doTCP, reusePort, tcpFastOpenQueueSize, interface, cpus);
df111b53 940
6bb38cd6 941 try {
43234e76 942 auto ctx = std::make_shared<DNSCryptContext>(providerName, certFile, keyFile);
6bb38cd6 943 g_dnsCryptLocals.push_back(std::make_tuple(ComboAddress(addr, 443), ctx, reusePort, tcpFastOpenQueueSize, interface, cpus));
df111b53 944 }
6bb38cd6
RG
945 catch(std::exception& e) {
946 errlog(e.what());
947 g_outputBuffer="Error: "+string(e.what())+"\n";
948 }
949#else
950 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
951#endif
4d5959e6 952
df111b53 953 });
954
6bb38cd6 955 g_lua.writeFunction("showDNSCryptBinds", []() {
f758857a 956 setLuaNoSideEffect();
6bb38cd6
RG
957#ifdef HAVE_DNSCRYPT
958 ostringstream ret;
43234e76
RG
959 boost::format fmt("%1$-3d %2% %|25t|%3$-20.20s");
960 ret << (fmt % "#" % "Address" % "Provider Name") << endl;
6bb38cd6
RG
961 size_t idx = 0;
962
963 for (const auto& local : g_dnsCryptLocals) {
43234e76
RG
964 const std::shared_ptr<DNSCryptContext> ctx = std::get<1>(local);
965 ret<< (fmt % idx % std::get<0>(local).toStringWithPort() % ctx->getProviderName()) << endl;
6bb38cd6 966 idx++;
df111b53 967 }
df111b53 968
6bb38cd6
RG
969 g_outputBuffer=ret.str();
970#else
971 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
972#endif
886e2cf2 973 });
df111b53 974
43234e76 975 g_lua.writeFunction("getDNSCryptBind", [](size_t idx) {
6bb38cd6
RG
976 setLuaNoSideEffect();
977#ifdef HAVE_DNSCRYPT
43234e76 978 std::shared_ptr<DNSCryptContext> ret = nullptr;
6bb38cd6 979 if (idx < g_dnsCryptLocals.size()) {
43234e76 980 ret = std::get<1>(g_dnsCryptLocals.at(idx));
d92708ed 981 }
6bb38cd6
RG
982 return ret;
983#else
984 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
985#endif
d92708ed
RG
986 });
987
6bb38cd6
RG
988 g_lua.writeFunction("generateDNSCryptProviderKeys", [](const std::string& publicKeyFile, const std::string privateKeyFile) {
989 setLuaNoSideEffect();
990#ifdef HAVE_DNSCRYPT
991 unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
992 unsigned char privateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
993 sodium_mlock(privateKey, sizeof(privateKey));
4c6f4321 994
6bb38cd6 995 try {
43234e76 996 DNSCryptContext::generateProviderKeys(publicKey, privateKey);
4c6f4321 997
6bb38cd6
RG
998 ofstream pubKStream(publicKeyFile);
999 pubKStream.write((char*) publicKey, sizeof(publicKey));
1000 pubKStream.close();
df111b53 1001
6bb38cd6
RG
1002 ofstream privKStream(privateKeyFile);
1003 privKStream.write((char*) privateKey, sizeof(privateKey));
1004 privKStream.close();
df111b53 1005
43234e76 1006 g_outputBuffer="Provider fingerprint is: " + DNSCryptContext::getProviderFingerprint(publicKey) + "\n";
6bb38cd6
RG
1007 }
1008 catch(std::exception& e) {
1009 errlog(e.what());
1010 g_outputBuffer="Error: "+string(e.what())+"\n";
1011 }
df111b53 1012
6bb38cd6
RG
1013 sodium_memzero(privateKey, sizeof(privateKey));
1014 sodium_munlock(privateKey, sizeof(privateKey));
1015#else
1016 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1017#endif
aeb36780
RG
1018 });
1019
6bb38cd6
RG
1020 g_lua.writeFunction("printDNSCryptProviderFingerprint", [](const std::string& publicKeyFile) {
1021 setLuaNoSideEffect();
1022#ifdef HAVE_DNSCRYPT
1023 unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
aeb36780 1024
6bb38cd6
RG
1025 try {
1026 ifstream file(publicKeyFile);
1027 file.read((char *) &publicKey, sizeof(publicKey));
df111b53 1028
6bb38cd6
RG
1029 if (file.fail())
1030 throw std::runtime_error("Invalid dnscrypt provider public key file " + publicKeyFile);
df111b53 1031
6bb38cd6 1032 file.close();
43234e76 1033 g_outputBuffer="Provider fingerprint is: " + DNSCryptContext::getProviderFingerprint(publicKey) + "\n";
6bb38cd6
RG
1034 }
1035 catch(std::exception& e) {
1036 errlog(e.what());
1037 g_outputBuffer="Error: "+string(e.what())+"\n";
1038 }
1039#else
1040 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
1041#endif
df111b53 1042 });
1043
501983cd 1044#ifdef HAVE_DNSCRYPT
43234e76 1045 g_lua.writeFunction("generateDNSCryptCertificate", [](const std::string& providerPrivateKeyFile, const std::string& certificateFile, const std::string privateKeyFile, uint32_t serial, time_t begin, time_t end, boost::optional<DNSCryptExchangeVersion> version) {
6bb38cd6 1046 setLuaNoSideEffect();
43234e76
RG
1047 DNSCryptPrivateKey privateKey;
1048 DNSCryptCert cert;
42fae326 1049
50bed881 1050 try {
43234e76 1051 if (generateDNSCryptCertificate(providerPrivateKeyFile, serial, begin, end, version ? *version : DNSCryptExchangeVersion::VERSION1, cert, privateKey)) {
6bb38cd6 1052 privateKey.saveToFile(privateKeyFile);
43234e76 1053 DNSCryptContext::saveCertFromFile(cert, certificateFile);
6bb38cd6 1054 }
50bed881 1055 }
6bb38cd6
RG
1056 catch(const std::exception& e) {
1057 errlog(e.what());
1058 g_outputBuffer="Error: "+string(e.what())+"\n";
50bed881 1059 }
50bed881 1060 });
501983cd 1061#endif
df111b53 1062
6bb38cd6
RG
1063 g_lua.writeFunction("showPools", []() {
1064 setLuaNoSideEffect();
df111b53 1065 try {
6bb38cd6
RG
1066 ostringstream ret;
1067 boost::format fmt("%1$-20.20s %|25t|%2$20s %|25t|%3$20s %|50t|%4%" );
1068 // 1 2 3 4
1069 ret << (fmt % "Name" % "Cache" % "ServerPolicy" % "Servers" ) << endl;
1070
1071 const auto localPools = g_pools.getCopy();
1072 for (const auto& entry : localPools) {
1073 const string& name = entry.first;
1074 const std::shared_ptr<ServerPool> pool = entry.second;
1075 string cache = pool->packetCache != nullptr ? pool->packetCache->toString() : "";
1076 string policy = g_policy.getLocal()->name;
1077 if (pool->policy != nullptr) {
1078 policy = pool->policy->name;
1079 }
1080 string servers;
1081
a1b1a29d 1082 for (const auto& server: pool->getServers()) {
6bb38cd6
RG
1083 if (!servers.empty()) {
1084 servers += ", ";
1085 }
1086 if (!server.second->name.empty()) {
1087 servers += server.second->name;
1088 servers += " ";
1089 }
1090 servers += server.second->remote.toStringWithPort();
1091 }
1092
1093 ret << (fmt % name % cache % policy % servers) << endl;
1094 }
1095 g_outputBuffer=ret.str();
1096 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
1097 });
1098
1099 g_lua.writeFunction("getPool", [client](const string& poolName) {
1100 if (client) {
1101 return std::make_shared<ServerPool>();
df111b53 1102 }
6bb38cd6
RG
1103 auto localPools = g_pools.getCopy();
1104 std::shared_ptr<ServerPool> pool = createPoolIfNotExists(localPools, poolName);
1105 g_pools.setState(localPools);
1106 return pool;
df111b53 1107 });
1108
6bb38cd6
RG
1109 g_lua.writeFunction("setVerboseHealthChecks", [](bool verbose) { g_verboseHealthChecks=verbose; });
1110 g_lua.writeFunction("setStaleCacheEntriesTTL", [](uint32_t ttl) { g_staleCacheEntriesTTL = ttl; });
03ebf8b2 1111
6bb38cd6 1112 g_lua.writeFunction("showBinds", []() {
f758857a 1113 setLuaNoSideEffect();
6bb38cd6
RG
1114 try {
1115 ostringstream ret;
1116 boost::format fmt("%1$-3d %2$-20.20s %|25t|%3$-8.8s %|35t|%4%" );
1117 // 1 2 3 4
1118 ret << (fmt % "#" % "Address" % "Protocol" % "Queries" ) << endl;
1119
1120 size_t counter = 0;
1121 for (const auto& front : g_frontends) {
1122 ret << (fmt % counter % front->local.toStringWithPort() % (front->udpFD != -1 ? "UDP" : "TCP") % front->queries) << endl;
1123 counter++;
0e41337b 1124 }
6bb38cd6
RG
1125 g_outputBuffer=ret.str();
1126 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
0e5b3cff 1127 });
1128
6bb38cd6 1129 g_lua.writeFunction("getBind", [](size_t num) {
f758857a 1130 setLuaNoSideEffect();
6bb38cd6
RG
1131 ClientState* ret = nullptr;
1132 if(num < g_frontends.size()) {
1133 ret=g_frontends[num];
df111b53 1134 }
df111b53 1135 return ret;
6bb38cd6 1136 });
df111b53 1137
6bb38cd6
RG
1138 g_lua.writeFunction("help", [](boost::optional<std::string> command) {
1139 setLuaNoSideEffect();
1140 g_outputBuffer = "";
1141 for (const auto& keyword : g_consoleKeywords) {
1142 if (!command) {
1143 g_outputBuffer += keyword.toString() + "\n";
1144 }
1145 else if (keyword.name == command) {
1146 g_outputBuffer = keyword.toString() + "\n";
1147 return;
1148 }
1149 }
1150 if (command) {
1151 g_outputBuffer = "Nothing found for " + *command + "\n";
786e4d8c 1152 }
786e4d8c 1153 });
520eb5a0 1154
6bb38cd6 1155 g_lua.writeFunction("showVersion", []() {
786e4d8c 1156 setLuaNoSideEffect();
6bb38cd6 1157 g_outputBuffer = "dnsdist " + std::string(VERSION) + "\n";
786e4d8c
RS
1158 });
1159
6bb38cd6
RG
1160#ifdef HAVE_EBPF
1161 g_lua.writeFunction("setDefaultBPFFilter", [](std::shared_ptr<BPFFilter> bpf) {
1162 if (g_configurationDone) {
1163 g_outputBuffer="setDefaultBPFFilter() cannot be used at runtime!\n";
1164 return;
1165 }
1166 g_defaultBPFFilter = bpf;
786e4d8c 1167 });
62edea30 1168
6bb38cd6
RG
1169 g_lua.writeFunction("registerDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
1170 if (dbpf) {
1171 g_dynBPFFilters.push_back(dbpf);
520eb5a0 1172 }
520eb5a0 1173 });
f5b58807 1174
6bb38cd6
RG
1175 g_lua.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
1176 if (dbpf) {
1177 for (auto it = g_dynBPFFilters.begin(); it != g_dynBPFFilters.end(); it++) {
1178 if (*it == dbpf) {
1179 g_dynBPFFilters.erase(it);
1180 break;
1181 }
1182 }
1183 }
2d11d1b2 1184 });
df111b53 1185
b6ba8123 1186 g_lua.writeFunction("addBPFFilterDynBlocks", [](const std::unordered_map<ComboAddress,unsigned int, ComboAddress::addressOnlyHash, ComboAddress::addressOnlyEqual>& m, std::shared_ptr<DynBPFFilter> dynbpf, boost::optional<int> seconds, boost::optional<std::string> msg) {
6bb38cd6
RG
1187 setLuaSideEffect();
1188 struct timespec until, now;
1189 clock_gettime(CLOCK_MONOTONIC, &now);
1190 until=now;
1191 int actualSeconds = seconds ? *seconds : 10;
1192 until.tv_sec += actualSeconds;
1193 for(const auto& capair : m) {
4900be3f
RG
1194 if (dynbpf->block(capair.first, until)) {
1195 warnlog("Inserting eBPF dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg ? *msg : "");
1196 }
6bb38cd6 1197 }
df111b53 1198 });
62edea30 1199
6bb38cd6 1200#endif /* HAVE_EBPF */
2d11d1b2 1201
6bb38cd6 1202 g_lua.writeFunction<std::unordered_map<string,uint64_t>()>("getStatisticsCounters", []() {
f758857a 1203 setLuaNoSideEffect();
6bb38cd6
RG
1204 std::unordered_map<string,uint64_t> res;
1205 for(const auto& entry : g_stats.entries) {
1206 if(const auto& val = boost::get<DNSDistStats::stat_t*>(&entry.second))
1207 res[entry.first] = (*val)->load();
df111b53 1208 }
6bb38cd6
RG
1209 return res;
1210 });
df111b53 1211
6bb38cd6
RG
1212 g_lua.writeFunction("includeDirectory", [](const std::string& dirname) {
1213 if (g_configurationDone) {
1214 errlog("includeDirectory() cannot be used at runtime!");
1215 g_outputBuffer="includeDirectory() cannot be used at runtime!\n";
1216 return;
df111b53 1217 }
1218
6bb38cd6
RG
1219 if (g_included) {
1220 errlog("includeDirectory() cannot be used recursively!");
1221 g_outputBuffer="includeDirectory() cannot be used recursively!\n";
e12b3374
RG
1222 return;
1223 }
1224
6bb38cd6
RG
1225 g_included = true;
1226 struct stat st;
1227 if (stat(dirname.c_str(), &st)) {
1228 errlog("The included directory %s does not exist!", dirname.c_str());
1229 g_outputBuffer="The included directory " + dirname + " does not exist!";
1230 return;
df111b53 1231 }
df111b53 1232
6bb38cd6
RG
1233 if (!S_ISDIR(st.st_mode)) {
1234 errlog("The included directory %s is not a directory!", dirname.c_str());
1235 g_outputBuffer="The included directory " + dirname + " is not a directory!";
1236 return;
6f6b4d69 1237 }
1238
6bb38cd6
RG
1239 DIR *dirp;
1240 struct dirent *ent;
1241 std::list<std::string> files;
1242 if (!(dirp = opendir(dirname.c_str()))) {
1243 errlog("Error opening the included directory %s!", dirname.c_str());
1244 g_outputBuffer="Error opening the included directory " + dirname + "!";
1245 return;
6f6b4d69 1246 }
df111b53 1247
6bb38cd6
RG
1248 while((ent = readdir(dirp)) != NULL) {
1249 if (ent->d_name[0] == '.') {
1250 continue;
1251 }
3f6d07a4 1252
6bb38cd6
RG
1253 if (boost::ends_with(ent->d_name, ".conf")) {
1254 std::ostringstream namebuf;
1255 namebuf << dirname.c_str() << "/" << ent->d_name;
2a817e5a 1256
6bb38cd6
RG
1257 if (stat(namebuf.str().c_str(), &st) || !S_ISREG(st.st_mode)) {
1258 continue;
1259 }
e0b5e49d 1260
6bb38cd6
RG
1261 files.push_back(namebuf.str());
1262 }
e41f8165 1263 }
e41f8165 1264
6bb38cd6
RG
1265 closedir(dirp);
1266 files.sort();
1267
1268 for (auto file = files.begin(); file != files.end(); ++file) {
1269 std::ifstream ifs(*file);
1270 if (!ifs) {
1271 warnlog("Unable to read configuration from '%s'", *file);
1272 } else {
1273 vinfolog("Read configuration from '%s'", *file);
1274 }
26a6373d 1275
6bb38cd6 1276 g_lua.executeCode(ifs);
741ebe08 1277 }
26a6373d 1278
6bb38cd6 1279 g_included = false;
26a6373d
SO
1280 });
1281
6bb38cd6
RG
1282 g_lua.writeFunction("setAPIWritable", [](bool writable, boost::optional<std::string> apiConfigDir) {
1283 setLuaSideEffect();
1284 g_apiReadWrite = writable;
1285 if (apiConfigDir) {
1286 if (!(*apiConfigDir).empty()) {
1287 g_apiConfigDirectory = *apiConfigDir;
1288 }
1289 else {
1290 errlog("The API configuration directory value cannot be empty!");
1291 g_outputBuffer="The API configuration directory value cannot be empty!";
1292 }
741ebe08 1293 }
5b8255ba 1294 });
26a6373d 1295
6bb38cd6
RG
1296 g_lua.writeFunction("setServFailWhenNoServer", [](bool servfail) {
1297 setLuaSideEffect();
1298 g_servFailOnNoPolicy = servfail;
1299 });
26a6373d 1300
6bb38cd6
RG
1301 g_lua.writeFunction("setRingBuffersSize", [](size_t capacity) {
1302 setLuaSideEffect();
1303 if (g_configurationDone) {
1304 errlog("setRingBuffersSize() cannot be used at runtime!");
1305 g_outputBuffer="setRingBuffersSize() cannot be used at runtime!\n";
1306 return;
5b8255ba 1307 }
6bb38cd6 1308 g_rings.setCapacity(capacity);
5b8255ba 1309 });
26a6373d 1310
6bb38cd6
RG
1311 g_lua.writeFunction("setWHashedPertubation", [](uint32_t pertub) {
1312 setLuaSideEffect();
1313 g_hashperturb = pertub;
1314 });
26a6373d 1315
6bb38cd6
RG
1316 g_lua.writeFunction("setTCPUseSinglePipe", [](bool flag) {
1317 if (g_configurationDone) {
1318 g_outputBuffer="setTCPUseSinglePipe() cannot be used at runtime!\n";
1319 return;
5b8255ba 1320 }
6bb38cd6
RG
1321 setLuaSideEffect();
1322 g_useTCPSinglePipe = flag;
5b8255ba 1323 });
26a6373d 1324
cd4bb56b
PD
1325 g_lua.writeFunction("snmpAgent", [client](bool enableTraps, boost::optional<std::string> masterSocket) {
1326 if(client)
1327 return;
9f4eb5cc 1328#ifdef HAVE_NET_SNMP
6bb38cd6
RG
1329 if (g_configurationDone) {
1330 errlog("snmpAgent() cannot be used at runtime!");
1331 g_outputBuffer="snmpAgent() cannot be used at runtime!\n";
1332 return;
1333 }
1334
1335 if (g_snmpEnabled) {
1336 errlog("snmpAgent() cannot be used twice!");
1337 g_outputBuffer="snmpAgent() cannot be used twice!\n";
1338 return;
9f4eb5cc 1339 }
6bb38cd6
RG
1340
1341 g_snmpEnabled = true;
1342 g_snmpTrapsEnabled = enableTraps;
1343 g_snmpAgent = new DNSDistSNMPAgent("dnsdist", masterSocket ? *masterSocket : std::string());
1344#else
1345 errlog("NET SNMP support is required to use snmpAgent()");
1346 g_outputBuffer="NET SNMP support is required to use snmpAgent()\n";
9f4eb5cc
RG
1347#endif /* HAVE_NET_SNMP */
1348 });
497a6e3a 1349
6bb38cd6 1350 g_lua.writeFunction("sendCustomTrap", [](const std::string& str) {
9f4eb5cc
RG
1351#ifdef HAVE_NET_SNMP
1352 if (g_snmpAgent && g_snmpTrapsEnabled) {
6bb38cd6 1353 g_snmpAgent->sendCustomTrap(str);
9f4eb5cc
RG
1354 }
1355#endif /* HAVE_NET_SNMP */
1356 });
6beb5731 1357
6bb38cd6
RG
1358 g_lua.writeFunction("setPoolServerPolicy", [](ServerPolicy policy, string pool) {
1359 setLuaSideEffect();
1360 auto localPools = g_pools.getCopy();
1361 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(policy));
1362 g_pools.setState(localPools);
6c1ca990 1363 });
e41f8165 1364
6bb38cd6
RG
1365 g_lua.writeFunction("setPoolServerPolicyLua", [](string name, policyfunc_t policy, string pool) {
1366 setLuaSideEffect();
1367 auto localPools = g_pools.getCopy();
a1b1a29d 1368 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, policy, true}));
6bb38cd6 1369 g_pools.setState(localPools);
9396d955
RG
1370 });
1371
6bb38cd6
RG
1372 g_lua.writeFunction("showPoolServerPolicy", [](string pool) {
1373 setLuaSideEffect();
1374 auto localPools = g_pools.getCopy();
1375 auto poolObj = getPool(localPools, pool);
1376 if (poolObj->policy == nullptr) {
1377 g_outputBuffer=g_policy.getLocal()->name+"\n";
9396d955 1378 } else {
6bb38cd6 1379 g_outputBuffer=poolObj->policy->name+"\n";
9396d955
RG
1380 }
1381 });
1382
6bb38cd6
RG
1383 g_lua.writeFunction("setTCPDownstreamCleanupInterval", [](uint16_t interval) {
1384 setLuaSideEffect();
1385 g_downstreamTCPCleanupInterval = interval;
9396d955
RG
1386 });
1387
6bb38cd6
RG
1388 g_lua.writeFunction("setConsoleConnectionsLogging", [](bool enabled) {
1389 g_logConsoleConnections = enabled;
e65ae260
RG
1390 });
1391
6bb38cd6
RG
1392 g_lua.writeFunction("setUDPMultipleMessagesVectorSize", [](size_t vSize) {
1393 if (g_configurationDone) {
1394 errlog("setUDPMultipleMessagesVectorSize() cannot be used at runtime!");
1395 g_outputBuffer="setUDPMultipleMessagesVectorSize() cannot be used at runtime!\n";
1396 return;
2a817e5a 1397 }
6bb38cd6
RG
1398#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
1399 setLuaSideEffect();
1400 g_udpVectorSize = vSize;
1401#else
1402 errlog("recvmmsg() support is not available!");
1403 g_outputBuffer="recvmmsg support is not available!\n";
1404#endif
1405 });
a227f47d
RG
1406
1407 g_lua.writeFunction("addTLSLocal", [client](const std::string& addr, const std::string& certFile, const std::string& keyFile, boost::optional<localbind_t> vars) {
1408 if (client)
1409 return;
1410#ifdef HAVE_DNS_OVER_TLS
1411 setLuaSideEffect();
1412 if (g_configurationDone) {
1413 g_outputBuffer="addTLSLocal cannot be used at runtime!\n";
1414 return;
1415 }
1416 shared_ptr<TLSFrontend> frontend = std::make_shared<TLSFrontend>();
1417 frontend->d_certFile = certFile;
1418 frontend->d_keyFile = keyFile;
1419
1420 if (vars) {
1421 bool doTCP = true;
1422 parseLocalBindVars(vars, doTCP, frontend->d_reusePort, frontend->d_tcpFastOpenQueueSize, frontend->d_interface, frontend->d_cpus);
1423
1424 if (vars->count("provider")) {
1425 frontend->d_provider = boost::get<const string>((*vars)["provider"]);
1426 }
1427
1428 if (vars->count("ciphers")) {
1429 frontend->d_ciphers = boost::get<const string>((*vars)["ciphers"]);
1430 }
1431
1432 if (vars->count("ticketKeyFile")) {
1433 frontend->d_ticketKeyFile = boost::get<const string>((*vars)["ticketKeyFile"]);
1434 }
1435
1436 if (vars->count("ticketsKeysRotationDelay")) {
1437 frontend->d_ticketsKeyRotationDelay = std::stoi(boost::get<const string>((*vars)["ticketsKeysRotationDelay"]));
1438 }
1439
1440 if (vars->count("numberOfTicketsKeys")) {
1441 frontend->d_numberOfTicketsKeys = std::stoi(boost::get<const string>((*vars)["numberOfTicketsKeys"]));
1442 }
1443 }
1444
1445 try {
1446 frontend->d_addr = ComboAddress(addr, 853);
1447 vinfolog("Loading TLS provider %s", frontend->d_provider);
1448 g_tlslocals.push_back(frontend); /// only works pre-startup, so no sync necessary
1449 }
1450 catch(const std::exception& e) {
1451 g_outputBuffer="Error: "+string(e.what())+"\n";
1452 }
1453#else
1454 g_outputBuffer="DNS over TLS support is not present!\n";
1455#endif
1456 });
1457
7f2f825d 1458 g_lua.writeFunction("showTLSContexts", []() {
a227f47d
RG
1459#ifdef HAVE_DNS_OVER_TLS
1460 setLuaNoSideEffect();
1461 try {
1462 ostringstream ret;
1463 boost::format fmt("%1$-3d %2$-20.20s %|25t|%3$-14d %|40t|%4$-14d %|54t|%5$-21.21s");
1464 // 1 2 3 4 5
1465 ret << (fmt % "#" % "Address" % "# ticket keys" % "Rotation delay" % "Next rotation" ) << endl;
1466 size_t counter = 0;
1467 for (const auto& ctx : g_tlslocals) {
1468 ret << (fmt % counter % ctx->d_addr.toStringWithPort() % ctx->getTicketsKeysCount() % ctx->getTicketsKeyRotationDelay() % ctx->getNextTicketsKeyRotation()) << endl;
1469 counter++;
1470 }
1471 g_outputBuffer = ret.str();
1472 }
1473 catch(const std::exception& e) {
1474 g_outputBuffer = e.what();
1475 throw;
1476 }
1477#else
1478 g_outputBuffer="DNS over TLS support is not present!\n";
1479#endif
1480 });
1481
7f2f825d 1482 g_lua.writeFunction("getTLSContext", [](size_t index) {
a227f47d
RG
1483 std::shared_ptr<TLSCtx> result = nullptr;
1484#ifdef HAVE_DNS_OVER_TLS
1485 setLuaNoSideEffect();
1486 try {
1487 if (index < g_tlslocals.size()) {
1488 result = g_tlslocals.at(index)->getContext();
1489 }
1490 else {
1491 errlog("Error: trying to get TLS context with index %zu but we only have %zu\n", index, g_tlslocals.size());
1492 g_outputBuffer="Error: trying to get TLS context with index " + std::to_string(index) + " but we only have " + std::to_string(g_tlslocals.size()) + "\n";
1493 }
1494 }
1495 catch(const std::exception& e) {
1496 g_outputBuffer="Error: "+string(e.what())+"\n";
1497 errlog("Error: %s\n", string(e.what()));
1498 }
1499#else
1500 g_outputBuffer="DNS over TLS support is not present!\n";
1501#endif
1502 return result;
1503 });
1504
1505 g_lua.registerFunction<void(std::shared_ptr<TLSCtx>::*)()>("rotateTicketsKey", [](std::shared_ptr<TLSCtx> ctx) {
1506 if (ctx != nullptr) {
1507 ctx->rotateTicketsKey(time(nullptr));
1508 }
1509 });
1510
1511 g_lua.registerFunction<void(std::shared_ptr<TLSCtx>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<TLSCtx> ctx, const std::string& file) {
1512 if (ctx != nullptr) {
1513 ctx->loadTicketsKeys(file);
1514 }
1515 });
6bb38cd6 1516}
2a817e5a 1517
6bb38cd6
RG
1518vector<std::function<void(void)>> setupLua(bool client, const std::string& config)
1519{
1520 g_launchWork= new vector<std::function<void(void)>>();
2a817e5a 1521
6bb38cd6
RG
1522 setupLuaActions();
1523 setupLuaConfig(client);
1524 setupLuaBindings(client);
1525 setupLuaBindingsDNSQuestion();
1526 setupLuaInspection();
1527 setupLuaRules();
1528 setupLuaVars();
80a216c9 1529
839f3021 1530 std::ifstream ifs(config);
6bb38cd6 1531 if(!ifs)
839f3021 1532 warnlog("Unable to read configuration from '%s'", config);
2e72cc0e 1533 else
cdc04ede 1534 vinfolog("Read configuration from '%s'", config);
df111b53 1535
1536 g_lua.executeCode(ifs);
d8c19b98 1537
6bb38cd6 1538 auto ret = *g_launchWork;
2e72cc0e 1539 delete g_launchWork;
6bb38cd6 1540 g_launchWork = nullptr;
2e72cc0e 1541 return ret;
df111b53 1542}