]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-lua.cc
Merge pull request #3457 from rgacogne/dnsdist-string-only-server
[thirdparty/pdns.git] / pdns / dnsdist-lua.cc
CommitLineData
df111b53 1#include "dnsdist.hh"
0940e4eb 2#include "dnsrulactions.hh"
df111b53 3#include <thread>
4#include "dolog.hh"
5#include "sodcrypto.hh"
6#include "base64.hh"
7#include <fstream>
b7860997 8#include "dnswriter.hh"
0e41337b 9#include "lock.hh"
fbe2a2e0 10#include <net/if.h>
df111b53 11
12using std::thread;
13
2e72cc0e 14static vector<std::function<void(void)>>* g_launchWork;
15
d8d85a30 16class LuaAction : public DNSAction
17{
18public:
497a6e3a 19 typedef std::function<std::tuple<int, string>(DNSQuestion* dq)> func_t;
d8d85a30 20 LuaAction(LuaAction::func_t func) : d_func(func)
21 {}
22
497a6e3a 23 Action operator()(DNSQuestion* dq, string* ruleresult) const override
d8d85a30 24 {
01d9286f 25 std::lock_guard<std::mutex> lock(g_luamutex);
497a6e3a 26 auto ret = d_func(dq);
d8d85a30 27 if(ruleresult)
28 *ruleresult=std::get<1>(ret);
29 return (Action)std::get<0>(ret);
30 }
31
497a6e3a 32 string toString() const override
d8d85a30 33 {
34 return "Lua script";
35 }
36
37private:
38 func_t d_func;
39};
40
6bba426c 41typedef boost::variant<string,vector<pair<int, string>>, std::shared_ptr<DNSRule> > luadnsrule_t;
42std::shared_ptr<DNSRule> makeRule(const luadnsrule_t& var)
d8d85a30 43{
6bba426c 44 if(auto src = boost::get<std::shared_ptr<DNSRule>>(&var))
45 return *src;
46
d8d85a30 47 SuffixMatchNode smn;
48 NetmaskGroup nmg;
6bba426c 49
d8d85a30 50 auto add=[&](string src) {
51 try {
5ad8b87a 52 nmg.addMask(src); // need to try mask first, all masks are domain names!
d8d85a30 53 } catch(...) {
5ad8b87a 54 smn.add(DNSName(src));
d8d85a30 55 }
56 };
57 if(auto src = boost::get<string>(&var))
58 add(*src);
59 else {
60 for(auto& a : boost::get<vector<pair<int, string>>>(var)) {
61 add(a.second);
62 }
63 }
64 if(nmg.empty())
65 return std::make_shared<SuffixMatchNodeRule>(smn);
66 else
67 return std::make_shared<NetmaskGroupRule>(nmg);
68}
69
2d11d1b2 70std::unordered_map<int, vector<boost::variant<string,double>>> getGenResponses(unsigned int top, boost::optional<int> labels, std::function<bool(const Rings::Response&)> pred)
71{
72 setLuaNoSideEffect();
73 map<DNSName, int> counts;
74 unsigned int total=0;
75 {
76 std::lock_guard<std::mutex> lock(g_rings.respMutex);
77 if(!labels) {
78 for(const auto& a : g_rings.respRing) {
79 if(!pred(a))
80 continue;
81 counts[a.name]++;
82 total++;
83 }
84 }
85 else {
86 unsigned int lab = *labels;
87 for(auto a : g_rings.respRing) {
88 if(!pred(a))
89 continue;
90
91 a.name.trimToLabels(lab);
92 counts[a.name]++;
93 total++;
94 }
95
96 }
97 }
98 // cout<<"Looked at "<<total<<" responses, "<<counts.size()<<" different ones"<<endl;
99 vector<pair<int, DNSName>> rcounts;
100 rcounts.reserve(counts.size());
101 for(const auto& c : counts)
102 rcounts.push_back(make_pair(c.second, c.first));
103
104 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
105 const decltype(rcounts)::value_type& b) {
106 return b.first < a.first;
107 });
108
109 std::unordered_map<int, vector<boost::variant<string,double>>> ret;
110 unsigned int count=1, rest=0;
111 for(const auto& rc : rcounts) {
112 if(count==top+1)
113 rest+=rc.first;
114 else
115 ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
116 }
6a62c0e3 117 ret.insert({count, {"Rest", rest, total > 0 ? 100.0*rest/total : 100.0}});
2d11d1b2 118 return ret;
119}
120
839f3021 121vector<std::function<void(void)>> setupLua(bool client, const std::string& config)
df111b53 122{
2e72cc0e 123 g_launchWork= new vector<std::function<void(void)>>();
a6e02424 124 typedef std::unordered_map<std::string, boost::variant<bool, std::string, vector<pair<int, std::string> > > > newserver_t;
e48090d1 125
d8d85a30 126 g_lua.writeVariable("DNSAction", std::unordered_map<string,int>{
127 {"Drop", (int)DNSAction::Action::Drop},
128 {"Nxdomain", (int)DNSAction::Action::Nxdomain},
129 {"Spoof", (int)DNSAction::Action::Spoof},
130 {"Allow", (int)DNSAction::Action::Allow},
131 {"HeaderModify", (int)DNSAction::Action::HeaderModify},
132 {"Pool", (int)DNSAction::Action::Pool},
21c2cdd8
RG
133 {"None",(int)DNSAction::Action::None},
134 {"Delay", (int)DNSAction::Action::Delay}}
d8d85a30 135 );
bac6e8fb 136
137 vector<pair<string, int> > dd;
138 for(const auto& n : QType::names)
139 dd.push_back({n.first, n.second});
140 g_lua.writeVariable("dnsdist", dd);
d8d85a30 141
df111b53 142 g_lua.writeFunction("newServer",
839f3021 143 [client](boost::variant<string,newserver_t> pvars, boost::optional<int> qps)
df111b53 144 {
f758857a 145 setLuaSideEffect();
2e72cc0e 146 if(client) {
b4fd86c3 147 return std::make_shared<DownstreamState>(ComboAddress());
2e72cc0e 148 }
fbe2a2e0
RG
149 ComboAddress sourceAddr;
150 unsigned int sourceItf = 0;
df111b53 151 if(auto address = boost::get<string>(&pvars)) {
cbbd1533 152 std::shared_ptr<DownstreamState> ret;
153 try {
154 ret=std::make_shared<DownstreamState>(ComboAddress(*address, 53));
155 }
156 catch(std::exception& e) {
157 g_outputBuffer="Error creating new server: "+string(e.what());
158 errlog("Error creating new server with address %s: %s", *address, e.what());
159 return ret;
160 }
2e72cc0e 161
df111b53 162 if(qps) {
163 ret->qps=QPSLimiter(*qps, *qps);
164 }
ecbe9133 165 g_dstates.modify([ret](servers_t& servers) {
166 servers.push_back(ret);
167 std::stable_sort(servers.begin(), servers.end(), [](const decltype(ret)& a, const decltype(ret)& b) {
168 return a->order < b->order;
169 });
170
171 });
2e72cc0e 172
46a839bf
RG
173 auto localPools = g_pools.getCopy();
174 addServerToPool(localPools, "", ret);
175 g_pools.setState(localPools);
176
2e72cc0e 177 if(g_launchWork) {
178 g_launchWork->push_back([ret]() {
179 ret->tid = move(thread(responderThread, ret));
180 });
181 }
182 else {
183 ret->tid = move(thread(responderThread, ret));
184 }
185
df111b53 186 return ret;
187 }
839f3021 188 auto vars=boost::get<newserver_t>(pvars);
fbe2a2e0
RG
189
190 if(vars.count("source")) {
191 /* handle source in the following forms:
192 - v4 address ("192.0.2.1")
193 - v6 address ("2001:DB8::1")
194 - interface name ("eth0")
195 - v4 address and interface name ("192.0.2.1@eth0")
196 - v6 address and interface name ("2001:DB8::1@eth0")
197 */
198 const string source = boost::get<string>(vars["source"]);
199 bool parsed = false;
200 std::string::size_type pos = source.find("@");
201 if (pos == std::string::npos) {
202 /* no '@', try to parse that as a valid v4/v6 address */
203 try {
204 sourceAddr = ComboAddress(source);
205 parsed = true;
206 }
207 catch(...)
208 {
209 }
210 }
211
212 if (parsed == false)
213 {
214 /* try to parse as interface name, or v4/v6@itf */
215 string itfName = source.substr(pos == std::string::npos ? 0 : pos + 1);
216 unsigned int itfIdx = if_nametoindex(itfName.c_str());
217
218 if (itfIdx != 0) {
219 if (pos == 0 || pos == std::string::npos) {
220 /* "eth0" or "@eth0" */
221 sourceItf = itfIdx;
222 }
223 else {
224 /* "192.0.2.1@eth0" */
225 sourceAddr = ComboAddress(source.substr(0, pos));
226 sourceItf = itfIdx;
227 }
228 }
229 else
230 {
231 warnlog("Dismissing source %s because '%s' is not a valid interface name", source, itfName);
232 }
233 }
234 }
235
cbbd1533 236 std::shared_ptr<DownstreamState> ret;
237 try {
fbe2a2e0 238 ret=std::make_shared<DownstreamState>(ComboAddress(boost::get<string>(vars["address"]), 53), sourceAddr, sourceItf);
cbbd1533 239 }
240 catch(std::exception& e) {
241 g_outputBuffer="Error creating new server: "+string(e.what());
242 errlog("Error creating new server with address %s: %s", boost::get<string>(vars["address"]), e.what());
243 return ret;
244 }
fbe2a2e0 245
df111b53 246 if(vars.count("qps")) {
335da0ba 247 int qps=std::stoi(boost::get<string>(vars["qps"]));
839f3021 248 ret->qps=QPSLimiter(qps, qps);
df111b53 249 }
250
886e2cf2 251 auto localPools = g_pools.getCopy();
df111b53 252 if(vars.count("pool")) {
839f3021 253 if(auto* pool = boost::get<string>(&vars["pool"]))
254 ret->pools.insert(*pool);
255 else {
256 auto* pools = boost::get<vector<pair<int, string> > >(&vars["pool"]);
257 for(auto& p : *pools)
258 ret->pools.insert(p.second);
259 }
886e2cf2
RG
260 for(const auto& poolName: ret->pools) {
261 addServerToPool(localPools, poolName, ret);
262 }
263 }
264 else {
265 addServerToPool(localPools, "", ret);
df111b53 266 }
886e2cf2 267 g_pools.setState(localPools);
df111b53 268
269 if(vars.count("order")) {
335da0ba 270 ret->order=std::stoi(boost::get<string>(vars["order"]));
df111b53 271 }
272
273 if(vars.count("weight")) {
335da0ba 274 ret->weight=std::stoi(boost::get<string>(vars["weight"]));
df111b53 275 }
276
3f6d07a4 277 if(vars.count("retries")) {
335da0ba 278 ret->retries=std::stoi(boost::get<string>(vars["retries"]));
3f6d07a4
RG
279 }
280
281 if(vars.count("tcpSendTimeout")) {
335da0ba 282 ret->tcpSendTimeout=std::stoi(boost::get<string>(vars["tcpSendTimeout"]));
3f6d07a4
RG
283 }
284
285 if(vars.count("tcpRecvTimeout")) {
335da0ba 286 ret->tcpRecvTimeout=std::stoi(boost::get<string>(vars["tcpRecvTimeout"]));
3f6d07a4
RG
287 }
288
18eeccc9
RG
289 if(vars.count("name")) {
290 ret->name=boost::get<string>(vars["name"]);
291 }
292
ad485896
RG
293 if(vars.count("checkName")) {
294 ret->checkName=DNSName(boost::get<string>(vars["checkName"]));
295 }
296
297 if(vars.count("checkType")) {
298 ret->checkType=boost::get<string>(vars["checkType"]);
299 }
300
a6e02424
RG
301 if(vars.count("mustResolve")) {
302 ret->mustResolve=boost::get<bool>(vars["mustResolve"]);
303 }
304
ca404e94
RG
305 if(vars.count("useClientSubnet")) {
306 ret->useECS=boost::get<bool>(vars["useClientSubnet"]);
307 }
308
9e87dcb8
RG
309 if(vars.count("maxCheckFailures")) {
310 ret->maxCheckFailures=std::stoi(boost::get<string>(vars["maxCheckFailures"]));
311 }
312
2e72cc0e 313 if(g_launchWork) {
314 g_launchWork->push_back([ret]() {
315 ret->tid = move(thread(responderThread, ret));
316 });
317 }
318 else {
319 ret->tid = move(thread(responderThread, ret));
320 }
df111b53 321
ecbe9133 322 auto states = g_dstates.getCopy();
e5a14b2b 323 states.push_back(ret);
324 std::stable_sort(states.begin(), states.end(), [](const decltype(ret)& a, const decltype(ret)& b) {
df111b53 325 return a->order < b->order;
326 });
ecbe9133 327 g_dstates.setState(states);
df111b53 328 return ret;
329 } );
330
ecc8a33b 331 g_lua.writeFunction("makeRule", makeRule);
490a29bb 332
0940e4eb 333 g_lua.writeFunction("addAnyTCRule", []() {
f758857a 334 setLuaSideEffect();
0940e4eb 335 auto rules=g_rulactions.getCopy();
490a29bb
RG
336 std::vector<pair<int, shared_ptr<DNSRule> >> v;
337 v.push_back({1, std::make_shared<QTypeRule>(0xff)});
338 v.push_back({2, std::make_shared<TCPRule>(false)});
339 rules.push_back({ std::shared_ptr<DNSRule>(new AndRule(v)), std::make_shared<TCAction>()});
0940e4eb 340 g_rulactions.setState(rules);
341 });
df111b53 342
0940e4eb 343 g_lua.writeFunction("rmRule", [](unsigned int num) {
f758857a 344 setLuaSideEffect();
0940e4eb 345 auto rules = g_rulactions.getCopy();
346 if(num >= rules.size()) {
347 g_outputBuffer = "Error: attempt to delete non-existing rule\n";
348 return;
349 }
350 rules.erase(rules.begin()+num);
351 g_rulactions.setState(rules);
352 });
353
b4fd86c3 354 g_lua.writeFunction("topRule", []() {
f758857a 355 setLuaSideEffect();
b4fd86c3 356 auto rules = g_rulactions.getCopy();
357 if(rules.empty())
358 return;
359 auto subject = *rules.rbegin();
360 rules.erase(std::prev(rules.end()));
361 rules.insert(rules.begin(), subject);
362 g_rulactions.setState(rules);
363 });
0940e4eb 364 g_lua.writeFunction("mvRule", [](unsigned int from, unsigned int to) {
f758857a 365 setLuaSideEffect();
0940e4eb 366 auto rules = g_rulactions.getCopy();
367 if(from >= rules.size() || to > rules.size()) {
368 g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
369 return;
370 }
0940e4eb 371
372 auto subject = rules[from];
373 rules.erase(rules.begin()+from);
374 if(to == rules.size())
375 rules.push_back(subject);
376 else {
377 if(from < to)
378 --to;
379 rules.insert(rules.begin()+to, subject);
380 }
381 g_rulactions.setState(rules);
382 });
df111b53 383
384
385 g_lua.writeFunction("rmServer",
386 [](boost::variant<std::shared_ptr<DownstreamState>, int> var)
387 {
f758857a 388 setLuaSideEffect();
886e2cf2
RG
389 shared_ptr<DownstreamState> server;
390 auto* rem = boost::get<shared_ptr<DownstreamState>>(&var);
391 auto states = g_dstates.getCopy();
392 if(rem) {
393 server = *rem;
394 }
395 else {
396 int idx = boost::get<int>(var);
397 server = states[idx];
398 }
399 auto localPools = g_pools.getCopy();
400 for (const string& poolName : server->pools) {
401 removeServerFromPool(localPools, poolName, server);
402 }
403 g_pools.setState(localPools);
404 states.erase(remove(states.begin(), states.end(), server), states.end());
405 g_dstates.setState(states);
df111b53 406 } );
407
408
409 g_lua.writeFunction("setServerPolicy", [](ServerPolicy policy) {
f758857a 410 setLuaSideEffect();
e5a14b2b 411 g_policy.setState(policy);
df111b53 412 });
70a57b05 413 g_lua.writeFunction("setServerPolicyLua", [](string name, policyfunc_t policy) {
f758857a 414 setLuaSideEffect();
e5a14b2b 415 g_policy.setState(ServerPolicy{name, policy});
df111b53 416 });
417
418 g_lua.writeFunction("showServerPolicy", []() {
f758857a 419 setLuaSideEffect();
ecbe9133 420 g_outputBuffer=g_policy.getLocal()->name+"\n";
df111b53 421 });
422
f758857a 423 g_lua.writeFunction("truncateTC", [](bool tc) { setLuaSideEffect(); g_truncateTC=tc; });
424 g_lua.writeFunction("fixupCase", [](bool fu) { setLuaSideEffect(); g_fixupCase=fu; });
df111b53 425
426 g_lua.registerMember("name", &ServerPolicy::name);
427 g_lua.registerMember("policy", &ServerPolicy::policy);
70a57b05 428 g_lua.writeFunction("newServerPolicy", [](string name, policyfunc_t policy) { return ServerPolicy{name, policy};});
df111b53 429 g_lua.writeVariable("firstAvailable", ServerPolicy{"firstAvailable", firstAvailable});
430 g_lua.writeVariable("roundrobin", ServerPolicy{"roundrobin", roundrobin});
431 g_lua.writeVariable("wrandom", ServerPolicy{"wrandom", wrandom});
a7f3108c 432 g_lua.writeVariable("whashed", ServerPolicy{"whashed", whashed});
df111b53 433 g_lua.writeVariable("leastOutstanding", ServerPolicy{"leastOutstanding", leastOutstanding});
434 g_lua.writeFunction("addACL", [](const std::string& domain) {
f758857a 435 setLuaSideEffect();
e5a14b2b 436 g_ACL.modify([domain](NetmaskGroup& nmg) { nmg.addMask(domain); });
df111b53 437 });
2e72cc0e 438
5949b95b 439 g_lua.writeFunction("setLocal", [client](const std::string& addr, boost::optional<bool> doTCP) {
f758857a 440 setLuaSideEffect();
5949b95b 441 if(client)
442 return;
85e4ce52
RG
443 if (g_configurationDone) {
444 g_outputBuffer="setLocal cannot be used at runtime!\n";
445 return;
446 }
5949b95b 447 try {
448 ComboAddress loc(addr, 53);
449 g_locals.clear();
450 g_locals.push_back({loc, doTCP ? *doTCP : true}); /// only works pre-startup, so no sync necessary
451 }
452 catch(std::exception& e) {
453 g_outputBuffer="Error: "+string(e.what())+"\n";
454 }
455 });
456
652a7355 457 g_lua.writeFunction("addLocal", [client](const std::string& addr, boost::optional<bool> doTCP) {
f758857a 458 setLuaSideEffect();
2e72cc0e 459 if(client)
460 return;
85e4ce52
RG
461 if (g_configurationDone) {
462 g_outputBuffer="addLocal cannot be used at runtime!\n";
463 return;
464 }
2e72cc0e 465 try {
466 ComboAddress loc(addr, 53);
652a7355 467 g_locals.push_back({loc, doTCP ? *doTCP : true}); /// only works pre-startup, so no sync necessary
2e72cc0e 468 }
469 catch(std::exception& e) {
470 g_outputBuffer="Error: "+string(e.what())+"\n";
471 }
472 });
e4944ea0 473 g_lua.writeFunction("setACL", [](boost::variant<string,vector<pair<int, string>>> inp) {
f758857a 474 setLuaSideEffect();
e5a14b2b 475 NetmaskGroup nmg;
e4944ea0 476 if(auto str = boost::get<string>(&inp)) {
477 nmg.addMask(*str);
478 }
479 else for(const auto& p : boost::get<vector<pair<int,string>>>(inp)) {
e5a14b2b 480 nmg.addMask(p.second);
cffde2fd 481 }
482 g_ACL.setState(nmg);
df111b53 483 });
484 g_lua.writeFunction("showACL", []() {
f758857a 485 setLuaNoSideEffect();
df111b53 486 vector<string> vec;
cffde2fd 487
e5a14b2b 488 g_ACL.getCopy().toStringVector(&vec);
cffde2fd 489
df111b53 490 for(const auto& s : vec)
cffde2fd 491 g_outputBuffer+=s+"\n";
492
df111b53 493 });
494 g_lua.writeFunction("shutdown", []() { _exit(0);} );
495
496
ecbe9133 497 g_lua.writeFunction("addDomainBlock", [](const std::string& domain) {
f758857a 498 setLuaSideEffect();
0940e4eb 499 SuffixMatchNode smn;
1f9100a7 500 smn.add(DNSName(domain));
0940e4eb 501 g_rulactions.modify([smn](decltype(g_rulactions)::value_type& rulactions) {
502 rulactions.push_back({
503 std::make_shared<SuffixMatchNodeRule>(smn),
504 std::make_shared<DropAction>() });
505 });
506
ecbe9133 507 });
df111b53 508 g_lua.writeFunction("showServers", []() {
f758857a 509 setLuaNoSideEffect();
df111b53 510 try {
511 ostringstream ret;
1287674c
RG
512 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%" );
513 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14
514 ret << (fmt % "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Outstanding" % "Pools") << endl;
df111b53 515
516 uint64_t totQPS{0}, totQueries{0}, totDrops{0};
517 int counter=0;
ecbe9133 518 auto states = g_dstates.getCopy();
e5a14b2b 519 for(const auto& s : states) {
df111b53 520 string status;
521 if(s->availability == DownstreamState::Availability::Up)
522 status = "UP";
523 else if(s->availability == DownstreamState::Availability::Down)
524 status = "DOWN";
525 else
526 status = (s->upStatus ? "up" : "down");
527
528 string pools;
529 for(auto& p : s->pools) {
530 if(!pools.empty())
531 pools+=" ";
532 pools+=p;
533 }
534
18eeccc9 535 ret << (fmt % counter % s->name % s->remote.toStringWithPort() %
df111b53 536 status %
1287674c 537 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 538
539 totQPS += s->queryLoad;
540 totQueries += s->queries.load();
541 totDrops += s->reuseds.load();
542 ++counter;
543 }
18eeccc9 544 ret<< (fmt % "All" % "" % "" % ""
df111b53 545 %
1287674c 546 (double)totQPS % "" % "" % "" % totQueries % totDrops % "" % "" % "" % "" ) << endl;
df111b53 547
548 g_outputBuffer=ret.str();
549 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
550 });
551
6bba426c 552 g_lua.writeFunction("addLuaAction", [](luadnsrule_t var, LuaAction::func_t func)
d8d85a30 553 {
f758857a 554 setLuaSideEffect();
d8d85a30 555 auto rule=makeRule(var);
556 g_rulactions.modify([rule,func](decltype(g_rulactions)::value_type& rulactions){
557 rulactions.push_back({rule,
558 std::make_shared<LuaAction>(func)});
559 });
560 });
df111b53 561
d8d85a30 562
6bba426c 563 g_lua.writeFunction("NoRecurseAction", []() {
564 return std::shared_ptr<DNSAction>(new NoRecurseAction);
565 });
566
6907f014 567 g_lua.writeFunction("MacAddrAction", [](int code) {
568 return std::shared_ptr<DNSAction>(new MacAddrAction(code));
569 });
570
571
e27097e4 572 g_lua.writeFunction("PoolAction", [](const string& a) {
573 return std::shared_ptr<DNSAction>(new PoolAction(a));
574 });
575
b1bec9f0
RG
576 g_lua.writeFunction("QPSPoolAction", [](int limit, const string& a) {
577 return std::shared_ptr<DNSAction>(new QPSPoolAction(limit, a));
578 });
579
9ebc0e91 580 g_lua.writeFunction("SpoofAction", [](boost::variant<string,vector<pair<int, string>>> inp, boost::optional<string> b ) {
581 vector<ComboAddress> addrs;
582 if(auto s = boost::get<string>(&inp))
583 addrs.push_back(ComboAddress(*s));
584 else {
585 const auto& v = boost::get<vector<pair<int,string>>>(inp);
586 for(const auto& a: v)
587 addrs.push_back(ComboAddress(a.second));
588 }
589 if(b)
590 addrs.push_back(ComboAddress(*b));
591 return std::shared_ptr<DNSAction>(new SpoofAction(addrs));
731774a8 592 });
593
87c605c4
RG
594 g_lua.writeFunction("SpoofCNAMEAction", [](const string& a) {
595 return std::shared_ptr<DNSAction>(new SpoofAction(a));
596 });
597
9ebc0e91 598 g_lua.writeFunction("addDomainSpoof", [](const std::string& domain, boost::variant<string,vector<pair<int, string>>> inp, boost::optional<string> b) {
f758857a 599 setLuaSideEffect();
731774a8 600 SuffixMatchNode smn;
9ebc0e91 601 vector<ComboAddress> outp;
731774a8 602 try
603 {
604 smn.add(DNSName(domain));
9ebc0e91 605
606 if(auto s = boost::get<string>(&inp))
607 outp.push_back(ComboAddress(*s));
608 else {
609 const auto& v = boost::get<vector<pair<int,string>>>(inp);
610 for(const auto& a: v)
611 outp.push_back(ComboAddress(a.second));
612 }
613 if(b)
614 outp.push_back(ComboAddress(*b));
615
731774a8 616 }
617 catch(std::exception& e) {
618 g_outputBuffer="Error parsing parameters: "+string(e.what());
619 return;
620 }
9ebc0e91 621 g_rulactions.modify([&smn,&outp](decltype(g_rulactions)::value_type& rulactions) {
731774a8 622 rulactions.push_back({
623 std::make_shared<SuffixMatchNodeRule>(smn),
9ebc0e91 624 std::make_shared<SpoofAction>(outp) });
731774a8 625 });
626
627 });
628
87c605c4
RG
629 g_lua.writeFunction("addDomainCNAMESpoof", [](const std::string& domain, const std::string& cname) {
630 setLuaSideEffect();
631 SuffixMatchNode smn;
632 try
633 {
634 smn.add(DNSName(domain));
635 }
636 catch(std::exception& e) {
637 g_outputBuffer="Error parsing parameters: "+string(e.what());
638 return;
639 }
640 g_rulactions.modify([&smn,&cname](decltype(g_rulactions)::value_type& rulactions) {
641 rulactions.push_back({
642 std::make_shared<SuffixMatchNodeRule>(smn),
643 std::make_shared<SpoofAction>(cname) });
644 });
645 });
731774a8 646
ae3dfa48 647 g_lua.writeFunction("DropAction", []() {
648 return std::shared_ptr<DNSAction>(new DropAction);
649 });
650
63beb26d
G
651 g_lua.writeFunction("AllowAction", []() {
652 return std::shared_ptr<DNSAction>(new AllowAction);
653 });
654
6eecd4c2 655 g_lua.writeFunction("DelayAction", [](int msec) {
656 return std::shared_ptr<DNSAction>(new DelayAction(msec));
657 });
658
2332b03c 659 g_lua.writeFunction("TCAction", []() {
660 return std::shared_ptr<DNSAction>(new TCAction);
661 });
662
f39b7598
RG
663 g_lua.writeFunction("DisableValidationAction", []() {
664 return std::shared_ptr<DNSAction>(new DisableValidationAction);
665 });
666
18029431
RG
667 g_lua.writeFunction("LogAction", [](const std::string& fname, boost::optional<bool> binary) {
668 return std::shared_ptr<DNSAction>(new LogAction(fname, binary ? *binary : true));
808c5ef7 669 });
670
0bd31ccc
RG
671 g_lua.writeFunction("RCodeAction", [](int rcode) {
672 return std::shared_ptr<DNSAction>(new RCodeAction(rcode));
673 });
ae3dfa48 674
886e2cf2
RG
675 g_lua.writeFunction("SkipCacheAction", []() {
676 return std::shared_ptr<DNSAction>(new SkipCacheAction);
677 });
678
1b726acf 679 g_lua.writeFunction("MaxQPSIPRule", [](unsigned int qps, boost::optional<int> ipv4trunc, boost::optional<int> ipv6trunc) {
680 return std::shared_ptr<DNSRule>(new MaxQPSIPRule(qps, ipv4trunc.get_value_or(32), ipv6trunc.get_value_or(64)));
6bba426c 681 });
682
683
2332b03c 684 g_lua.writeFunction("MaxQPSRule", [](unsigned int qps, boost::optional<int> burst) {
685 if(!burst)
686 return std::shared_ptr<DNSRule>(new MaxQPSRule(qps));
687 else
688 return std::shared_ptr<DNSRule>(new MaxQPSRule(qps, *burst));
689 });
690
691
6eecd4c2 692 g_lua.writeFunction("RegexRule", [](const std::string& str) {
693 return std::shared_ptr<DNSRule>(new RegexRule(str));
694 });
695
4ed8dfeb 696#ifdef HAVE_RE2
697 g_lua.writeFunction("RE2Rule", [](const std::string& str) {
698 return std::shared_ptr<DNSRule>(new RE2Rule(str));
699 });
700#endif
701
0bd31ccc
RG
702 g_lua.writeFunction("SuffixMatchNodeRule", [](const SuffixMatchNode& smn) {
703 return std::shared_ptr<DNSRule>(new SuffixMatchNodeRule(smn));
704 });
b7860997 705
706 g_lua.writeFunction("benchRule", [](std::shared_ptr<DNSRule> rule, boost::optional<int> times_, boost::optional<string> suffix_) {
f758857a 707 setLuaNoSideEffect();
b7860997 708 int times = times_.get_value_or(100000);
709 DNSName suffix(suffix_.get_value_or("powerdns.com"));
710 struct item {
711 vector<uint8_t> packet;
712 ComboAddress rem;
713 DNSName qname;
3b069df2 714 uint16_t qtype, qclass;
b7860997 715 };
716 vector<item> items;
717 items.reserve(1000);
718 for(int n=0; n < 1000; ++n) {
719 struct item i;
720 i.qname=DNSName(std::to_string(random()));
721 i.qname += suffix;
722 i.qtype = random() % 0xff;
3b069df2 723 i.qclass = 1;
b7860997 724 i.rem=ComboAddress("127.0.0.1");
725 i.rem.sin4.sin_addr.s_addr = random();
726 DNSPacketWriter pw(i.packet, i.qname, i.qtype);
727 items.push_back(i);
728 }
729
730 int matches=0;
497a6e3a 731 ComboAddress dummy("127.0.0.1");
b7860997 732 DTime dt;
733 dt.set();
734 for(int n=0; n < times; ++n) {
735 const item& i = items[n % items.size()];
3b069df2 736 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 737 if(rule->matches(&dq))
b7860997 738 matches++;
739 }
740 double udiff=dt.udiff();
741 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();
742
743 });
89cb6f9a 744
745 g_lua.writeFunction("AllRule", []() {
746 return std::shared_ptr<DNSRule>(new AllRule());
747 });
748
b7860997 749 g_lua.writeFunction("QTypeRule", [](boost::variant<int, std::string> str) {
750 uint16_t qtype;
751 if(auto dir = boost::get<int>(&str)) {
752 qtype = *dir;
753 }
754 else {
755 string val=boost::get<string>(str);
756 qtype = QType::chartocode(val.c_str());
757 if(!qtype)
758 throw std::runtime_error("Unable to convert '"+val+"' to a DNS type");
759 }
760 return std::shared_ptr<DNSRule>(new QTypeRule(qtype));
761 });
3b069df2 762 g_lua.writeFunction("QClassRule", [](int c) {
763 return std::shared_ptr<DNSRule>(new QClassRule(c));
764 });
765
b7860997 766
767 g_lua.writeFunction("AndRule", [](vector<pair<int, std::shared_ptr<DNSRule> > >a) {
768 return std::shared_ptr<DNSRule>(new AndRule(a));
769 });
770
e7a1029c
RG
771 g_lua.writeFunction("OrRule", [](vector<pair<int, std::shared_ptr<DNSRule> > >a) {
772 return std::shared_ptr<DNSRule>(new OrRule(a));
773 });
774
490a29bb
RG
775 g_lua.writeFunction("TCPRule", [](bool tcp) {
776 return std::shared_ptr<DNSRule>(new TCPRule(tcp));
777 });
b7860997 778
b1bec9f0
RG
779 g_lua.writeFunction("DNSSECRule", []() {
780 return std::shared_ptr<DNSRule>(new DNSSECRule());
781 });
782
e7a1029c
RG
783 g_lua.writeFunction("NotRule", [](std::shared_ptr<DNSRule>rule) {
784 return std::shared_ptr<DNSRule>(new NotRule(rule));
785 });
786
6bba426c 787 g_lua.writeFunction("addAction", [](luadnsrule_t var, std::shared_ptr<DNSAction> ea)
788 {
f758857a 789 setLuaSideEffect();
6bba426c 790 auto rule=makeRule(var);
791 g_rulactions.modify([rule, ea](decltype(g_rulactions)::value_type& rulactions){
792 rulactions.push_back({rule, ea});
793 });
794 });
795
796
797 g_lua.writeFunction("addPoolRule", [](luadnsrule_t var, string pool) {
f758857a 798 setLuaSideEffect();
d8d85a30 799 auto rule=makeRule(var);
800 g_rulactions.modify([rule, pool](decltype(g_rulactions)::value_type& rulactions) {
0940e4eb 801 rulactions.push_back({
d8d85a30 802 rule,
803 std::make_shared<PoolAction>(pool) });
ecbe9133 804 });
df111b53 805 });
0570f37c 806
6bba426c 807 g_lua.writeFunction("addNoRecurseRule", [](luadnsrule_t var) {
f758857a 808 setLuaSideEffect();
0570f37c 809 auto rule=makeRule(var);
810 g_rulactions.modify([rule](decltype(g_rulactions)::value_type& rulactions) {
811 rulactions.push_back({
812 rule,
813 std::make_shared<NoRecurseAction>() });
814 });
815 });
816
f39b7598 817 g_lua.writeFunction("addDisableValidationRule", [](luadnsrule_t var) {
f758857a 818 setLuaSideEffect();
f39b7598
RG
819 auto rule=makeRule(var);
820 g_rulactions.modify([rule](decltype(g_rulactions)::value_type& rulactions) {
821 rulactions.push_back({
822 rule,
823 std::make_shared<DisableValidationAction>() });
824 });
825 });
826
0570f37c 827
6bba426c 828 g_lua.writeFunction("addQPSPoolRule", [](luadnsrule_t var, int limit, string pool) {
f758857a 829 setLuaSideEffect();
d8d85a30 830 auto rule = makeRule(var);
831 g_rulactions.modify([rule, pool,limit](decltype(g_rulactions)::value_type& rulactions) {
832 rulactions.push_back({
833 rule,
834 std::make_shared<QPSPoolAction>(limit, pool) });
835 });
fd010ca3 836 });
df111b53 837
520eb5a0 838 g_lua.writeFunction("setDNSSECPool", [](const std::string& pool) {
f758857a 839 setLuaSideEffect();
520eb5a0 840 g_rulactions.modify([pool](decltype(g_rulactions)::value_type& rulactions) {
841 rulactions.push_back({std::make_shared<DNSSECRule>(),
842 std::make_shared<PoolAction>(pool)});
843 });
844 });
df111b53 845
6bba426c 846 g_lua.writeFunction("addQPSLimit", [](luadnsrule_t var, int lim) {
f758857a 847 setLuaSideEffect();
d8d85a30 848 auto rule = makeRule(var);
849 g_rulactions.modify([lim,rule](decltype(g_rulactions)::value_type& rulactions) {
850 rulactions.push_back({rule,
851 std::make_shared<QPSAction>(lim)});
852 });
df111b53 853 });
d8d85a30 854
6bba426c 855 g_lua.writeFunction("addDelay", [](luadnsrule_t var, int msec) {
f758857a 856 setLuaSideEffect();
7b3865cd 857 auto rule = makeRule(var);
858 g_rulactions.modify([msec,rule](decltype(g_rulactions)::value_type& rulactions) {
859 rulactions.push_back({rule,
860 std::make_shared<DelayAction>(msec)});
861 });
862 });
df111b53 863
df111b53 864
0940e4eb 865 g_lua.writeFunction("showRules", []() {
f758857a 866 setLuaNoSideEffect();
0940e4eb 867 boost::format fmt("%-3d %9d %-50s %s\n");
868 g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
869 int num=0;
870 for(const auto& lim : g_rulactions.getCopy()) {
871 string name = lim.first->toString();
872 g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
df111b53 873 ++num;
874 }
875 });
876
df111b53 877 g_lua.writeFunction("getServers", []() {
f758857a 878 setLuaNoSideEffect();
df111b53 879 vector<pair<int, std::shared_ptr<DownstreamState> > > ret;
880 int count=1;
e5a14b2b 881 for(const auto& s : g_dstates.getCopy()) {
df111b53 882 ret.push_back(make_pair(count++, s));
883 }
884 return ret;
885 });
886
da4e7813 887 g_lua.writeFunction("getPoolServers", [](string pool) {
886e2cf2 888 return getDownstreamCandidates(g_pools.getCopy(), pool);
da4e7813 889 });
890
8780ba53
RG
891 g_lua.writeFunction("getServer", [client](int i) {
892 if (client)
893 return std::make_shared<DownstreamState>(ComboAddress());
894 return g_dstates.getCopy().at(i);
895 });
df111b53 896
df111b53 897 g_lua.registerFunction<void(DownstreamState::*)(int)>("setQPS", [](DownstreamState& s, int lim) { s.qps = lim ? QPSLimiter(lim, lim) : QPSLimiter(); });
886e2cf2
RG
898 g_lua.registerFunction<void(std::shared_ptr<DownstreamState>::*)(string)>("addPool", [](std::shared_ptr<DownstreamState> s, string pool) {
899 auto localPools = g_pools.getCopy();
900 addServerToPool(localPools, pool, s);
901 g_pools.setState(localPools);
902 s->pools.insert(pool);
903 });
904 g_lua.registerFunction<void(std::shared_ptr<DownstreamState>::*)(string)>("rmPool", [](std::shared_ptr<DownstreamState> s, string pool) {
905 auto localPools = g_pools.getCopy();
906 removeServerFromPool(localPools, pool, s);
907 g_pools.setState(localPools);
908 s->pools.erase(pool);
909 });
df111b53 910
911 g_lua.registerFunction<void(DownstreamState::*)()>("getOutstanding", [](const DownstreamState& s) { g_outputBuffer=std::to_string(s.outstanding.load()); });
912
913
914 g_lua.registerFunction("isUp", &DownstreamState::isUp);
915 g_lua.registerFunction("setDown", &DownstreamState::setDown);
916 g_lua.registerFunction("setUp", &DownstreamState::setUp);
917 g_lua.registerFunction("setAuto", &DownstreamState::setAuto);
46a839bf
RG
918 g_lua.registerFunction("getName", &DownstreamState::getName);
919 g_lua.registerFunction("getNameWithAddr", &DownstreamState::getNameWithAddr);
b0976f44 920 g_lua.registerMember("upStatus", &DownstreamState::upStatus);
df111b53 921 g_lua.registerMember("weight", &DownstreamState::weight);
922 g_lua.registerMember("order", &DownstreamState::order);
46a839bf 923 g_lua.registerMember("name", &DownstreamState::name);
df111b53 924
4c6f4321 925 g_lua.writeFunction("infolog", [](const string& arg) {
926 infolog("%s", arg);
927 });
928 g_lua.writeFunction("errlog", [](const string& arg) {
929 errlog("%s", arg);
930 });
931 g_lua.writeFunction("warnlog", [](const string& arg) {
932 warnlog("%s", arg);
933 });
934
935
df111b53 936 g_lua.writeFunction("show", [](const string& arg) {
937 g_outputBuffer+=arg;
938 g_outputBuffer+="\n";
939 });
940
941 g_lua.registerFunction<void(dnsheader::*)(bool)>("setRD", [](dnsheader& dh, bool v) {
942 dh.rd=v;
943 });
944
945 g_lua.registerFunction<bool(dnsheader::*)()>("getRD", [](dnsheader& dh) {
946 return (bool)dh.rd;
947 });
948
aeb36780
RG
949 g_lua.registerFunction<void(dnsheader::*)(bool)>("setCD", [](dnsheader& dh, bool v) {
950 dh.cd=v;
951 });
952
953 g_lua.registerFunction<bool(dnsheader::*)()>("getCD", [](dnsheader& dh) {
954 return (bool)dh.cd;
955 });
956
df111b53 957
958 g_lua.registerFunction<void(dnsheader::*)(bool)>("setTC", [](dnsheader& dh, bool v) {
959 dh.tc=v;
bde3ab96 960 if(v) dh.ra = dh.rd; // you'll always need this, otherwise TC=1 gets ignored
df111b53 961 });
962
963 g_lua.registerFunction<void(dnsheader::*)(bool)>("setQR", [](dnsheader& dh, bool v) {
964 dh.qr=v;
965 });
966
df111b53 967
968 g_lua.registerFunction("tostring", &ComboAddress::toString);
0a112365 969 g_lua.registerFunction("tostringWithPort", &ComboAddress::toStringWithPort);
497a6e3a
RG
970 g_lua.registerFunction("toString", &ComboAddress::toString);
971 g_lua.registerFunction("toStringWithPort", &ComboAddress::toStringWithPort);
0a112365 972 g_lua.registerFunction<uint16_t(ComboAddress::*)()>("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); } );
df111b53 973 g_lua.registerFunction("isPartOf", &DNSName::isPartOf);
b5425a3d 974 g_lua.registerFunction<string(DNSName::*)()>("tostring", [](const DNSName&dn ) { return dn.toString(); });
497a6e3a 975 g_lua.registerFunction<string(DNSName::*)()>("toString", [](const DNSName&dn ) { return dn.toString(); });
df111b53 976 g_lua.writeFunction("newDNSName", [](const std::string& name) { return DNSName(name); });
b0976f44 977 g_lua.writeFunction("newSuffixMatchNode", []() { return SuffixMatchNode(); });
df111b53 978
979 g_lua.registerFunction("add",(void (SuffixMatchNode::*)(const DNSName&)) &SuffixMatchNode::add);
980 g_lua.registerFunction("check",(bool (SuffixMatchNode::*)(const DNSName&) const) &SuffixMatchNode::check);
981
42fae326 982 g_lua.writeFunction("carbonServer", [](const std::string& address, boost::optional<string> ourName,
983 boost::optional<int> interval) {
f758857a 984 setLuaSideEffect();
42fae326 985 auto ours = g_carbon.getCopy();
986 ours.server=ComboAddress(address, 2003);
987 if(ourName)
988 ours.ourname=*ourName;
989 if(interval)
990 ours.interval=*interval;
991 if(!ours.interval)
992 ours.interval=1;
993 g_carbon.setState(ours);
994 });
995
50bed881 996 g_lua.writeFunction("webserver", [client](const std::string& address, const std::string& password) {
f758857a 997 setLuaSideEffect();
50bed881 998 if(client)
999 return;
1000 ComboAddress local(address);
1001 try {
8d06661a 1002 int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
50bed881 1003 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
1004 SBind(sock, local);
1005 SListen(sock, 5);
1006 auto launch=[sock, local, password]() {
1007 thread t(dnsdistWebserverThread, sock, local, password);
1008 t.detach();
1009 };
1010 if(g_launchWork)
1011 g_launchWork->push_back(launch);
1012 else
1013 launch();
1014 }
1015 catch(std::exception& e) {
0c8e7c58 1016 g_outputBuffer="Unable to bind to webserver socket on " + local.toStringWithPort() + ": " + e.what();
50bed881 1017 errlog("Unable to bind to webserver socket on %s: %s", local.toStringWithPort(), e.what());
1018 }
1019
1020 });
df111b53 1021 g_lua.writeFunction("controlSocket", [client](const std::string& str) {
f758857a 1022 setLuaSideEffect();
df111b53 1023 ComboAddress local(str, 5199);
1024
1025 if(client) {
1026 g_serverControl = local;
1027 return;
1028 }
1029
1030 try {
8d06661a 1031 int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
df111b53 1032 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
1033 SBind(sock, local);
1034 SListen(sock, 5);
2e72cc0e 1035 auto launch=[sock, local]() {
1036 thread t(controlThread, sock, local);
1037 t.detach();
1038 };
1039 if(g_launchWork)
1040 g_launchWork->push_back(launch);
1041 else
1042 launch();
1043
df111b53 1044 }
1045 catch(std::exception& e) {
0c8e7c58 1046 g_outputBuffer="Unable to bind to control socket on " + local.toStringWithPort() + ": " + e.what();
df111b53 1047 errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what());
1048 }
1049 });
1050
03ebf8b2 1051
d179bc47 1052 g_lua.writeFunction("topClients", [](boost::optional<unsigned int> top_) {
f758857a 1053 setLuaNoSideEffect();
d179bc47 1054 auto top = top_.get_value_or(10);
0e5b3cff 1055 map<ComboAddress, int,ComboAddress::addressOnlyLessThan > counts;
1056 unsigned int total=0;
0e41337b
RG
1057 {
1058 ReadLock rl(&g_rings.queryLock);
1059 for(const auto& c : g_rings.queryRing) {
1060 counts[c.requestor]++;
1061 total++;
1062 }
0e5b3cff 1063 }
1064 vector<pair<int, ComboAddress>> rcounts;
a73fc812 1065 rcounts.reserve(counts.size());
0e5b3cff 1066 for(const auto& c : counts)
1067 rcounts.push_back(make_pair(c.second, c.first));
1068
1069 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
1070 const decltype(rcounts)::value_type& b) {
1071 return b.first < a.first;
1072 });
1073 unsigned int count=1, rest=0;
1074 boost::format fmt("%4d %-40s %4d %4.1f%%\n");
1075 for(const auto& rc : rcounts) {
1076 if(count==top+1)
1077 rest+=rc.first;
1078 else
1079 g_outputBuffer += (fmt % (count++) % rc.second.toString() % rc.first % (100.0*rc.first/total)).str();
1080 }
6a62c0e3 1081 g_outputBuffer += (fmt % (count) % "Rest" % rest % (total > 0 ? 100.0*rest/total : 100.0)).str();
0e5b3cff 1082 });
1083
df111b53 1084 g_lua.writeFunction("getTopQueries", [](unsigned int top, boost::optional<int> labels) {
f758857a 1085 setLuaNoSideEffect();
df111b53 1086 map<DNSName, int> counts;
1087 unsigned int total=0;
1088 if(!labels) {
0e41337b 1089 ReadLock rl(&g_rings.queryLock);
df111b53 1090 for(const auto& a : g_rings.queryRing) {
0ba5eecf 1091 counts[a.name]++;
df111b53 1092 total++;
1093 }
1094 }
1095 else {
1096 unsigned int lab = *labels;
0e41337b 1097 ReadLock rl(&g_rings.queryLock);
df111b53 1098 for(auto a : g_rings.queryRing) {
0ba5eecf 1099 a.name.trimToLabels(lab);
1100 counts[a.name]++;
df111b53 1101 total++;
1102 }
df111b53 1103 }
cffde2fd 1104 // cout<<"Looked at "<<total<<" queries, "<<counts.size()<<" different ones"<<endl;
df111b53 1105 vector<pair<int, DNSName>> rcounts;
3821bfe2 1106 rcounts.reserve(counts.size());
df111b53 1107 for(const auto& c : counts)
1108 rcounts.push_back(make_pair(c.second, c.first));
1109
1110 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
1111 const decltype(rcounts)::value_type& b) {
1112 return b.first < a.first;
1113 });
1114
1115 std::unordered_map<int, vector<boost::variant<string,double>>> ret;
1116 unsigned int count=1, rest=0;
1117 for(const auto& rc : rcounts) {
1118 if(count==top+1)
1119 rest+=rc.first;
1120 else
1121 ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
1122 }
6a62c0e3 1123 ret.insert({count, {"Rest", rest, total > 0 ? 100.0*rest/total : 100.0}});
df111b53 1124 return ret;
1125
1126 });
1127
a78a3a0e 1128 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 1129
520eb5a0 1130
62edea30 1131
520eb5a0 1132 g_lua.writeFunction("getResponseRing", []() {
f758857a 1133 setLuaNoSideEffect();
520eb5a0 1134 decltype(g_rings.respRing) ring;
1135 {
1136 std::lock_guard<std::mutex> lock(g_rings.respMutex);
1137 ring = g_rings.respRing;
1138 }
1139 vector<std::unordered_map<string, boost::variant<string, unsigned int> > > ret;
1140 ret.reserve(ring.size());
1141 decltype(ret)::value_type item;
1142 for(const auto& r : ring) {
1143 item["name"]=r.name.toString();
1144 item["qtype"]=r.qtype;
3fcaeeac 1145 item["rcode"]=r.dh.rcode;
520eb5a0 1146 item["usec"]=r.usec;
1147 ret.push_back(item);
1148 }
1149 return ret;
1150 });
f5b58807 1151
df111b53 1152 g_lua.writeFunction("getTopResponses", [](unsigned int top, unsigned int kind, boost::optional<int> labels) {
2d11d1b2 1153 return getGenResponses(top, labels, [kind](const Rings::Response& r) { return r.dh.rcode == kind; });
1154 });
df111b53 1155
2d11d1b2 1156 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 1157
df111b53 1158
2d11d1b2 1159 g_lua.writeFunction("getSlowResponses", [](unsigned int top, unsigned int msec, boost::optional<int> labels) {
1160 return getGenResponses(top, labels, [msec](const Rings::Response& r) { return r.usec > msec*1000; });
df111b53 1161 });
62edea30 1162
2d11d1b2 1163
2a05b4a9 1164 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 1165
1166
1167 g_lua.writeFunction("showResponseLatency", []() {
f758857a 1168 setLuaNoSideEffect();
df111b53 1169 map<double, unsigned int> histo;
1170 double bin=100;
1171 for(int i=0; i < 15; ++i) {
1172 histo[bin];
1173 bin*=2;
1174 }
1175
1176 double totlat=0;
1177 int size=0;
1178 {
1179 std::lock_guard<std::mutex> lock(g_rings.respMutex);
1180 for(const auto& r : g_rings.respRing) {
1181 ++size;
1182 auto iter = histo.lower_bound(r.usec);
1183 if(iter != histo.end())
1184 iter->second++;
1185 else
1186 histo.rbegin()++;
1187 totlat+=r.usec;
1188 }
1189 }
1190
e12b3374
RG
1191 if (size == 0) {
1192 g_outputBuffer = "No traffic yet.\n";
1193 return;
1194 }
1195
df111b53 1196 g_outputBuffer = (boost::format("Average response latency: %.02f msec\n") % (0.001*totlat/size)).str();
1197 double highest=0;
1198
1199 for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
1200 highest=std::max(highest, iter->second*1.0);
1201 }
1202 boost::format fmt("%7.2f\t%s\n");
1203 g_outputBuffer += (fmt % "msec" % "").str();
1204
1205 for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
1206 int stars = (70.0 * iter->second/highest);
1207 char c='*';
1208 if(!stars && iter->second) {
1209 stars=1; // you get 1 . to show something is there..
1210 if(70.0*iter->second/highest > 0.5)
1211 c=':';
1212 else
1213 c='.';
1214 }
1215 g_outputBuffer += (fmt % (iter->first/1000.0) % string(stars, c)).str();
1216 }
1217 });
1218
1219 g_lua.writeFunction("newQPSLimiter", [](int rate, int burst) { return QPSLimiter(rate, burst); });
1220 g_lua.registerFunction("check", &QPSLimiter::check);
1221
1222
1223 g_lua.writeFunction("makeKey", []() {
f758857a 1224 setLuaNoSideEffect();
df111b53 1225 g_outputBuffer="setKey("+newKey()+")\n";
1226 });
1227
1228 g_lua.writeFunction("setKey", [](const std::string& key) {
f758857a 1229 setLuaSideEffect();
845a82c4 1230 if(B64Decode(key, g_key) < 0) {
1231 g_outputBuffer=string("Unable to decode ")+key+" as Base64";
1232 errlog("%s", g_outputBuffer);
1233 }
df111b53 1234 });
1235
1236
107d4911 1237 g_lua.writeFunction("testCrypto", [](boost::optional<string> optTestMsg)
df111b53 1238 {
f758857a 1239 setLuaNoSideEffect();
a8eafc52 1240#ifdef HAVE_LIBSODIUM
df111b53 1241 try {
107d4911
RG
1242 string testmsg;
1243
1244 if (optTestMsg) {
1245 testmsg = *optTestMsg;
1246 }
1247 else {
1248 testmsg = "testStringForCryptoTests";
1249 }
1250
df111b53 1251 SodiumNonce sn, sn2;
1252 sn.init();
1253 sn2=sn;
1254 string encrypted = sodEncryptSym(testmsg, g_key, sn);
1255 string decrypted = sodDecryptSym(encrypted, g_key, sn2);
1256
2e72cc0e 1257 sn.increment();
1258 sn2.increment();
1259
1260 encrypted = sodEncryptSym(testmsg, g_key, sn);
1261 decrypted = sodDecryptSym(encrypted, g_key, sn2);
1262
df111b53 1263 if(testmsg == decrypted)
1264 g_outputBuffer="Everything is ok!\n";
1265 else
1266 g_outputBuffer="Crypto failed..\n";
1267
1268 }
1269 catch(...) {
1270 g_outputBuffer="Crypto failed..\n";
a8eafc52
MK
1271 }
1272#else
1273 g_outputBuffer="Crypto not available.\n";
1274#endif
1275 });
df111b53 1276
3f6d07a4
RG
1277 g_lua.writeFunction("setTCPRecvTimeout", [](int timeout) { g_tcpRecvTimeout=timeout; });
1278
1279 g_lua.writeFunction("setTCPSendTimeout", [](int timeout) { g_tcpSendTimeout=timeout; });
2a817e5a 1280
e41f8165
RG
1281 g_lua.writeFunction("setMaxUDPOutstanding", [](uint16_t max) {
1282 if (!g_configurationDone) {
1283 g_maxOutstanding = max;
1284 } else {
1285 g_outputBuffer="Max UDP outstanding cannot be altered at runtime!\n";
1286 }
1287 });
1288
497a6e3a
RG
1289 /* DNSQuestion bindings */
1290 /* PowerDNS DNSQuestion compat */
1291 g_lua.registerMember<const ComboAddress (DNSQuestion::*)>("localaddr", [](const DNSQuestion& dq) -> const ComboAddress { return *dq.local; }, [](DNSQuestion& dq, const ComboAddress newLocal) { (void) newLocal; });
1292 g_lua.registerMember<const DNSName (DNSQuestion::*)>("qname", [](const DNSQuestion& dq) -> const DNSName { return *dq.qname; }, [](DNSQuestion& dq, const DNSName newName) { (void) newName; });
1293 g_lua.registerMember<uint16_t (DNSQuestion::*)>("qtype", [](const DNSQuestion& dq) -> uint16_t { return dq.qtype; }, [](DNSQuestion& dq, uint16_t newType) { (void) newType; });
3b069df2 1294 g_lua.registerMember<uint16_t (DNSQuestion::*)>("qclass", [](const DNSQuestion& dq) -> uint16_t { return dq.qclass; }, [](DNSQuestion& dq, uint16_t newClass) { (void) newClass; });
497a6e3a
RG
1295 g_lua.registerMember<int (DNSQuestion::*)>("rcode", [](const DNSQuestion& dq) -> int { return dq.dh->rcode; }, [](DNSQuestion& dq, int newRCode) { dq.dh->rcode = newRCode; });
1296 g_lua.registerMember<const ComboAddress (DNSQuestion::*)>("remoteaddr", [](const DNSQuestion& dq) -> const ComboAddress { return *dq.remote; }, [](DNSQuestion& dq, const ComboAddress newRemote) { (void) newRemote; });
1297 /* DNSDist DNSQuestion */
1298 g_lua.registerMember("dh", &DNSQuestion::dh);
1299 g_lua.registerMember<uint16_t (DNSQuestion::*)>("len", [](const DNSQuestion& dq) -> uint16_t { return dq.len; }, [](DNSQuestion& dq, uint16_t newlen) { dq.len = newlen; });
1300 g_lua.registerMember<size_t (DNSQuestion::*)>("size", [](const DNSQuestion& dq) -> size_t { return dq.size; }, [](DNSQuestion& dq, size_t newSize) { (void) newSize; });
1301 g_lua.registerMember<bool (DNSQuestion::*)>("tcp", [](const DNSQuestion& dq) -> bool { return dq.tcp; }, [](DNSQuestion& dq, bool newTcp) { (void) newTcp; });
1302
e41f8165
RG
1303 g_lua.writeFunction("setMaxTCPClientThreads", [](uint64_t max) { g_maxTCPClientThreads = max; });
1304
886e2cf2
RG
1305 g_lua.writeFunction("setCacheCleaningDelay", [](uint32_t delay) { g_cacheCleaningDelay = delay; });
1306
ca404e94
RG
1307 g_lua.writeFunction("setECSSourcePrefixV4", [](uint16_t prefix) { g_ECSSourcePrefixV4=prefix; });
1308
1309 g_lua.writeFunction("setECSSourcePrefixV6", [](uint16_t prefix) { g_ECSSourcePrefixV6=prefix; });
1310
1311 g_lua.writeFunction("setECSOverride", [](bool override) { g_ECSOverride=override; });
1312
2a817e5a 1313 g_lua.writeFunction("dumpStats", [] {
f758857a 1314 setLuaNoSideEffect();
2a817e5a 1315 vector<string> leftcolumn, rightcolumn;
1316
1317 boost::format fmt("%-23s\t%+11s");
1318 g_outputBuffer.clear();
1319 auto entries = g_stats.entries;
1320 sort(entries.begin(), entries.end(),
1321 [](const decltype(entries)::value_type& a, const decltype(entries)::value_type& b) {
1322 return a.first < b.first;
1323 });
1324 boost::format flt(" %9.1f");
1325 for(const auto& e : entries) {
1326 string second;
1327 if(const auto& val = boost::get<DNSDistStats::stat_t*>(&e.second))
1328 second=std::to_string((*val)->load());
1329 else if (const auto& val = boost::get<double*>(&e.second))
1330 second=(flt % (**val)).str();
1331 else
1332 second=std::to_string((*boost::get<DNSDistStats::statfunction_t>(&e.second))(e.first));
1333
1334 if(leftcolumn.size() < g_stats.entries.size()/2)
1335 leftcolumn.push_back((fmt % e.first % second).str());
1336 else
1337 rightcolumn.push_back((fmt % e.first % second).str());
1338 }
1339
1340 auto leftiter=leftcolumn.begin(), rightiter=rightcolumn.begin();
1341 boost::format clmn("%|0t|%1% %|39t|%2%\n");
1342
1343 for(;leftiter != leftcolumn.end() || rightiter != rightcolumn.end();) {
1344 string lentry, rentry;
1345 if(leftiter!= leftcolumn.end()) {
1346 lentry = *leftiter;
1347 leftiter++;
1348 }
1349 if(rightiter!= rightcolumn.end()) {
1350 rentry = *rightiter;
1351 rightiter++;
1352 }
1353 g_outputBuffer += (clmn % lentry % rentry).str();
1354 }
1355 });
80a216c9 1356
886e2cf2 1357 moreLua(client);
df111b53 1358
839f3021 1359 std::ifstream ifs(config);
2e72cc0e 1360 if(!ifs)
839f3021 1361 warnlog("Unable to read configuration from '%s'", config);
2e72cc0e 1362 else
839f3021 1363 infolog("Read configuration from '%s'", config);
df111b53 1364
1365 g_lua.executeCode(ifs);
2e72cc0e 1366 auto ret=*g_launchWork;
1367 delete g_launchWork;
1368 g_launchWork=0;
1369 return ret;
df111b53 1370}