]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-lua.cc
dnsdist: Add missing `newServer` options, pool management functions to the docs
[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 */
9ef9b494
PD
22#include <sys/types.h>
23#include <sys/socket.h>
65613131 24#include <net/if.h>
df111b53 25#include "dnsdist.hh"
0940e4eb 26#include "dnsrulactions.hh"
df111b53 27#include <thread>
28#include "dolog.hh"
29#include "sodcrypto.hh"
30#include "base64.hh"
31#include <fstream>
b7860997 32#include "dnswriter.hh"
0e41337b 33#include "lock.hh"
cf48b0ce 34#include "dnsdist-lua.hh"
df111b53 35
6ab65223
PL
36#ifdef HAVE_SYSTEMD
37#include <systemd/sd-daemon.h>
38#endif
39
df111b53 40using std::thread;
41
2e72cc0e 42static vector<std::function<void(void)>>* g_launchWork;
43
d8d85a30 44class LuaAction : public DNSAction
45{
46public:
497a6e3a 47 typedef std::function<std::tuple<int, string>(DNSQuestion* dq)> func_t;
d8d85a30 48 LuaAction(LuaAction::func_t func) : d_func(func)
49 {}
50
497a6e3a 51 Action operator()(DNSQuestion* dq, string* ruleresult) const override
d8d85a30 52 {
01d9286f 53 std::lock_guard<std::mutex> lock(g_luamutex);
497a6e3a 54 auto ret = d_func(dq);
d8d85a30 55 if(ruleresult)
56 *ruleresult=std::get<1>(ret);
57 return (Action)std::get<0>(ret);
58 }
59
497a6e3a 60 string toString() const override
d8d85a30 61 {
62 return "Lua script";
63 }
64
65private:
66 func_t d_func;
67};
68
153d5065
RG
69class LuaResponseAction : public DNSResponseAction
70{
71public:
72 typedef std::function<std::tuple<int, string>(DNSResponse* dr)> func_t;
73 LuaResponseAction(LuaResponseAction::func_t func) : d_func(func)
74 {}
75
76 Action operator()(DNSResponse* dr, string* ruleresult) const override
77 {
78 std::lock_guard<std::mutex> lock(g_luamutex);
79 auto ret = d_func(dr);
80 if(ruleresult)
81 *ruleresult=std::get<1>(ret);
82 return (Action)std::get<0>(ret);
83 }
84
85 string toString() const override
86 {
87 return "Lua response script";
88 }
89
90private:
91 func_t d_func;
92};
93
6bba426c 94std::shared_ptr<DNSRule> makeRule(const luadnsrule_t& var)
d8d85a30 95{
f850b032
PL
96 if (var.type() == typeid(std::shared_ptr<DNSRule>))
97 return *boost::get<std::shared_ptr<DNSRule>>(&var);
98
d8d85a30 99 SuffixMatchNode smn;
100 NetmaskGroup nmg;
d8d85a30 101 auto add=[&](string src) {
102 try {
5ad8b87a 103 nmg.addMask(src); // need to try mask first, all masks are domain names!
d8d85a30 104 } catch(...) {
5ad8b87a 105 smn.add(DNSName(src));
d8d85a30 106 }
107 };
f850b032
PL
108
109 if (var.type() == typeid(string))
110 add(*boost::get<string>(&var));
111
112 else if (var.type() == typeid(vector<pair<int, string>>))
113 for(const auto& a : *boost::get<vector<pair<int, string>>>(&var))
d8d85a30 114 add(a.second);
f850b032
PL
115
116 else if (var.type() == typeid(DNSName))
117 smn.add(*boost::get<DNSName>(&var));
118
119 else if (var.type() == typeid(vector<pair<int, DNSName>>))
120 for(const auto& a : *boost::get<vector<pair<int, DNSName>>>(&var))
121 smn.add(a.second);
122
d8d85a30 123 if(nmg.empty())
124 return std::make_shared<SuffixMatchNodeRule>(smn);
125 else
8a616250 126 return std::make_shared<NetmaskGroupRule>(nmg, true);
d8d85a30 127}
128
2d11d1b2 129std::unordered_map<int, vector<boost::variant<string,double>>> getGenResponses(unsigned int top, boost::optional<int> labels, std::function<bool(const Rings::Response&)> pred)
130{
131 setLuaNoSideEffect();
132 map<DNSName, int> counts;
133 unsigned int total=0;
134 {
135 std::lock_guard<std::mutex> lock(g_rings.respMutex);
136 if(!labels) {
137 for(const auto& a : g_rings.respRing) {
138 if(!pred(a))
139 continue;
140 counts[a.name]++;
141 total++;
142 }
143 }
144 else {
145 unsigned int lab = *labels;
146 for(auto a : g_rings.respRing) {
147 if(!pred(a))
148 continue;
149
150 a.name.trimToLabels(lab);
151 counts[a.name]++;
152 total++;
153 }
154
155 }
156 }
157 // cout<<"Looked at "<<total<<" responses, "<<counts.size()<<" different ones"<<endl;
158 vector<pair<int, DNSName>> rcounts;
159 rcounts.reserve(counts.size());
160 for(const auto& c : counts)
2f95ab40 161 rcounts.push_back(make_pair(c.second, c.first.makeLowerCase()));
2d11d1b2 162
163 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
164 const decltype(rcounts)::value_type& b) {
165 return b.first < a.first;
166 });
167
168 std::unordered_map<int, vector<boost::variant<string,double>>> ret;
169 unsigned int count=1, rest=0;
170 for(const auto& rc : rcounts) {
171 if(count==top+1)
172 rest+=rc.first;
173 else
174 ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
175 }
6a62c0e3 176 ret.insert({count, {"Rest", rest, total > 0 ? 100.0*rest/total : 100.0}});
2d11d1b2 177 return ret;
178}
179
efd35aa8
RG
180void parseLocalBindVars(boost::optional<localbind_t> vars, bool& doTCP, bool& reusePort, int& tcpFastOpenQueueSize, std::string& interface)
181{
182 if (vars) {
183 if (vars->count("doTCP")) {
184 doTCP = boost::get<bool>((*vars)["doTCP"]);
185 }
186 if (vars->count("reusePort")) {
187 reusePort = boost::get<bool>((*vars)["reusePort"]);
188 }
189 if (vars->count("tcpFastOpenQueueSize")) {
190 tcpFastOpenQueueSize = boost::get<int>((*vars)["tcpFastOpenQueueSize"]);
191 }
192 if (vars->count("interface")) {
193 interface = boost::get<std::string>((*vars)["interface"]);
194 }
195 }
196}
197
839f3021 198vector<std::function<void(void)>> setupLua(bool client, const std::string& config)
df111b53 199{
2e72cc0e 200 g_launchWork= new vector<std::function<void(void)>>();
a6e02424 201 typedef std::unordered_map<std::string, boost::variant<bool, std::string, vector<pair<int, std::string> > > > newserver_t;
e48090d1 202
d8d85a30 203 g_lua.writeVariable("DNSAction", std::unordered_map<string,int>{
dd46e5e3
RG
204 {"Drop", (int)DNSAction::Action::Drop},
205 {"Nxdomain", (int)DNSAction::Action::Nxdomain},
206 {"Refused", (int)DNSAction::Action::Refused},
207 {"Spoof", (int)DNSAction::Action::Spoof},
208 {"Allow", (int)DNSAction::Action::Allow},
d8d85a30 209 {"HeaderModify", (int)DNSAction::Action::HeaderModify},
dd46e5e3 210 {"Pool", (int)DNSAction::Action::Pool},
21c2cdd8 211 {"None",(int)DNSAction::Action::None},
c4f5aeff
RG
212 {"Delay", (int)DNSAction::Action::Delay},
213 {"Truncate", (int)DNSAction::Action::Truncate}
214 });
bac6e8fb 215
55baa1f2 216 g_lua.writeVariable("DNSResponseAction", std::unordered_map<string,int>{
788c3243
RG
217 {"Allow", (int)DNSResponseAction::Action::Allow },
218 {"Delay", (int)DNSResponseAction::Action::Delay },
219 {"HeaderModify", (int)DNSResponseAction::Action::HeaderModify },
220 {"None", (int)DNSResponseAction::Action::None }
221 });
8146444b 222
55baa1f2
RG
223 g_lua.writeVariable("DNSClass", std::unordered_map<string,int>{
224 {"IN", QClass::IN },
225 {"CHAOS", QClass::CHAOS },
226 {"NONE", QClass::NONE },
227 {"ANY", QClass::ANY }
228 });
229
230 g_lua.writeVariable("DNSOpcode", std::unordered_map<string,int>{
231 {"Query", Opcode::Query },
232 {"IQuery", Opcode::IQuery },
233 {"Status", Opcode::Status },
234 {"Notify", Opcode::Notify },
235 {"Update", Opcode::Update }
236 });
237
238 g_lua.writeVariable("DNSSection", std::unordered_map<string,int>{
239 {"Question", 0 },
240 {"Answer", 1 },
241 {"Authority", 2 },
242 {"Additional",3 }
243 });
244
245 vector<pair<string, int> > rcodes = {{"NOERROR", RCode::NoError },
246 {"FORMERR", RCode::FormErr },
247 {"SERVFAIL", RCode::ServFail },
248 {"NXDOMAIN", RCode::NXDomain },
249 {"NOTIMP", RCode::NotImp },
250 {"REFUSED", RCode::Refused },
251 {"YXDOMAIN", RCode::YXDomain },
252 {"YXRRSET", RCode::YXRRSet },
253 {"NXRRSET", RCode::NXRRSet },
254 {"NOTAUTH", RCode::NotAuth },
255 {"NOTZONE", RCode::NotZone }
256 };
bac6e8fb 257 vector<pair<string, int> > dd;
258 for(const auto& n : QType::names)
259 dd.push_back({n.first, n.second});
55baa1f2
RG
260 for(const auto& n : rcodes)
261 dd.push_back({n.first, n.second});
bac6e8fb 262 g_lua.writeVariable("dnsdist", dd);
d8d85a30 263
df111b53 264 g_lua.writeFunction("newServer",
839f3021 265 [client](boost::variant<string,newserver_t> pvars, boost::optional<int> qps)
df111b53 266 {
f758857a 267 setLuaSideEffect();
2e72cc0e 268 if(client) {
b4fd86c3 269 return std::make_shared<DownstreamState>(ComboAddress());
2e72cc0e 270 }
fbe2a2e0
RG
271 ComboAddress sourceAddr;
272 unsigned int sourceItf = 0;
557d7631 273 if(auto addressStr = boost::get<string>(&pvars)) {
cbbd1533 274 std::shared_ptr<DownstreamState> ret;
275 try {
bd01350a
RG
276 ComboAddress address(*addressStr, 53);
277 if(IsAnyAddress(address)) {
278 g_outputBuffer="Error creating new server: invalid address for a downstream server.";
279 errlog("Error creating new server: %s is not a valid address for a downstream server", *addressStr);
280 return ret;
281 }
557d7631 282 ret=std::make_shared<DownstreamState>(address);
cbbd1533 283 }
bd01350a
RG
284 catch(const PDNSException& e) {
285 g_outputBuffer="Error creating new server: "+string(e.reason);
286 errlog("Error creating new server with address %s: %s", addressStr, e.reason);
287 return ret;
288 }
cbbd1533 289 catch(std::exception& e) {
290 g_outputBuffer="Error creating new server: "+string(e.what());
557d7631 291 errlog("Error creating new server with address %s: %s", addressStr, e.what());
cbbd1533 292 return ret;
293 }
2e72cc0e 294
df111b53 295 if(qps) {
296 ret->qps=QPSLimiter(*qps, *qps);
297 }
ecbe9133 298 g_dstates.modify([ret](servers_t& servers) {
299 servers.push_back(ret);
300 std::stable_sort(servers.begin(), servers.end(), [](const decltype(ret)& a, const decltype(ret)& b) {
301 return a->order < b->order;
302 });
303
304 });
2e72cc0e 305
46a839bf
RG
306 auto localPools = g_pools.getCopy();
307 addServerToPool(localPools, "", ret);
308 g_pools.setState(localPools);
309
7565f4e6
RG
310 if (ret->connected) {
311 if(g_launchWork) {
312 g_launchWork->push_back([ret]() {
2717a92f 313 ret->tid = thread(responderThread, ret);
2e72cc0e 314 });
7565f4e6
RG
315 }
316 else {
2717a92f 317 ret->tid = thread(responderThread, ret);
7565f4e6 318 }
2e72cc0e 319 }
320
df111b53 321 return ret;
322 }
839f3021 323 auto vars=boost::get<newserver_t>(pvars);
fbe2a2e0
RG
324
325 if(vars.count("source")) {
326 /* handle source in the following forms:
327 - v4 address ("192.0.2.1")
328 - v6 address ("2001:DB8::1")
329 - interface name ("eth0")
330 - v4 address and interface name ("192.0.2.1@eth0")
331 - v6 address and interface name ("2001:DB8::1@eth0")
332 */
333 const string source = boost::get<string>(vars["source"]);
334 bool parsed = false;
335 std::string::size_type pos = source.find("@");
336 if (pos == std::string::npos) {
337 /* no '@', try to parse that as a valid v4/v6 address */
338 try {
339 sourceAddr = ComboAddress(source);
340 parsed = true;
341 }
342 catch(...)
343 {
344 }
345 }
346
347 if (parsed == false)
348 {
349 /* try to parse as interface name, or v4/v6@itf */
350 string itfName = source.substr(pos == std::string::npos ? 0 : pos + 1);
351 unsigned int itfIdx = if_nametoindex(itfName.c_str());
352
353 if (itfIdx != 0) {
354 if (pos == 0 || pos == std::string::npos) {
355 /* "eth0" or "@eth0" */
356 sourceItf = itfIdx;
357 }
358 else {
359 /* "192.0.2.1@eth0" */
360 sourceAddr = ComboAddress(source.substr(0, pos));
361 sourceItf = itfIdx;
362 }
363 }
364 else
365 {
366 warnlog("Dismissing source %s because '%s' is not a valid interface name", source, itfName);
367 }
368 }
369 }
370
cbbd1533 371 std::shared_ptr<DownstreamState> ret;
372 try {
bd01350a
RG
373 ComboAddress address(boost::get<string>(vars["address"]), 53);
374 if(IsAnyAddress(address)) {
375 g_outputBuffer="Error creating new server: invalid address for a downstream server.";
376 errlog("Error creating new server: %s is not a valid address for a downstream server", boost::get<string>(vars["address"]));
377 return ret;
378 }
557d7631 379 ret=std::make_shared<DownstreamState>(address, sourceAddr, sourceItf);
cbbd1533 380 }
bd01350a
RG
381 catch(const PDNSException& e) {
382 g_outputBuffer="Error creating new server: "+string(e.reason);
383 errlog("Error creating new server with address %s: %s", boost::get<string>(vars["address"]), e.reason);
384 return ret;
385 }
cbbd1533 386 catch(std::exception& e) {
387 g_outputBuffer="Error creating new server: "+string(e.what());
388 errlog("Error creating new server with address %s: %s", boost::get<string>(vars["address"]), e.what());
389 return ret;
390 }
fbe2a2e0 391
df111b53 392 if(vars.count("qps")) {
af619119
RG
393 int qpsVal=std::stoi(boost::get<string>(vars["qps"]));
394 ret->qps=QPSLimiter(qpsVal, qpsVal);
df111b53 395 }
396
886e2cf2 397 auto localPools = g_pools.getCopy();
df111b53 398 if(vars.count("pool")) {
839f3021 399 if(auto* pool = boost::get<string>(&vars["pool"]))
400 ret->pools.insert(*pool);
401 else {
402 auto* pools = boost::get<vector<pair<int, string> > >(&vars["pool"]);
403 for(auto& p : *pools)
404 ret->pools.insert(p.second);
405 }
886e2cf2
RG
406 for(const auto& poolName: ret->pools) {
407 addServerToPool(localPools, poolName, ret);
408 }
409 }
410 else {
411 addServerToPool(localPools, "", ret);
df111b53 412 }
886e2cf2 413 g_pools.setState(localPools);
df111b53 414
415 if(vars.count("order")) {
335da0ba 416 ret->order=std::stoi(boost::get<string>(vars["order"]));
df111b53 417 }
418
419 if(vars.count("weight")) {
335da0ba 420 ret->weight=std::stoi(boost::get<string>(vars["weight"]));
df111b53 421 }
422
3f6d07a4 423 if(vars.count("retries")) {
335da0ba 424 ret->retries=std::stoi(boost::get<string>(vars["retries"]));
3f6d07a4
RG
425 }
426
b40cffe7
RG
427 if(vars.count("tcpConnectTimeout")) {
428 ret->tcpConnectTimeout=std::stoi(boost::get<string>(vars["tcpConnectTimeout"]));
429 }
430
3f6d07a4 431 if(vars.count("tcpSendTimeout")) {
335da0ba 432 ret->tcpSendTimeout=std::stoi(boost::get<string>(vars["tcpSendTimeout"]));
3f6d07a4
RG
433 }
434
435 if(vars.count("tcpRecvTimeout")) {
335da0ba 436 ret->tcpRecvTimeout=std::stoi(boost::get<string>(vars["tcpRecvTimeout"]));
3f6d07a4
RG
437 }
438
284d460c 439 if(vars.count("tcpFastOpen")) {
d987f632
RG
440 bool fastOpen = boost::get<bool>(vars["tcpFastOpen"]);
441 if (fastOpen) {
442#ifdef MSG_FASTOPEN
443 ret->tcpFastOpen=true;
444#else
445 warnlog("TCP Fast Open has been configured on downstream server %s but is not supported", boost::get<string>(vars["address"]));
446#endif
447 }
284d460c
RG
448 }
449
18eeccc9
RG
450 if(vars.count("name")) {
451 ret->name=boost::get<string>(vars["name"]);
452 }
453
ad485896
RG
454 if(vars.count("checkName")) {
455 ret->checkName=DNSName(boost::get<string>(vars["checkName"]));
456 }
457
458 if(vars.count("checkType")) {
459 ret->checkType=boost::get<string>(vars["checkType"]);
460 }
461
21830638
RG
462 if(vars.count("setCD")) {
463 ret->setCD=boost::get<bool>(vars["setCD"]);
464 }
465
a6e02424
RG
466 if(vars.count("mustResolve")) {
467 ret->mustResolve=boost::get<bool>(vars["mustResolve"]);
468 }
469
ca404e94
RG
470 if(vars.count("useClientSubnet")) {
471 ret->useECS=boost::get<bool>(vars["useClientSubnet"]);
472 }
473
9e87dcb8
RG
474 if(vars.count("maxCheckFailures")) {
475 ret->maxCheckFailures=std::stoi(boost::get<string>(vars["maxCheckFailures"]));
476 }
477
7565f4e6
RG
478 if (ret->connected) {
479 if(g_launchWork) {
480 g_launchWork->push_back([ret]() {
2717a92f 481 ret->tid = thread(responderThread, ret);
2e72cc0e 482 });
7565f4e6
RG
483 }
484 else {
2717a92f 485 ret->tid = thread(responderThread, ret);
7565f4e6 486 }
2e72cc0e 487 }
df111b53 488
ecbe9133 489 auto states = g_dstates.getCopy();
e5a14b2b 490 states.push_back(ret);
491 std::stable_sort(states.begin(), states.end(), [](const decltype(ret)& a, const decltype(ret)& b) {
df111b53 492 return a->order < b->order;
493 });
ecbe9133 494 g_dstates.setState(states);
df111b53 495 return ret;
496 } );
497
ecc8a33b 498 g_lua.writeFunction("makeRule", makeRule);
490a29bb 499
0940e4eb 500 g_lua.writeFunction("addAnyTCRule", []() {
f758857a 501 setLuaSideEffect();
0940e4eb 502 auto rules=g_rulactions.getCopy();
490a29bb
RG
503 std::vector<pair<int, shared_ptr<DNSRule> >> v;
504 v.push_back({1, std::make_shared<QTypeRule>(0xff)});
505 v.push_back({2, std::make_shared<TCPRule>(false)});
506 rules.push_back({ std::shared_ptr<DNSRule>(new AndRule(v)), std::make_shared<TCAction>()});
0940e4eb 507 g_rulactions.setState(rules);
508 });
df111b53 509
0940e4eb 510 g_lua.writeFunction("rmRule", [](unsigned int num) {
f758857a 511 setLuaSideEffect();
0940e4eb 512 auto rules = g_rulactions.getCopy();
513 if(num >= rules.size()) {
514 g_outputBuffer = "Error: attempt to delete non-existing rule\n";
515 return;
516 }
517 rules.erase(rules.begin()+num);
518 g_rulactions.setState(rules);
519 });
520
b4fd86c3 521 g_lua.writeFunction("topRule", []() {
f758857a 522 setLuaSideEffect();
b4fd86c3 523 auto rules = g_rulactions.getCopy();
524 if(rules.empty())
525 return;
526 auto subject = *rules.rbegin();
527 rules.erase(std::prev(rules.end()));
528 rules.insert(rules.begin(), subject);
529 g_rulactions.setState(rules);
530 });
0940e4eb 531 g_lua.writeFunction("mvRule", [](unsigned int from, unsigned int to) {
f758857a 532 setLuaSideEffect();
0940e4eb 533 auto rules = g_rulactions.getCopy();
534 if(from >= rules.size() || to > rules.size()) {
535 g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
536 return;
537 }
0940e4eb 538
539 auto subject = rules[from];
540 rules.erase(rules.begin()+from);
541 if(to == rules.size())
542 rules.push_back(subject);
543 else {
544 if(from < to)
545 --to;
546 rules.insert(rules.begin()+to, subject);
547 }
548 g_rulactions.setState(rules);
549 });
50ce537a
RG
550 g_lua.writeFunction("clearRules", []() {
551 setLuaSideEffect();
552 g_rulactions.modify([](decltype(g_rulactions)::value_type& rulactions) {
553 rulactions.clear();
554 });
555 });
556
557 g_lua.writeFunction("newRuleAction", [](luadnsrule_t dnsrule, std::shared_ptr<DNSAction> action) {
558 auto rule=makeRule(dnsrule);
559 return std::make_shared<std::pair< luadnsrule_t, std::shared_ptr<DNSAction> > >(rule, action);
560 });
df111b53 561
50ce537a
RG
562 g_lua.writeFunction("setRules", [](std::vector< std::pair<int, std::shared_ptr<std::pair<luadnsrule_t, std::shared_ptr<DNSAction> > > > > newruleactions) {
563 setLuaSideEffect();
564 g_rulactions.modify([newruleactions](decltype(g_rulactions)::value_type& gruleactions) {
565 gruleactions.clear();
566 for (const auto& newruleaction : newruleactions) {
567 if (newruleaction.second) {
568 auto rule=makeRule(newruleaction.second->first);
569 gruleactions.push_back({rule, newruleaction.second->second});
570 }
571 }
572 });
573 });
df111b53 574
575 g_lua.writeFunction("rmServer",
576 [](boost::variant<std::shared_ptr<DownstreamState>, int> var)
577 {
f758857a 578 setLuaSideEffect();
886e2cf2
RG
579 shared_ptr<DownstreamState> server;
580 auto* rem = boost::get<shared_ptr<DownstreamState>>(&var);
581 auto states = g_dstates.getCopy();
582 if(rem) {
583 server = *rem;
584 }
585 else {
586 int idx = boost::get<int>(var);
0f06cd4c 587 server = states.at(idx);
886e2cf2
RG
588 }
589 auto localPools = g_pools.getCopy();
590 for (const string& poolName : server->pools) {
591 removeServerFromPool(localPools, poolName, server);
592 }
0f06cd4c
RG
593 /* the server might also be in the default pool */
594 removeServerFromPool(localPools, "", server);
886e2cf2
RG
595 g_pools.setState(localPools);
596 states.erase(remove(states.begin(), states.end(), server), states.end());
597 g_dstates.setState(states);
df111b53 598 } );
599
600
601 g_lua.writeFunction("setServerPolicy", [](ServerPolicy policy) {
f758857a 602 setLuaSideEffect();
e5a14b2b 603 g_policy.setState(policy);
df111b53 604 });
70a57b05 605 g_lua.writeFunction("setServerPolicyLua", [](string name, policyfunc_t policy) {
f758857a 606 setLuaSideEffect();
e5a14b2b 607 g_policy.setState(ServerPolicy{name, policy});
df111b53 608 });
609
610 g_lua.writeFunction("showServerPolicy", []() {
f758857a 611 setLuaSideEffect();
ecbe9133 612 g_outputBuffer=g_policy.getLocal()->name+"\n";
df111b53 613 });
614
f758857a 615 g_lua.writeFunction("truncateTC", [](bool tc) { setLuaSideEffect(); g_truncateTC=tc; });
616 g_lua.writeFunction("fixupCase", [](bool fu) { setLuaSideEffect(); g_fixupCase=fu; });
df111b53 617
618 g_lua.registerMember("name", &ServerPolicy::name);
619 g_lua.registerMember("policy", &ServerPolicy::policy);
70a57b05 620 g_lua.writeFunction("newServerPolicy", [](string name, policyfunc_t policy) { return ServerPolicy{name, policy};});
df111b53 621 g_lua.writeVariable("firstAvailable", ServerPolicy{"firstAvailable", firstAvailable});
622 g_lua.writeVariable("roundrobin", ServerPolicy{"roundrobin", roundrobin});
623 g_lua.writeVariable("wrandom", ServerPolicy{"wrandom", wrandom});
a7f3108c 624 g_lua.writeVariable("whashed", ServerPolicy{"whashed", whashed});
df111b53 625 g_lua.writeVariable("leastOutstanding", ServerPolicy{"leastOutstanding", leastOutstanding});
626 g_lua.writeFunction("addACL", [](const std::string& domain) {
f758857a 627 setLuaSideEffect();
e5a14b2b 628 g_ACL.modify([domain](NetmaskGroup& nmg) { nmg.addMask(domain); });
df111b53 629 });
2e72cc0e 630
efd35aa8 631 g_lua.writeFunction("setLocal", [client](const std::string& addr, boost::optional<localbind_t> vars) {
f758857a 632 setLuaSideEffect();
5949b95b 633 if(client)
634 return;
85e4ce52
RG
635 if (g_configurationDone) {
636 g_outputBuffer="setLocal cannot be used at runtime!\n";
637 return;
638 }
efd35aa8
RG
639 bool doTCP = true;
640 bool reusePort = false;
641 int tcpFastOpenQueueSize = 0;
642 std::string interface;
643
644 parseLocalBindVars(vars, doTCP, reusePort, tcpFastOpenQueueSize, interface);
645
5949b95b 646 try {
647 ComboAddress loc(addr, 53);
648 g_locals.clear();
efd35aa8 649 g_locals.push_back(std::make_tuple(loc, doTCP, reusePort, tcpFastOpenQueueSize, interface)); /// only works pre-startup, so no sync necessary
5949b95b 650 }
651 catch(std::exception& e) {
652 g_outputBuffer="Error: "+string(e.what())+"\n";
653 }
654 });
655
efd35aa8 656 g_lua.writeFunction("addLocal", [client](const std::string& addr, boost::optional<localbind_t> vars) {
f758857a 657 setLuaSideEffect();
2e72cc0e 658 if(client)
659 return;
85e4ce52
RG
660 if (g_configurationDone) {
661 g_outputBuffer="addLocal cannot be used at runtime!\n";
662 return;
663 }
efd35aa8
RG
664 bool doTCP = true;
665 bool reusePort = false;
666 int tcpFastOpenQueueSize = 0;
667 std::string interface;
668
669 parseLocalBindVars(vars, doTCP, reusePort, tcpFastOpenQueueSize, interface);
670
2e72cc0e 671 try {
672 ComboAddress loc(addr, 53);
efd35aa8 673 g_locals.push_back(std::make_tuple(loc, doTCP, reusePort, tcpFastOpenQueueSize, interface)); /// only works pre-startup, so no sync necessary
2e72cc0e 674 }
675 catch(std::exception& e) {
676 g_outputBuffer="Error: "+string(e.what())+"\n";
677 }
678 });
e4944ea0 679 g_lua.writeFunction("setACL", [](boost::variant<string,vector<pair<int, string>>> inp) {
f758857a 680 setLuaSideEffect();
e5a14b2b 681 NetmaskGroup nmg;
e4944ea0 682 if(auto str = boost::get<string>(&inp)) {
683 nmg.addMask(*str);
684 }
685 else for(const auto& p : boost::get<vector<pair<int,string>>>(inp)) {
e5a14b2b 686 nmg.addMask(p.second);
cffde2fd 687 }
688 g_ACL.setState(nmg);
df111b53 689 });
690 g_lua.writeFunction("showACL", []() {
f758857a 691 setLuaNoSideEffect();
df111b53 692 vector<string> vec;
cffde2fd 693
e5a14b2b 694 g_ACL.getCopy().toStringVector(&vec);
cffde2fd 695
df111b53 696 for(const auto& s : vec)
cffde2fd 697 g_outputBuffer+=s+"\n";
698
df111b53 699 });
6ab65223
PL
700 g_lua.writeFunction("shutdown", []() {
701#ifdef HAVE_SYSTEMD
702 sd_notify(0, "STOPPING=1");
703#endif
704 _exit(0);
705 } );
df111b53 706
707
ecbe9133 708 g_lua.writeFunction("addDomainBlock", [](const std::string& domain) {
f758857a 709 setLuaSideEffect();
0940e4eb 710 SuffixMatchNode smn;
1f9100a7 711 smn.add(DNSName(domain));
0940e4eb 712 g_rulactions.modify([smn](decltype(g_rulactions)::value_type& rulactions) {
713 rulactions.push_back({
714 std::make_shared<SuffixMatchNodeRule>(smn),
715 std::make_shared<DropAction>() });
716 });
717
ecbe9133 718 });
df111b53 719 g_lua.writeFunction("showServers", []() {
f758857a 720 setLuaNoSideEffect();
df111b53 721 try {
722 ostringstream ret;
1287674c
RG
723 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%" );
724 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14
725 ret << (fmt % "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools") << endl;
df111b53 726
727 uint64_t totQPS{0}, totQueries{0}, totDrops{0};
728 int counter=0;
ecbe9133 729 auto states = g_dstates.getCopy();
e5a14b2b 730 for(const auto& s : states) {
9f4eb5cc 731 string status = s->getStatus();
df111b53 732 string pools;
733 for(auto& p : s->pools) {
734 if(!pools.empty())
735 pools+=" ";
736 pools+=p;
737 }
738
18eeccc9 739 ret << (fmt % counter % s->name % s->remote.toStringWithPort() %
df111b53 740 status %
1287674c 741 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 742
743 totQPS += s->queryLoad;
744 totQueries += s->queries.load();
745 totDrops += s->reuseds.load();
746 ++counter;
747 }
18eeccc9 748 ret<< (fmt % "All" % "" % "" % ""
df111b53 749 %
1287674c 750 (double)totQPS % "" % "" % "" % totQueries % totDrops % "" % "" % "" % "" ) << endl;
df111b53 751
752 g_outputBuffer=ret.str();
753 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
754 });
755
6bba426c 756 g_lua.writeFunction("addLuaAction", [](luadnsrule_t var, LuaAction::func_t func)
d8d85a30 757 {
f758857a 758 setLuaSideEffect();
d8d85a30 759 auto rule=makeRule(var);
760 g_rulactions.modify([rule,func](decltype(g_rulactions)::value_type& rulactions){
761 rulactions.push_back({rule,
762 std::make_shared<LuaAction>(func)});
763 });
764 });
df111b53 765
153d5065
RG
766 g_lua.writeFunction("addLuaResponseAction", [](luadnsrule_t var, LuaResponseAction::func_t func) {
767 setLuaSideEffect();
768 auto rule=makeRule(var);
769 g_resprulactions.modify([rule,func](decltype(g_resprulactions)::value_type& rulactions){
770 rulactions.push_back({rule,
771 std::make_shared<LuaResponseAction>(func)});
772 });
773 });
d8d85a30 774
6bba426c 775 g_lua.writeFunction("NoRecurseAction", []() {
776 return std::shared_ptr<DNSAction>(new NoRecurseAction);
777 });
778
6907f014 779 g_lua.writeFunction("MacAddrAction", [](int code) {
780 return std::shared_ptr<DNSAction>(new MacAddrAction(code));
781 });
782
783
e27097e4 784 g_lua.writeFunction("PoolAction", [](const string& a) {
785 return std::shared_ptr<DNSAction>(new PoolAction(a));
786 });
787
b1bec9f0
RG
788 g_lua.writeFunction("QPSPoolAction", [](int limit, const string& a) {
789 return std::shared_ptr<DNSAction>(new QPSPoolAction(limit, a));
790 });
791
9ebc0e91 792 g_lua.writeFunction("SpoofAction", [](boost::variant<string,vector<pair<int, string>>> inp, boost::optional<string> b ) {
793 vector<ComboAddress> addrs;
794 if(auto s = boost::get<string>(&inp))
795 addrs.push_back(ComboAddress(*s));
796 else {
797 const auto& v = boost::get<vector<pair<int,string>>>(inp);
798 for(const auto& a: v)
799 addrs.push_back(ComboAddress(a.second));
800 }
801 if(b)
802 addrs.push_back(ComboAddress(*b));
803 return std::shared_ptr<DNSAction>(new SpoofAction(addrs));
731774a8 804 });
805
87c605c4
RG
806 g_lua.writeFunction("SpoofCNAMEAction", [](const string& a) {
807 return std::shared_ptr<DNSAction>(new SpoofAction(a));
808 });
809
9ebc0e91 810 g_lua.writeFunction("addDomainSpoof", [](const std::string& domain, boost::variant<string,vector<pair<int, string>>> inp, boost::optional<string> b) {
f758857a 811 setLuaSideEffect();
731774a8 812 SuffixMatchNode smn;
9ebc0e91 813 vector<ComboAddress> outp;
731774a8 814 try
815 {
816 smn.add(DNSName(domain));
9ebc0e91 817
818 if(auto s = boost::get<string>(&inp))
819 outp.push_back(ComboAddress(*s));
820 else {
821 const auto& v = boost::get<vector<pair<int,string>>>(inp);
822 for(const auto& a: v)
823 outp.push_back(ComboAddress(a.second));
824 }
825 if(b)
826 outp.push_back(ComboAddress(*b));
827
731774a8 828 }
829 catch(std::exception& e) {
830 g_outputBuffer="Error parsing parameters: "+string(e.what());
831 return;
832 }
9ebc0e91 833 g_rulactions.modify([&smn,&outp](decltype(g_rulactions)::value_type& rulactions) {
731774a8 834 rulactions.push_back({
835 std::make_shared<SuffixMatchNodeRule>(smn),
9ebc0e91 836 std::make_shared<SpoofAction>(outp) });
731774a8 837 });
838
839 });
840
87c605c4
RG
841 g_lua.writeFunction("addDomainCNAMESpoof", [](const std::string& domain, const std::string& cname) {
842 setLuaSideEffect();
843 SuffixMatchNode smn;
844 try
845 {
846 smn.add(DNSName(domain));
847 }
848 catch(std::exception& e) {
849 g_outputBuffer="Error parsing parameters: "+string(e.what());
850 return;
851 }
852 g_rulactions.modify([&smn,&cname](decltype(g_rulactions)::value_type& rulactions) {
853 rulactions.push_back({
854 std::make_shared<SuffixMatchNodeRule>(smn),
855 std::make_shared<SpoofAction>(cname) });
856 });
857 });
731774a8 858
ae3dfa48 859 g_lua.writeFunction("DropAction", []() {
860 return std::shared_ptr<DNSAction>(new DropAction);
861 });
862
63beb26d
G
863 g_lua.writeFunction("AllowAction", []() {
864 return std::shared_ptr<DNSAction>(new AllowAction);
865 });
866
6eecd4c2 867 g_lua.writeFunction("DelayAction", [](int msec) {
868 return std::shared_ptr<DNSAction>(new DelayAction(msec));
869 });
870
2332b03c 871 g_lua.writeFunction("TCAction", []() {
872 return std::shared_ptr<DNSAction>(new TCAction);
873 });
874
f39b7598
RG
875 g_lua.writeFunction("DisableValidationAction", []() {
876 return std::shared_ptr<DNSAction>(new DisableValidationAction);
877 });
878
456fc645 879 g_lua.writeFunction("LogAction", [](const std::string& fname, boost::optional<bool> binary, boost::optional<bool> append, boost::optional<bool> buffered) {
880 return std::shared_ptr<DNSAction>(new LogAction(fname, binary ? *binary : true, append ? *append : false, buffered ? *buffered : false));
808c5ef7 881 });
882
0bd31ccc
RG
883 g_lua.writeFunction("RCodeAction", [](int rcode) {
884 return std::shared_ptr<DNSAction>(new RCodeAction(rcode));
885 });
ae3dfa48 886
886e2cf2
RG
887 g_lua.writeFunction("SkipCacheAction", []() {
888 return std::shared_ptr<DNSAction>(new SkipCacheAction);
889 });
890
1b726acf 891 g_lua.writeFunction("MaxQPSIPRule", [](unsigned int qps, boost::optional<int> ipv4trunc, boost::optional<int> ipv6trunc) {
892 return std::shared_ptr<DNSRule>(new MaxQPSIPRule(qps, ipv4trunc.get_value_or(32), ipv6trunc.get_value_or(64)));
6bba426c 893 });
894
895
2332b03c 896 g_lua.writeFunction("MaxQPSRule", [](unsigned int qps, boost::optional<int> burst) {
897 if(!burst)
898 return std::shared_ptr<DNSRule>(new MaxQPSRule(qps));
899 else
900 return std::shared_ptr<DNSRule>(new MaxQPSRule(qps, *burst));
901 });
902
903
6eecd4c2 904 g_lua.writeFunction("RegexRule", [](const std::string& str) {
905 return std::shared_ptr<DNSRule>(new RegexRule(str));
906 });
907
4ed8dfeb 908#ifdef HAVE_RE2
909 g_lua.writeFunction("RE2Rule", [](const std::string& str) {
910 return std::shared_ptr<DNSRule>(new RE2Rule(str));
911 });
912#endif
913
291729f3 914 g_lua.writeFunction("SuffixMatchNodeRule", [](const SuffixMatchNode& smn, boost::optional<bool> quiet) {
915 return std::shared_ptr<DNSRule>(new SuffixMatchNodeRule(smn, quiet ? *quiet : false));
0bd31ccc 916 });
b7860997 917
b6dcb2f7
RS
918 g_lua.writeFunction("NetmaskGroupRule", [](const NetmaskGroup& nmg, boost::optional<bool> src) {
919 return std::shared_ptr<DNSRule>(new NetmaskGroupRule(nmg, src ? *src : true));
36da3ecd
RG
920 });
921
b7860997 922 g_lua.writeFunction("benchRule", [](std::shared_ptr<DNSRule> rule, boost::optional<int> times_, boost::optional<string> suffix_) {
f758857a 923 setLuaNoSideEffect();
b7860997 924 int times = times_.get_value_or(100000);
925 DNSName suffix(suffix_.get_value_or("powerdns.com"));
926 struct item {
927 vector<uint8_t> packet;
928 ComboAddress rem;
929 DNSName qname;
3b069df2 930 uint16_t qtype, qclass;
b7860997 931 };
932 vector<item> items;
933 items.reserve(1000);
934 for(int n=0; n < 1000; ++n) {
935 struct item i;
936 i.qname=DNSName(std::to_string(random()));
937 i.qname += suffix;
938 i.qtype = random() % 0xff;
3b069df2 939 i.qclass = 1;
b7860997 940 i.rem=ComboAddress("127.0.0.1");
941 i.rem.sin4.sin_addr.s_addr = random();
942 DNSPacketWriter pw(i.packet, i.qname, i.qtype);
943 items.push_back(i);
944 }
945
946 int matches=0;
497a6e3a 947 ComboAddress dummy("127.0.0.1");
b7860997 948 DTime dt;
949 dt.set();
950 for(int n=0; n < times; ++n) {
951 const item& i = items[n % items.size()];
3b069df2 952 DNSQuestion dq(&i.qname, i.qtype, i.qclass, &i.rem, &i.rem, (struct dnsheader*)&i.packet[0], i.packet.size(), i.packet.size(), false);
497a6e3a 953 if(rule->matches(&dq))
b7860997 954 matches++;
955 }
956 double udiff=dt.udiff();
957 g_outputBuffer=(boost::format("Had %d matches out of %d, %.1f qps, in %.1f usec\n") % matches % times % (1000000*(1.0*times/udiff)) % udiff).str();
958
959 });
89cb6f9a 960
961 g_lua.writeFunction("AllRule", []() {
962 return std::shared_ptr<DNSRule>(new AllRule());
963 });
964
0f697c45 965 g_lua.writeFunction("QNameRule", [](const std::string& qname) {
966 return std::shared_ptr<DNSRule>(new QNameRule(DNSName(qname)));
967 });
968
b7860997 969 g_lua.writeFunction("QTypeRule", [](boost::variant<int, std::string> str) {
970 uint16_t qtype;
971 if(auto dir = boost::get<int>(&str)) {
972 qtype = *dir;
973 }
974 else {
975 string val=boost::get<string>(str);
976 qtype = QType::chartocode(val.c_str());
977 if(!qtype)
978 throw std::runtime_error("Unable to convert '"+val+"' to a DNS type");
979 }
980 return std::shared_ptr<DNSRule>(new QTypeRule(qtype));
981 });
55baa1f2 982 g_lua.writeFunction("QClassRule", [](int c) {
3b069df2 983 return std::shared_ptr<DNSRule>(new QClassRule(c));
984 });
985
55baa1f2
RG
986 g_lua.writeFunction("OpcodeRule", [](uint8_t code) {
987 return std::shared_ptr<DNSRule>(new OpcodeRule(code));
988 });
b7860997 989
990 g_lua.writeFunction("AndRule", [](vector<pair<int, std::shared_ptr<DNSRule> > >a) {
991 return std::shared_ptr<DNSRule>(new AndRule(a));
992 });
993
e7a1029c
RG
994 g_lua.writeFunction("OrRule", [](vector<pair<int, std::shared_ptr<DNSRule> > >a) {
995 return std::shared_ptr<DNSRule>(new OrRule(a));
996 });
997
490a29bb
RG
998 g_lua.writeFunction("TCPRule", [](bool tcp) {
999 return std::shared_ptr<DNSRule>(new TCPRule(tcp));
1000 });
b7860997 1001
b1bec9f0
RG
1002 g_lua.writeFunction("DNSSECRule", []() {
1003 return std::shared_ptr<DNSRule>(new DNSSECRule());
1004 });
1005
e7a1029c
RG
1006 g_lua.writeFunction("NotRule", [](std::shared_ptr<DNSRule>rule) {
1007 return std::shared_ptr<DNSRule>(new NotRule(rule));
1008 });
1009
55baa1f2
RG
1010 g_lua.writeFunction("RecordsCountRule", [](uint8_t section, uint16_t minCount, uint16_t maxCount) {
1011 return std::shared_ptr<DNSRule>(new RecordsCountRule(section, minCount, maxCount));
1012 });
1013
1014 g_lua.writeFunction("RecordsTypeCountRule", [](uint8_t section, uint16_t type, uint16_t minCount, uint16_t maxCount) {
1015 return std::shared_ptr<DNSRule>(new RecordsTypeCountRule(section, type, minCount, maxCount));
1016 });
1017
1018 g_lua.writeFunction("TrailingDataRule", []() {
1019 return std::shared_ptr<DNSRule>(new TrailingDataRule());
1020 });
1021
57c61ce9
RG
1022 g_lua.writeFunction("QNameLabelsCountRule", [](unsigned int minLabelsCount, unsigned int maxLabelsCount) {
1023 return std::shared_ptr<DNSRule>(new QNameLabelsCountRule(minLabelsCount, maxLabelsCount));
1024 });
1025
1026 g_lua.writeFunction("QNameWireLengthRule", [](size_t min, size_t max) {
1027 return std::shared_ptr<DNSRule>(new QNameWireLengthRule(min, max));
1028 });
1029
788c3243
RG
1030 g_lua.writeFunction("RCodeRule", [](int rcode) {
1031 return std::shared_ptr<DNSRule>(new RCodeRule(rcode));
1032 });
1033
f186603d 1034 g_lua.writeFunction("addAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era)
6bba426c 1035 {
f186603d
RG
1036 if (era.type() == typeid(std::shared_ptr<DNSResponseAction>)) {
1037 throw std::runtime_error("addAction() can only be called with query-related actions, not response-related ones. Are you looking for addResponseAction()?");
1038 }
1039
1040 auto ea = *boost::get<std::shared_ptr<DNSAction>>(&era);
f758857a 1041 setLuaSideEffect();
6bba426c 1042 auto rule=makeRule(var);
1043 g_rulactions.modify([rule, ea](decltype(g_rulactions)::value_type& rulactions){
1044 rulactions.push_back({rule, ea});
1045 });
1046 });
1047
1048
1049 g_lua.writeFunction("addPoolRule", [](luadnsrule_t var, string pool) {
f758857a 1050 setLuaSideEffect();
d8d85a30 1051 auto rule=makeRule(var);
1052 g_rulactions.modify([rule, pool](decltype(g_rulactions)::value_type& rulactions) {
0940e4eb 1053 rulactions.push_back({
d8d85a30 1054 rule,
1055 std::make_shared<PoolAction>(pool) });
ecbe9133 1056 });
df111b53 1057 });
0570f37c 1058
6bba426c 1059 g_lua.writeFunction("addNoRecurseRule", [](luadnsrule_t var) {
f758857a 1060 setLuaSideEffect();
0570f37c 1061 auto rule=makeRule(var);
1062 g_rulactions.modify([rule](decltype(g_rulactions)::value_type& rulactions) {
1063 rulactions.push_back({
1064 rule,
1065 std::make_shared<NoRecurseAction>() });
1066 });
1067 });
1068
f39b7598 1069 g_lua.writeFunction("addDisableValidationRule", [](luadnsrule_t var) {
f758857a 1070 setLuaSideEffect();
f39b7598
RG
1071 auto rule=makeRule(var);
1072 g_rulactions.modify([rule](decltype(g_rulactions)::value_type& rulactions) {
1073 rulactions.push_back({
1074 rule,
1075 std::make_shared<DisableValidationAction>() });
1076 });
1077 });
1078
0570f37c 1079
6bba426c 1080 g_lua.writeFunction("addQPSPoolRule", [](luadnsrule_t var, int limit, string pool) {
f758857a 1081 setLuaSideEffect();
d8d85a30 1082 auto rule = makeRule(var);
1083 g_rulactions.modify([rule, pool,limit](decltype(g_rulactions)::value_type& rulactions) {
1084 rulactions.push_back({
1085 rule,
1086 std::make_shared<QPSPoolAction>(limit, pool) });
1087 });
fd010ca3 1088 });
df111b53 1089
520eb5a0 1090 g_lua.writeFunction("setDNSSECPool", [](const std::string& pool) {
f758857a 1091 setLuaSideEffect();
520eb5a0 1092 g_rulactions.modify([pool](decltype(g_rulactions)::value_type& rulactions) {
1093 rulactions.push_back({std::make_shared<DNSSECRule>(),
1094 std::make_shared<PoolAction>(pool)});
1095 });
1096 });
df111b53 1097
6bba426c 1098 g_lua.writeFunction("addQPSLimit", [](luadnsrule_t var, int lim) {
f758857a 1099 setLuaSideEffect();
d8d85a30 1100 auto rule = makeRule(var);
1101 g_rulactions.modify([lim,rule](decltype(g_rulactions)::value_type& rulactions) {
1102 rulactions.push_back({rule,
1103 std::make_shared<QPSAction>(lim)});
1104 });
df111b53 1105 });
d8d85a30 1106
6bba426c 1107 g_lua.writeFunction("addDelay", [](luadnsrule_t var, int msec) {
f758857a 1108 setLuaSideEffect();
7b3865cd 1109 auto rule = makeRule(var);
1110 g_rulactions.modify([msec,rule](decltype(g_rulactions)::value_type& rulactions) {
1111 rulactions.push_back({rule,
1112 std::make_shared<DelayAction>(msec)});
1113 });
1114 });
df111b53 1115
df111b53 1116
0940e4eb 1117 g_lua.writeFunction("showRules", []() {
f758857a 1118 setLuaNoSideEffect();
0940e4eb 1119 boost::format fmt("%-3d %9d %-50s %s\n");
1120 g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
1121 int num=0;
1122 for(const auto& lim : g_rulactions.getCopy()) {
1123 string name = lim.first->toString();
1124 g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
df111b53 1125 ++num;
1126 }
1127 });
1128
df111b53 1129 g_lua.writeFunction("getServers", []() {
f758857a 1130 setLuaNoSideEffect();
df111b53 1131 vector<pair<int, std::shared_ptr<DownstreamState> > > ret;
1132 int count=1;
e5a14b2b 1133 for(const auto& s : g_dstates.getCopy()) {
df111b53 1134 ret.push_back(make_pair(count++, s));
1135 }
1136 return ret;
1137 });
1138
da4e7813 1139 g_lua.writeFunction("getPoolServers", [](string pool) {
886e2cf2 1140 return getDownstreamCandidates(g_pools.getCopy(), pool);
da4e7813 1141 });
1142
8780ba53
RG
1143 g_lua.writeFunction("getServer", [client](int i) {
1144 if (client)
1145 return std::make_shared<DownstreamState>(ComboAddress());
1146 return g_dstates.getCopy().at(i);
1147 });
df111b53 1148
df111b53 1149 g_lua.registerFunction<void(DownstreamState::*)(int)>("setQPS", [](DownstreamState& s, int lim) { s.qps = lim ? QPSLimiter(lim, lim) : QPSLimiter(); });
886e2cf2
RG
1150 g_lua.registerFunction<void(std::shared_ptr<DownstreamState>::*)(string)>("addPool", [](std::shared_ptr<DownstreamState> s, string pool) {
1151 auto localPools = g_pools.getCopy();
1152 addServerToPool(localPools, pool, s);
1153 g_pools.setState(localPools);
1154 s->pools.insert(pool);
1155 });
1156 g_lua.registerFunction<void(std::shared_ptr<DownstreamState>::*)(string)>("rmPool", [](std::shared_ptr<DownstreamState> s, string pool) {
1157 auto localPools = g_pools.getCopy();
1158 removeServerFromPool(localPools, pool, s);
1159 g_pools.setState(localPools);
1160 s->pools.erase(pool);
1161 });
df111b53 1162
1163 g_lua.registerFunction<void(DownstreamState::*)()>("getOutstanding", [](const DownstreamState& s) { g_outputBuffer=std::to_string(s.outstanding.load()); });
1164
1165
1166 g_lua.registerFunction("isUp", &DownstreamState::isUp);
1167 g_lua.registerFunction("setDown", &DownstreamState::setDown);
1168 g_lua.registerFunction("setUp", &DownstreamState::setUp);
1169 g_lua.registerFunction("setAuto", &DownstreamState::setAuto);
46a839bf
RG
1170 g_lua.registerFunction("getName", &DownstreamState::getName);
1171 g_lua.registerFunction("getNameWithAddr", &DownstreamState::getNameWithAddr);
b0976f44 1172 g_lua.registerMember("upStatus", &DownstreamState::upStatus);
df111b53 1173 g_lua.registerMember("weight", &DownstreamState::weight);
1174 g_lua.registerMember("order", &DownstreamState::order);
46a839bf 1175 g_lua.registerMember("name", &DownstreamState::name);
df111b53 1176
4c6f4321 1177 g_lua.writeFunction("infolog", [](const string& arg) {
1178 infolog("%s", arg);
1179 });
1180 g_lua.writeFunction("errlog", [](const string& arg) {
1181 errlog("%s", arg);
1182 });
1183 g_lua.writeFunction("warnlog", [](const string& arg) {
1184 warnlog("%s", arg);
1185 });
1186
1187
df111b53 1188 g_lua.writeFunction("show", [](const string& arg) {
1189 g_outputBuffer+=arg;
1190 g_outputBuffer+="\n";
1191 });
1192
1193 g_lua.registerFunction<void(dnsheader::*)(bool)>("setRD", [](dnsheader& dh, bool v) {
1194 dh.rd=v;
1195 });
1196
1197 g_lua.registerFunction<bool(dnsheader::*)()>("getRD", [](dnsheader& dh) {
1198 return (bool)dh.rd;
1199 });
1200
aeb36780
RG
1201 g_lua.registerFunction<void(dnsheader::*)(bool)>("setCD", [](dnsheader& dh, bool v) {
1202 dh.cd=v;
1203 });
1204
1205 g_lua.registerFunction<bool(dnsheader::*)()>("getCD", [](dnsheader& dh) {
1206 return (bool)dh.cd;
1207 });
1208
df111b53 1209
1210 g_lua.registerFunction<void(dnsheader::*)(bool)>("setTC", [](dnsheader& dh, bool v) {
1211 dh.tc=v;
bde3ab96 1212 if(v) dh.ra = dh.rd; // you'll always need this, otherwise TC=1 gets ignored
df111b53 1213 });
1214
1215 g_lua.registerFunction<void(dnsheader::*)(bool)>("setQR", [](dnsheader& dh, bool v) {
1216 dh.qr=v;
1217 });
1218
b769dbb7
RG
1219 g_lua.registerFunction<string(std::shared_ptr<DNSRule>::*)()>("toString", [](const std::shared_ptr<DNSRule>& rule) { return rule->toString(); });
1220
d6a9a395
RG
1221 g_lua.registerFunction<string(ComboAddress::*)()>("tostring", [](const ComboAddress& ca) { return ca.toString(); });
1222 g_lua.registerFunction<string(ComboAddress::*)()>("tostringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); });
1223 g_lua.registerFunction<string(ComboAddress::*)()>("toString", [](const ComboAddress& ca) { return ca.toString(); });
1224 g_lua.registerFunction<string(ComboAddress::*)()>("toStringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); });
0a112365 1225 g_lua.registerFunction<uint16_t(ComboAddress::*)()>("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); } );
d6a9a395
RG
1226 g_lua.registerFunction<void(ComboAddress::*)(unsigned int)>("truncate", [](ComboAddress& ca, unsigned int bits) { ca.truncate(bits); });
1227 g_lua.registerFunction<bool(ComboAddress::*)()>("isIPv4", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET; });
1228 g_lua.registerFunction<bool(ComboAddress::*)()>("isIPv6", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET6; });
1229 g_lua.registerFunction<bool(ComboAddress::*)()>("isMappedIPv4", [](const ComboAddress& ca) { return ca.isMappedIPv4(); });
1230 g_lua.registerFunction<ComboAddress(ComboAddress::*)()>("mapToIPv4", [](const ComboAddress& ca) { return ca.mapToIPv4(); });
a94673ea 1231
df111b53 1232 g_lua.registerFunction("isPartOf", &DNSName::isPartOf);
2b18ac9f 1233 g_lua.registerFunction<bool(DNSName::*)()>("chopOff", [](DNSName&dn ) { return dn.chopOff(); });
d6a9a395
RG
1234 g_lua.registerFunction<unsigned int(DNSName::*)()>("countLabels", [](const DNSName& name) { return name.countLabels(); });
1235 g_lua.registerFunction<size_t(DNSName::*)()>("wirelength", [](const DNSName& name) { return name.wirelength(); });
b5425a3d 1236 g_lua.registerFunction<string(DNSName::*)()>("tostring", [](const DNSName&dn ) { return dn.toString(); });
497a6e3a 1237 g_lua.registerFunction<string(DNSName::*)()>("toString", [](const DNSName&dn ) { return dn.toString(); });
df111b53 1238 g_lua.writeFunction("newDNSName", [](const std::string& name) { return DNSName(name); });
b0976f44 1239 g_lua.writeFunction("newSuffixMatchNode", []() { return SuffixMatchNode(); });
df111b53 1240
1241 g_lua.registerFunction("add",(void (SuffixMatchNode::*)(const DNSName&)) &SuffixMatchNode::add);
1242 g_lua.registerFunction("check",(bool (SuffixMatchNode::*)(const DNSName&) const) &SuffixMatchNode::check);
1243
42fae326 1244 g_lua.writeFunction("carbonServer", [](const std::string& address, boost::optional<string> ourName,
d617b22c 1245 boost::optional<unsigned int> interval) {
f758857a 1246 setLuaSideEffect();
42fae326 1247 auto ours = g_carbon.getCopy();
d617b22c 1248 ours.push_back({ComboAddress(address, 2003), ourName ? *ourName : "", interval ? *interval : 30});
42fae326 1249 g_carbon.setState(ours);
1250 });
1251
002decab 1252 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 1253 setLuaSideEffect();
50bed881 1254 if(client)
1255 return;
1256 ComboAddress local(address);
1257 try {
8d06661a 1258 int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
50bed881 1259 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
1260 SBind(sock, local);
1261 SListen(sock, 5);
002decab
RG
1262 auto launch=[sock, local, password, apiKey, customHeaders]() {
1263 thread t(dnsdistWebserverThread, sock, local, password, apiKey ? *apiKey : "", customHeaders);
50bed881 1264 t.detach();
1265 };
1266 if(g_launchWork)
1267 g_launchWork->push_back(launch);
1268 else
1269 launch();
1270 }
1271 catch(std::exception& e) {
0c8e7c58 1272 g_outputBuffer="Unable to bind to webserver socket on " + local.toStringWithPort() + ": " + e.what();
50bed881 1273 errlog("Unable to bind to webserver socket on %s: %s", local.toStringWithPort(), e.what());
1274 }
1275
1276 });
df111b53 1277 g_lua.writeFunction("controlSocket", [client](const std::string& str) {
f758857a 1278 setLuaSideEffect();
df111b53 1279 ComboAddress local(str, 5199);
1280
1281 if(client) {
1282 g_serverControl = local;
1283 return;
1284 }
1285
1286 try {
8d06661a 1287 int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
df111b53 1288 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
1289 SBind(sock, local);
1290 SListen(sock, 5);
2e72cc0e 1291 auto launch=[sock, local]() {
1292 thread t(controlThread, sock, local);
1293 t.detach();
1294 };
1295 if(g_launchWork)
1296 g_launchWork->push_back(launch);
1297 else
1298 launch();
1299
df111b53 1300 }
1301 catch(std::exception& e) {
0c8e7c58 1302 g_outputBuffer="Unable to bind to control socket on " + local.toStringWithPort() + ": " + e.what();
df111b53 1303 errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what());
1304 }
1305 });
1306
03ebf8b2 1307
d179bc47 1308 g_lua.writeFunction("topClients", [](boost::optional<unsigned int> top_) {
f758857a 1309 setLuaNoSideEffect();
d179bc47 1310 auto top = top_.get_value_or(10);
0e5b3cff 1311 map<ComboAddress, int,ComboAddress::addressOnlyLessThan > counts;
1312 unsigned int total=0;
0e41337b
RG
1313 {
1314 ReadLock rl(&g_rings.queryLock);
1315 for(const auto& c : g_rings.queryRing) {
1316 counts[c.requestor]++;
1317 total++;
1318 }
0e5b3cff 1319 }
1320 vector<pair<int, ComboAddress>> rcounts;
a73fc812 1321 rcounts.reserve(counts.size());
0e5b3cff 1322 for(const auto& c : counts)
1323 rcounts.push_back(make_pair(c.second, c.first));
1324
1325 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
1326 const decltype(rcounts)::value_type& b) {
1327 return b.first < a.first;
1328 });
1329 unsigned int count=1, rest=0;
1330 boost::format fmt("%4d %-40s %4d %4.1f%%\n");
1331 for(const auto& rc : rcounts) {
1332 if(count==top+1)
1333 rest+=rc.first;
1334 else
1335 g_outputBuffer += (fmt % (count++) % rc.second.toString() % rc.first % (100.0*rc.first/total)).str();
1336 }
6a62c0e3 1337 g_outputBuffer += (fmt % (count) % "Rest" % rest % (total > 0 ? 100.0*rest/total : 100.0)).str();
0e5b3cff 1338 });
1339
df111b53 1340 g_lua.writeFunction("getTopQueries", [](unsigned int top, boost::optional<int> labels) {
f758857a 1341 setLuaNoSideEffect();
df111b53 1342 map<DNSName, int> counts;
1343 unsigned int total=0;
1344 if(!labels) {
0e41337b 1345 ReadLock rl(&g_rings.queryLock);
df111b53 1346 for(const auto& a : g_rings.queryRing) {
0ba5eecf 1347 counts[a.name]++;
df111b53 1348 total++;
1349 }
1350 }
1351 else {
1352 unsigned int lab = *labels;
0e41337b 1353 ReadLock rl(&g_rings.queryLock);
df111b53 1354 for(auto a : g_rings.queryRing) {
0ba5eecf 1355 a.name.trimToLabels(lab);
1356 counts[a.name]++;
df111b53 1357 total++;
1358 }
df111b53 1359 }
cffde2fd 1360 // cout<<"Looked at "<<total<<" queries, "<<counts.size()<<" different ones"<<endl;
df111b53 1361 vector<pair<int, DNSName>> rcounts;
3821bfe2 1362 rcounts.reserve(counts.size());
df111b53 1363 for(const auto& c : counts)
2f95ab40 1364 rcounts.push_back(make_pair(c.second, c.first.makeLowerCase()));
df111b53 1365
1366 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
1367 const decltype(rcounts)::value_type& b) {
1368 return b.first < a.first;
1369 });
1370
1371 std::unordered_map<int, vector<boost::variant<string,double>>> ret;
1372 unsigned int count=1, rest=0;
1373 for(const auto& rc : rcounts) {
1374 if(count==top+1)
1375 rest+=rc.first;
1376 else
1377 ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
1378 }
6a62c0e3 1379 ret.insert({count, {"Rest", rest, total > 0 ? 100.0*rest/total : 100.0}});
df111b53 1380 return ret;
1381
1382 });
786e4d8c 1383
a78a3a0e 1384 g_lua.executeCode(R"(function topQueries(top, labels) top = top or 10; for k,v in ipairs(getTopQueries(top,labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2], v[3])) end end)");
df111b53 1385
786e4d8c
RS
1386 g_lua.writeFunction("clearQueryCounters", []() {
1387 unsigned int size{0};
1388 {
1389 WriteLock wl(&g_qcount.queryLock);
1390 size = g_qcount.records.size();
1391 g_qcount.records.clear();
1392 }
1393
1394 boost::format fmt("%d records cleared from query counter buffer\n");
1395 g_outputBuffer = (fmt % size).str();
1396 });
520eb5a0 1397
786e4d8c
RS
1398 g_lua.writeFunction("getQueryCounters", [](boost::optional<unsigned int> optMax) {
1399 setLuaNoSideEffect();
1400 ReadLock rl(&g_qcount.queryLock);
1401 g_outputBuffer = "query counting is currently: ";
1402 g_outputBuffer+= g_qcount.enabled ? "enabled" : "disabled";
1403 g_outputBuffer+= (boost::format(" (%d records in buffer)\n") % g_qcount.records.size()).str();
1404
1405 boost::format fmt("%-3d %s: %d request(s)\n");
1406 QueryCountRecords::iterator it;
1407 unsigned int max = optMax ? *optMax : 10;
1408 unsigned int index{1};
1409 for(it = g_qcount.records.begin(); it != g_qcount.records.end() && index <= max; ++it, ++index) {
1410 g_outputBuffer += (fmt % index % it->first % it->second).str();
1411 }
1412 });
1413
1414 g_lua.writeFunction("setQueryCount", [](bool enabled) { g_qcount.enabled=enabled; });
1415 g_lua.writeFunction("setQueryCountFilter", [](QueryCountFilter func) {
1416 g_qcount.filter = func;
1417 });
62edea30 1418
520eb5a0 1419 g_lua.writeFunction("getResponseRing", []() {
f758857a 1420 setLuaNoSideEffect();
520eb5a0 1421 decltype(g_rings.respRing) ring;
1422 {
1423 std::lock_guard<std::mutex> lock(g_rings.respMutex);
1424 ring = g_rings.respRing;
1425 }
1426 vector<std::unordered_map<string, boost::variant<string, unsigned int> > > ret;
1427 ret.reserve(ring.size());
1428 decltype(ret)::value_type item;
1429 for(const auto& r : ring) {
1430 item["name"]=r.name.toString();
1431 item["qtype"]=r.qtype;
3fcaeeac 1432 item["rcode"]=r.dh.rcode;
520eb5a0 1433 item["usec"]=r.usec;
1434 ret.push_back(item);
1435 }
1436 return ret;
1437 });
f5b58807 1438
df111b53 1439 g_lua.writeFunction("getTopResponses", [](unsigned int top, unsigned int kind, boost::optional<int> labels) {
2d11d1b2 1440 return getGenResponses(top, labels, [kind](const Rings::Response& r) { return r.dh.rcode == kind; });
1441 });
df111b53 1442
2d11d1b2 1443 g_lua.executeCode(R"(function topResponses(top, kind, labels) top = top or 10; kind = kind or 0; for k,v in ipairs(getTopResponses(top, kind, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
df111b53 1444
df111b53 1445
2d11d1b2 1446 g_lua.writeFunction("getSlowResponses", [](unsigned int top, unsigned int msec, boost::optional<int> labels) {
1447 return getGenResponses(top, labels, [msec](const Rings::Response& r) { return r.usec > msec*1000; });
df111b53 1448 });
62edea30 1449
2d11d1b2 1450
2a05b4a9 1451 g_lua.executeCode(R"(function topSlow(top, msec, labels) top = top or 10; msec = msec or 500; for k,v in ipairs(getSlowResponses(top, msec, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
df111b53 1452
1453
1454 g_lua.writeFunction("showResponseLatency", []() {
f758857a 1455 setLuaNoSideEffect();
df111b53 1456 map<double, unsigned int> histo;
1457 double bin=100;
1458 for(int i=0; i < 15; ++i) {
1459 histo[bin];
1460 bin*=2;
1461 }
1462
1463 double totlat=0;
2b2ab383 1464 unsigned int size=0;
df111b53 1465 {
1466 std::lock_guard<std::mutex> lock(g_rings.respMutex);
1467 for(const auto& r : g_rings.respRing) {
2b2ab383
RG
1468 /* skip actively discovered timeouts */
1469 if (r.usec == std::numeric_limits<unsigned int>::max())
1470 continue;
1471
df111b53 1472 ++size;
1473 auto iter = histo.lower_bound(r.usec);
1474 if(iter != histo.end())
1475 iter->second++;
1476 else
1477 histo.rbegin()++;
1478 totlat+=r.usec;
1479 }
1480 }
1481
e12b3374
RG
1482 if (size == 0) {
1483 g_outputBuffer = "No traffic yet.\n";
1484 return;
1485 }
1486
df111b53 1487 g_outputBuffer = (boost::format("Average response latency: %.02f msec\n") % (0.001*totlat/size)).str();
1488 double highest=0;
1489
1490 for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
1491 highest=std::max(highest, iter->second*1.0);
1492 }
1493 boost::format fmt("%7.2f\t%s\n");
1494 g_outputBuffer += (fmt % "msec" % "").str();
1495
1496 for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
1497 int stars = (70.0 * iter->second/highest);
1498 char c='*';
1499 if(!stars && iter->second) {
1500 stars=1; // you get 1 . to show something is there..
1501 if(70.0*iter->second/highest > 0.5)
1502 c=':';
1503 else
1504 c='.';
1505 }
1506 g_outputBuffer += (fmt % (iter->first/1000.0) % string(stars, c)).str();
1507 }
1508 });
1509
1510 g_lua.writeFunction("newQPSLimiter", [](int rate, int burst) { return QPSLimiter(rate, burst); });
1511 g_lua.registerFunction("check", &QPSLimiter::check);
1512
1513
1514 g_lua.writeFunction("makeKey", []() {
f758857a 1515 setLuaNoSideEffect();
df111b53 1516 g_outputBuffer="setKey("+newKey()+")\n";
1517 });
1518
1519 g_lua.writeFunction("setKey", [](const std::string& key) {
6f6b4d69 1520 if(!g_configurationDone && ! g_key.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
1521 return; // but later setKeys() trump the -k value again
1522 }
1523
f758857a 1524 setLuaSideEffect();
3a23cd0f 1525 string newkey;
1526 if(B64Decode(key, newkey) < 0) {
6f6b4d69 1527 g_outputBuffer=string("Unable to decode ")+key+" as Base64";
1528 errlog("%s", g_outputBuffer);
1529 }
3a23cd0f 1530 else
1531 g_key=newkey;
df111b53 1532 });
1533
1534
107d4911 1535 g_lua.writeFunction("testCrypto", [](boost::optional<string> optTestMsg)
df111b53 1536 {
f758857a 1537 setLuaNoSideEffect();
a8eafc52 1538#ifdef HAVE_LIBSODIUM
df111b53 1539 try {
107d4911
RG
1540 string testmsg;
1541
1542 if (optTestMsg) {
1543 testmsg = *optTestMsg;
1544 }
1545 else {
1546 testmsg = "testStringForCryptoTests";
1547 }
1548
df111b53 1549 SodiumNonce sn, sn2;
1550 sn.init();
1551 sn2=sn;
1552 string encrypted = sodEncryptSym(testmsg, g_key, sn);
1553 string decrypted = sodDecryptSym(encrypted, g_key, sn2);
1554
2e72cc0e 1555 sn.increment();
1556 sn2.increment();
1557
1558 encrypted = sodEncryptSym(testmsg, g_key, sn);
1559 decrypted = sodDecryptSym(encrypted, g_key, sn2);
1560
df111b53 1561 if(testmsg == decrypted)
1562 g_outputBuffer="Everything is ok!\n";
1563 else
1564 g_outputBuffer="Crypto failed..\n";
1565
1566 }
1567 catch(...) {
1568 g_outputBuffer="Crypto failed..\n";
a8eafc52
MK
1569 }
1570#else
1571 g_outputBuffer="Crypto not available.\n";
1572#endif
1573 });
df111b53 1574
3f6d07a4
RG
1575 g_lua.writeFunction("setTCPRecvTimeout", [](int timeout) { g_tcpRecvTimeout=timeout; });
1576
1577 g_lua.writeFunction("setTCPSendTimeout", [](int timeout) { g_tcpSendTimeout=timeout; });
2a817e5a 1578
e0b5e49d
RG
1579 g_lua.writeFunction("setUDPTimeout", [](int timeout) { g_udpTimeout=timeout; });
1580
e41f8165
RG
1581 g_lua.writeFunction("setMaxUDPOutstanding", [](uint16_t max) {
1582 if (!g_configurationDone) {
1583 g_maxOutstanding = max;
1584 } else {
1585 g_outputBuffer="Max UDP outstanding cannot be altered at runtime!\n";
1586 }
1587 });
1588
497a6e3a
RG
1589 /* DNSQuestion bindings */
1590 /* PowerDNS DNSQuestion compat */
1591 g_lua.registerMember<const ComboAddress (DNSQuestion::*)>("localaddr", [](const DNSQuestion& dq) -> const ComboAddress { return *dq.local; }, [](DNSQuestion& dq, const ComboAddress newLocal) { (void) newLocal; });
1592 g_lua.registerMember<const DNSName (DNSQuestion::*)>("qname", [](const DNSQuestion& dq) -> const DNSName { return *dq.qname; }, [](DNSQuestion& dq, const DNSName newName) { (void) newName; });
1593 g_lua.registerMember<uint16_t (DNSQuestion::*)>("qtype", [](const DNSQuestion& dq) -> uint16_t { return dq.qtype; }, [](DNSQuestion& dq, uint16_t newType) { (void) newType; });
3b069df2 1594 g_lua.registerMember<uint16_t (DNSQuestion::*)>("qclass", [](const DNSQuestion& dq) -> uint16_t { return dq.qclass; }, [](DNSQuestion& dq, uint16_t newClass) { (void) newClass; });
497a6e3a
RG
1595 g_lua.registerMember<int (DNSQuestion::*)>("rcode", [](const DNSQuestion& dq) -> int { return dq.dh->rcode; }, [](DNSQuestion& dq, int newRCode) { dq.dh->rcode = newRCode; });
1596 g_lua.registerMember<const ComboAddress (DNSQuestion::*)>("remoteaddr", [](const DNSQuestion& dq) -> const ComboAddress { return *dq.remote; }, [](DNSQuestion& dq, const ComboAddress newRemote) { (void) newRemote; });
1597 /* DNSDist DNSQuestion */
1598 g_lua.registerMember("dh", &DNSQuestion::dh);
1599 g_lua.registerMember<uint16_t (DNSQuestion::*)>("len", [](const DNSQuestion& dq) -> uint16_t { return dq.len; }, [](DNSQuestion& dq, uint16_t newlen) { dq.len = newlen; });
55baa1f2 1600 g_lua.registerMember<uint8_t (DNSQuestion::*)>("opcode", [](const DNSQuestion& dq) -> uint8_t { return dq.dh->opcode; }, [](DNSQuestion& dq, uint8_t newOpcode) { (void) newOpcode; });
497a6e3a
RG
1601 g_lua.registerMember<size_t (DNSQuestion::*)>("size", [](const DNSQuestion& dq) -> size_t { return dq.size; }, [](DNSQuestion& dq, size_t newSize) { (void) newSize; });
1602 g_lua.registerMember<bool (DNSQuestion::*)>("tcp", [](const DNSQuestion& dq) -> bool { return dq.tcp; }, [](DNSQuestion& dq, bool newTcp) { (void) newTcp; });
a14c7f7b 1603 g_lua.registerMember<bool (DNSQuestion::*)>("skipCache", [](const DNSQuestion& dq) -> bool { return dq.skipCache; }, [](DNSQuestion& dq, bool newSkipCache) { dq.skipCache = newSkipCache; });
ff0902ec
RG
1604 g_lua.registerMember<bool (DNSQuestion::*)>("useECS", [](const DNSQuestion& dq) -> bool { return dq.useECS; }, [](DNSQuestion& dq, bool useECS) { dq.useECS = useECS; });
1605 g_lua.registerMember<bool (DNSQuestion::*)>("ecsOverride", [](const DNSQuestion& dq) -> bool { return dq.ecsOverride; }, [](DNSQuestion& dq, bool ecsOverride) { dq.ecsOverride = ecsOverride; });
1606 g_lua.registerMember<uint16_t (DNSQuestion::*)>("ecsPrefixLength", [](const DNSQuestion& dq) -> uint16_t { return dq.ecsPrefixLength; }, [](DNSQuestion& dq, uint16_t newPrefixLength) { dq.ecsPrefixLength = newPrefixLength; });
b82f0bc2
RG
1607 g_lua.registerFunction<bool(DNSQuestion::*)()>("getDO", [](const DNSQuestion& dq) {
1608 return getEDNSZ((const char*)dq.dh, dq.len) & EDNS_HEADER_FLAG_DO;
1609 });
9f4eb5cc
RG
1610 g_lua.registerFunction<void(DNSQuestion::*)(std::string)>("sendTrap", [](const DNSQuestion& dq, boost::optional<std::string> reason) {
1611#ifdef HAVE_NET_SNMP
1612 if (g_snmpAgent && g_snmpTrapsEnabled) {
1613 g_snmpAgent->sendDNSTrap(dq, reason ? *reason : "");
1614 }
1615#endif /* HAVE_NET_SNMP */
1616 });
497a6e3a 1617
6beb5731
RG
1618 /* LuaWrapper doesn't support inheritance */
1619 g_lua.registerMember<const ComboAddress (DNSResponse::*)>("localaddr", [](const DNSResponse& dq) -> const ComboAddress { return *dq.local; }, [](DNSResponse& dq, const ComboAddress newLocal) { (void) newLocal; });
1620 g_lua.registerMember<const DNSName (DNSResponse::*)>("qname", [](const DNSResponse& dq) -> const DNSName { return *dq.qname; }, [](DNSResponse& dq, const DNSName newName) { (void) newName; });
1621 g_lua.registerMember<uint16_t (DNSResponse::*)>("qtype", [](const DNSResponse& dq) -> uint16_t { return dq.qtype; }, [](DNSResponse& dq, uint16_t newType) { (void) newType; });
1622 g_lua.registerMember<uint16_t (DNSResponse::*)>("qclass", [](const DNSResponse& dq) -> uint16_t { return dq.qclass; }, [](DNSResponse& dq, uint16_t newClass) { (void) newClass; });
1623 g_lua.registerMember<int (DNSResponse::*)>("rcode", [](const DNSResponse& dq) -> int { return dq.dh->rcode; }, [](DNSResponse& dq, int newRCode) { dq.dh->rcode = newRCode; });
1624 g_lua.registerMember<const ComboAddress (DNSResponse::*)>("remoteaddr", [](const DNSResponse& dq) -> const ComboAddress { return *dq.remote; }, [](DNSResponse& dq, const ComboAddress newRemote) { (void) newRemote; });
1625 g_lua.registerMember("dh", &DNSResponse::dh);
1626 g_lua.registerMember<uint16_t (DNSResponse::*)>("len", [](const DNSResponse& dq) -> uint16_t { return dq.len; }, [](DNSResponse& dq, uint16_t newlen) { dq.len = newlen; });
1627 g_lua.registerMember<uint8_t (DNSResponse::*)>("opcode", [](const DNSResponse& dq) -> uint8_t { return dq.dh->opcode; }, [](DNSResponse& dq, uint8_t newOpcode) { (void) newOpcode; });
1628 g_lua.registerMember<size_t (DNSResponse::*)>("size", [](const DNSResponse& dq) -> size_t { return dq.size; }, [](DNSResponse& dq, size_t newSize) { (void) newSize; });
1629 g_lua.registerMember<bool (DNSResponse::*)>("tcp", [](const DNSResponse& dq) -> bool { return dq.tcp; }, [](DNSResponse& dq, bool newTcp) { (void) newTcp; });
1630 g_lua.registerMember<bool (DNSResponse::*)>("skipCache", [](const DNSResponse& dq) -> bool { return dq.skipCache; }, [](DNSResponse& dq, bool newSkipCache) { dq.skipCache = newSkipCache; });
153d5065
RG
1631 g_lua.registerFunction<void(DNSResponse::*)(std::function<uint32_t(uint8_t section, uint16_t qclass, uint16_t qtype, uint32_t ttl)> editFunc)>("editTTLs", [](const DNSResponse& dr, std::function<uint32_t(uint8_t section, uint16_t qclass, uint16_t qtype, uint32_t ttl)> editFunc) {
1632 editDNSPacketTTL((char*) dr.dh, dr.len, editFunc);
1633 });
9f4eb5cc
RG
1634 g_lua.registerFunction<void(DNSResponse::*)(std::string)>("sendTrap", [](const DNSResponse& dr, boost::optional<std::string> reason) {
1635#ifdef HAVE_NET_SNMP
1636 if (g_snmpAgent && g_snmpTrapsEnabled) {
1637 g_snmpAgent->sendDNSTrap(dr, reason ? *reason : "");
1638 }
1639#endif /* HAVE_NET_SNMP */
1640 });
6beb5731 1641
6c1ca990
RG
1642 g_lua.writeFunction("setMaxTCPClientThreads", [](uint64_t max) {
1643 if (!g_configurationDone) {
1644 g_maxTCPClientThreads = max;
1645 } else {
1646 g_outputBuffer="Maximum TCP client threads count cannot be altered at runtime!\n";
1647 }
1648 });
1649
1650 g_lua.writeFunction("setMaxTCPQueuedConnections", [](uint64_t max) {
1651 if (!g_configurationDone) {
1652 g_maxTCPQueuedConnections = max;
1653 } else {
1654 g_outputBuffer="The maximum number of queued TCP connections cannot be altered at runtime!\n";
1655 }
1656 });
e41f8165 1657
9396d955
RG
1658 g_lua.writeFunction("setMaxTCPQueriesPerConnection", [](size_t max) {
1659 if (!g_configurationDone) {
1660 g_maxTCPQueriesPerConn = max;
1661 } else {
1662 g_outputBuffer="The maximum number of queries per TCP connection cannot be altered at runtime!\n";
1663 }
1664 });
1665
1666 g_lua.writeFunction("setMaxTCPConnectionsPerClient", [](size_t max) {
1667 if (!g_configurationDone) {
1668 g_maxTCPConnectionsPerClient = max;
1669 } else {
1670 g_outputBuffer="The maximum number of TCP connection per client cannot be altered at runtime!\n";
1671 }
1672 });
1673
1674 g_lua.writeFunction("setMaxTCPConnectionDuration", [](size_t max) {
1675 if (!g_configurationDone) {
1676 g_maxTCPConnectionDuration = max;
1677 } else {
1678 g_outputBuffer="The maximum duration of a TCP connection cannot be altered at runtime!\n";
1679 }
1680 });
1681
e65ae260
RG
1682 g_lua.writeFunction("showTCPStats", [] {
1683 setLuaNoSideEffect();
1684 boost::format fmt("%-10d %-10d %-10d %-10d\n");
1685 g_outputBuffer += (fmt % "Clients" % "MaxClients" % "Queued" % "MaxQueued").str();
ded1985a 1686 g_outputBuffer += (fmt % g_tcpclientthreads->getThreadsCount() % g_maxTCPClientThreads % g_tcpclientthreads->getQueuedCount() % g_maxTCPQueuedConnections).str();
edbda1ad 1687 g_outputBuffer += "Query distribution mode is: " + std::string(g_useTCPSinglePipe ? "single queue" : "per-thread queues") + "\n";
e65ae260
RG
1688 });
1689
886e2cf2 1690 g_lua.writeFunction("setCacheCleaningDelay", [](uint32_t delay) { g_cacheCleaningDelay = delay; });
f65ea0c2 1691 g_lua.writeFunction("setCacheCleaningPercentage", [](uint16_t percentage) { if (percentage < 100) g_cacheCleaningPercentage = percentage; else g_cacheCleaningPercentage = 100; });
886e2cf2 1692
ca404e94
RG
1693 g_lua.writeFunction("setECSSourcePrefixV4", [](uint16_t prefix) { g_ECSSourcePrefixV4=prefix; });
1694
1695 g_lua.writeFunction("setECSSourcePrefixV6", [](uint16_t prefix) { g_ECSSourcePrefixV6=prefix; });
1696
1697 g_lua.writeFunction("setECSOverride", [](bool override) { g_ECSOverride=override; });
1698
2a817e5a 1699 g_lua.writeFunction("dumpStats", [] {
f758857a 1700 setLuaNoSideEffect();
2a817e5a 1701 vector<string> leftcolumn, rightcolumn;
1702
1703 boost::format fmt("%-23s\t%+11s");
1704 g_outputBuffer.clear();
1705 auto entries = g_stats.entries;
1706 sort(entries.begin(), entries.end(),
1707 [](const decltype(entries)::value_type& a, const decltype(entries)::value_type& b) {
1708 return a.first < b.first;
1709 });
1710 boost::format flt(" %9.1f");
1711 for(const auto& e : entries) {
1712 string second;
1713 if(const auto& val = boost::get<DNSDistStats::stat_t*>(&e.second))
1714 second=std::to_string((*val)->load());
af619119
RG
1715 else if (const auto& dval = boost::get<double*>(&e.second))
1716 second=(flt % (**dval)).str();
2a817e5a 1717 else
1718 second=std::to_string((*boost::get<DNSDistStats::statfunction_t>(&e.second))(e.first));
1719
1720 if(leftcolumn.size() < g_stats.entries.size()/2)
1721 leftcolumn.push_back((fmt % e.first % second).str());
1722 else
1723 rightcolumn.push_back((fmt % e.first % second).str());
1724 }
1725
1726 auto leftiter=leftcolumn.begin(), rightiter=rightcolumn.begin();
1727 boost::format clmn("%|0t|%1% %|39t|%2%\n");
1728
1729 for(;leftiter != leftcolumn.end() || rightiter != rightcolumn.end();) {
1730 string lentry, rentry;
1731 if(leftiter!= leftcolumn.end()) {
1732 lentry = *leftiter;
1733 leftiter++;
1734 }
1735 if(rightiter!= rightcolumn.end()) {
1736 rentry = *rightiter;
1737 rightiter++;
1738 }
1739 g_outputBuffer += (clmn % lentry % rentry).str();
1740 }
1741 });
80a216c9 1742
886e2cf2 1743 moreLua(client);
df111b53 1744
839f3021 1745 std::ifstream ifs(config);
2e72cc0e 1746 if(!ifs)
839f3021 1747 warnlog("Unable to read configuration from '%s'", config);
2e72cc0e 1748 else
cdc04ede 1749 vinfolog("Read configuration from '%s'", config);
df111b53 1750
1751 g_lua.executeCode(ifs);
d8c19b98 1752
2e72cc0e 1753 auto ret=*g_launchWork;
1754 delete g_launchWork;
1755 g_launchWork=0;
1756 return ret;
df111b53 1757}