]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-lua.cc
we might have more DiffTimes
[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>
8
9using std::thread;
10
2e72cc0e 11static vector<std::function<void(void)>>* g_launchWork;
12
d8d85a30 13class LuaAction : public DNSAction
14{
15public:
16 typedef std::function<std::tuple<int, string>(const ComboAddress& remote, const DNSName& qname, uint16_t qtype, dnsheader* dh, int len)> func_t;
17 LuaAction(LuaAction::func_t func) : d_func(func)
18 {}
19
20 Action operator()(const ComboAddress& remote, const DNSName& qname, uint16_t qtype, dnsheader* dh, int len, string* ruleresult) const
21 {
22 auto ret = d_func(remote, qname, qtype, dh, len);
23 if(ruleresult)
24 *ruleresult=std::get<1>(ret);
25 return (Action)std::get<0>(ret);
26 }
27
28 string toString() const
29 {
30 return "Lua script";
31 }
32
33private:
34 func_t d_func;
35};
36
6bba426c 37typedef boost::variant<string,vector<pair<int, string>>, std::shared_ptr<DNSRule> > luadnsrule_t;
38std::shared_ptr<DNSRule> makeRule(const luadnsrule_t& var)
d8d85a30 39{
6bba426c 40 if(auto src = boost::get<std::shared_ptr<DNSRule>>(&var))
41 return *src;
42
d8d85a30 43 SuffixMatchNode smn;
44 NetmaskGroup nmg;
6bba426c 45
d8d85a30 46 auto add=[&](string src) {
47 try {
5ad8b87a 48 nmg.addMask(src); // need to try mask first, all masks are domain names!
d8d85a30 49 } catch(...) {
5ad8b87a 50 smn.add(DNSName(src));
d8d85a30 51 }
52 };
53 if(auto src = boost::get<string>(&var))
54 add(*src);
55 else {
56 for(auto& a : boost::get<vector<pair<int, string>>>(var)) {
57 add(a.second);
58 }
59 }
60 if(nmg.empty())
61 return std::make_shared<SuffixMatchNodeRule>(smn);
62 else
63 return std::make_shared<NetmaskGroupRule>(nmg);
64}
65
839f3021 66vector<std::function<void(void)>> setupLua(bool client, const std::string& config)
df111b53 67{
2e72cc0e 68 g_launchWork= new vector<std::function<void(void)>>();
a6e02424 69 typedef std::unordered_map<std::string, boost::variant<bool, std::string, vector<pair<int, std::string> > > > newserver_t;
e48090d1 70
d8d85a30 71 g_lua.writeVariable("DNSAction", std::unordered_map<string,int>{
72 {"Drop", (int)DNSAction::Action::Drop},
73 {"Nxdomain", (int)DNSAction::Action::Nxdomain},
74 {"Spoof", (int)DNSAction::Action::Spoof},
75 {"Allow", (int)DNSAction::Action::Allow},
76 {"HeaderModify", (int)DNSAction::Action::HeaderModify},
77 {"Pool", (int)DNSAction::Action::Pool},
78 {"None",(int)DNSAction::Action::Pool}}
79 );
80
81
df111b53 82 g_lua.writeFunction("newServer",
839f3021 83 [client](boost::variant<string,newserver_t> pvars, boost::optional<int> qps)
df111b53 84 {
2e72cc0e 85 if(client) {
b4fd86c3 86 return std::make_shared<DownstreamState>(ComboAddress());
2e72cc0e 87 }
df111b53 88 if(auto address = boost::get<string>(&pvars)) {
cbbd1533 89 std::shared_ptr<DownstreamState> ret;
90 try {
91 ret=std::make_shared<DownstreamState>(ComboAddress(*address, 53));
92 }
93 catch(std::exception& e) {
94 g_outputBuffer="Error creating new server: "+string(e.what());
95 errlog("Error creating new server with address %s: %s", *address, e.what());
96 return ret;
97 }
2e72cc0e 98
df111b53 99 if(qps) {
100 ret->qps=QPSLimiter(*qps, *qps);
101 }
ecbe9133 102 g_dstates.modify([ret](servers_t& servers) {
103 servers.push_back(ret);
104 std::stable_sort(servers.begin(), servers.end(), [](const decltype(ret)& a, const decltype(ret)& b) {
105 return a->order < b->order;
106 });
107
108 });
2e72cc0e 109
110 if(g_launchWork) {
111 g_launchWork->push_back([ret]() {
112 ret->tid = move(thread(responderThread, ret));
113 });
114 }
115 else {
116 ret->tid = move(thread(responderThread, ret));
117 }
118
df111b53 119 return ret;
120 }
839f3021 121 auto vars=boost::get<newserver_t>(pvars);
cbbd1533 122 std::shared_ptr<DownstreamState> ret;
123 try {
124 ret=std::make_shared<DownstreamState>(ComboAddress(boost::get<string>(vars["address"]), 53));
125 }
126 catch(std::exception& e) {
127 g_outputBuffer="Error creating new server: "+string(e.what());
128 errlog("Error creating new server with address %s: %s", boost::get<string>(vars["address"]), e.what());
129 return ret;
130 }
2e72cc0e 131
df111b53 132 if(vars.count("qps")) {
839f3021 133 int qps=boost::lexical_cast<int>(boost::get<string>(vars["qps"]));
134 ret->qps=QPSLimiter(qps, qps);
df111b53 135 }
136
137 if(vars.count("pool")) {
839f3021 138 if(auto* pool = boost::get<string>(&vars["pool"]))
139 ret->pools.insert(*pool);
140 else {
141 auto* pools = boost::get<vector<pair<int, string> > >(&vars["pool"]);
142 for(auto& p : *pools)
143 ret->pools.insert(p.second);
144 }
df111b53 145 }
146
147 if(vars.count("order")) {
839f3021 148 ret->order=boost::lexical_cast<int>(boost::get<string>(vars["order"]));
df111b53 149 }
150
151 if(vars.count("weight")) {
839f3021 152 ret->weight=boost::lexical_cast<int>(boost::get<string>(vars["weight"]));
df111b53 153 }
154
3f6d07a4
RG
155 if(vars.count("retries")) {
156 ret->retries=boost::lexical_cast<int>(boost::get<string>(vars["retries"]));
157 }
158
159 if(vars.count("tcpSendTimeout")) {
160 ret->tcpSendTimeout=boost::lexical_cast<int>(boost::get<string>(vars["tcpSendTimeout"]));
161 }
162
163 if(vars.count("tcpRecvTimeout")) {
164 ret->tcpRecvTimeout=boost::lexical_cast<int>(boost::get<string>(vars["tcpRecvTimeout"]));
165 }
166
18eeccc9
RG
167 if(vars.count("name")) {
168 ret->name=boost::get<string>(vars["name"]);
169 }
170
ad485896
RG
171 if(vars.count("checkName")) {
172 ret->checkName=DNSName(boost::get<string>(vars["checkName"]));
173 }
174
175 if(vars.count("checkType")) {
176 ret->checkType=boost::get<string>(vars["checkType"]);
177 }
178
a6e02424
RG
179 if(vars.count("mustResolve")) {
180 ret->mustResolve=boost::get<bool>(vars["mustResolve"]);
181 }
182
2e72cc0e 183 if(g_launchWork) {
184 g_launchWork->push_back([ret]() {
185 ret->tid = move(thread(responderThread, ret));
186 });
187 }
188 else {
189 ret->tid = move(thread(responderThread, ret));
190 }
df111b53 191
ecbe9133 192 auto states = g_dstates.getCopy();
e5a14b2b 193 states.push_back(ret);
194 std::stable_sort(states.begin(), states.end(), [](const decltype(ret)& a, const decltype(ret)& b) {
df111b53 195 return a->order < b->order;
196 });
ecbe9133 197 g_dstates.setState(states);
df111b53 198 return ret;
199 } );
200
d8d85a30 201
0940e4eb 202 g_lua.writeFunction("addAnyTCRule", []() {
203 auto rules=g_rulactions.getCopy();
204 rules.push_back({ std::make_shared<QTypeRule>(0xff), std::make_shared<TCAction>()});
205 g_rulactions.setState(rules);
206 });
df111b53 207
0940e4eb 208 g_lua.writeFunction("rmRule", [](unsigned int num) {
209 auto rules = g_rulactions.getCopy();
210 if(num >= rules.size()) {
211 g_outputBuffer = "Error: attempt to delete non-existing rule\n";
212 return;
213 }
214 rules.erase(rules.begin()+num);
215 g_rulactions.setState(rules);
216 });
217
b4fd86c3 218 g_lua.writeFunction("topRule", []() {
219 auto rules = g_rulactions.getCopy();
220 if(rules.empty())
221 return;
222 auto subject = *rules.rbegin();
223 rules.erase(std::prev(rules.end()));
224 rules.insert(rules.begin(), subject);
225 g_rulactions.setState(rules);
226 });
0940e4eb 227 g_lua.writeFunction("mvRule", [](unsigned int from, unsigned int to) {
228 auto rules = g_rulactions.getCopy();
229 if(from >= rules.size() || to > rules.size()) {
230 g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
231 return;
232 }
0940e4eb 233
234 auto subject = rules[from];
235 rules.erase(rules.begin()+from);
236 if(to == rules.size())
237 rules.push_back(subject);
238 else {
239 if(from < to)
240 --to;
241 rules.insert(rules.begin()+to, subject);
242 }
243 g_rulactions.setState(rules);
244 });
df111b53 245
246
247 g_lua.writeFunction("rmServer",
248 [](boost::variant<std::shared_ptr<DownstreamState>, int> var)
249 {
ecbe9133 250 auto states = g_dstates.getCopy();
df111b53 251 if(auto* rem = boost::get<shared_ptr<DownstreamState>>(&var))
e5a14b2b 252 states.erase(remove(states.begin(), states.end(), *rem), states.end());
df111b53 253 else
e5a14b2b 254 states.erase(states.begin() + boost::get<int>(var));
ecbe9133 255 g_dstates.setState(states);
df111b53 256 } );
257
258
259 g_lua.writeFunction("setServerPolicy", [](ServerPolicy policy) {
e5a14b2b 260 g_policy.setState(policy);
df111b53 261 });
df111b53 262 g_lua.writeFunction("setServerPolicyLua", [](string name, policy_t policy) {
e5a14b2b 263 g_policy.setState(ServerPolicy{name, policy});
df111b53 264 });
265
266 g_lua.writeFunction("showServerPolicy", []() {
ecbe9133 267 g_outputBuffer=g_policy.getLocal()->name+"\n";
df111b53 268 });
269
6ad8b29a 270 g_lua.writeFunction("truncateTC", [](bool tc) { g_truncateTC=tc; });
df111b53 271
272 g_lua.registerMember("name", &ServerPolicy::name);
273 g_lua.registerMember("policy", &ServerPolicy::policy);
274 g_lua.writeFunction("newServerPolicy", [](string name, policy_t policy) { return ServerPolicy{name, policy};});
275 g_lua.writeVariable("firstAvailable", ServerPolicy{"firstAvailable", firstAvailable});
276 g_lua.writeVariable("roundrobin", ServerPolicy{"roundrobin", roundrobin});
277 g_lua.writeVariable("wrandom", ServerPolicy{"wrandom", wrandom});
a7f3108c 278 g_lua.writeVariable("whashed", ServerPolicy{"whashed", whashed});
df111b53 279 g_lua.writeVariable("leastOutstanding", ServerPolicy{"leastOutstanding", leastOutstanding});
280 g_lua.writeFunction("addACL", [](const std::string& domain) {
e5a14b2b 281 g_ACL.modify([domain](NetmaskGroup& nmg) { nmg.addMask(domain); });
df111b53 282 });
2e72cc0e 283
5949b95b 284 g_lua.writeFunction("setLocal", [client](const std::string& addr, boost::optional<bool> doTCP) {
285 if(client)
286 return;
287 try {
288 ComboAddress loc(addr, 53);
289 g_locals.clear();
290 g_locals.push_back({loc, doTCP ? *doTCP : true}); /// only works pre-startup, so no sync necessary
291 }
292 catch(std::exception& e) {
293 g_outputBuffer="Error: "+string(e.what())+"\n";
294 }
295 });
296
652a7355 297 g_lua.writeFunction("addLocal", [client](const std::string& addr, boost::optional<bool> doTCP) {
2e72cc0e 298 if(client)
299 return;
300 try {
301 ComboAddress loc(addr, 53);
652a7355 302 g_locals.push_back({loc, doTCP ? *doTCP : true}); /// only works pre-startup, so no sync necessary
2e72cc0e 303 }
304 catch(std::exception& e) {
305 g_outputBuffer="Error: "+string(e.what())+"\n";
306 }
307 });
e4944ea0 308 g_lua.writeFunction("setACL", [](boost::variant<string,vector<pair<int, string>>> inp) {
e5a14b2b 309 NetmaskGroup nmg;
e4944ea0 310 if(auto str = boost::get<string>(&inp)) {
311 nmg.addMask(*str);
312 }
313 else for(const auto& p : boost::get<vector<pair<int,string>>>(inp)) {
e5a14b2b 314 nmg.addMask(p.second);
cffde2fd 315 }
316 g_ACL.setState(nmg);
df111b53 317 });
318 g_lua.writeFunction("showACL", []() {
319 vector<string> vec;
cffde2fd 320
e5a14b2b 321 g_ACL.getCopy().toStringVector(&vec);
cffde2fd 322
df111b53 323 for(const auto& s : vec)
cffde2fd 324 g_outputBuffer+=s+"\n";
325
df111b53 326 });
327 g_lua.writeFunction("shutdown", []() { _exit(0);} );
328
329
ecbe9133 330 g_lua.writeFunction("addDomainBlock", [](const std::string& domain) {
0940e4eb 331 SuffixMatchNode smn;
1f9100a7 332 smn.add(DNSName(domain));
0940e4eb 333 g_rulactions.modify([smn](decltype(g_rulactions)::value_type& rulactions) {
334 rulactions.push_back({
335 std::make_shared<SuffixMatchNodeRule>(smn),
336 std::make_shared<DropAction>() });
337 });
338
ecbe9133 339 });
df111b53 340 g_lua.writeFunction("showServers", []() {
341 try {
342 ostringstream ret;
2dd8d58f 343 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 %13%" );
18eeccc9
RG
344 // 1 2 3 4 5 6 7 8 9 10 11 12
345 ret << (fmt % "#" % "Name" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Pools") << endl;
df111b53 346
347 uint64_t totQPS{0}, totQueries{0}, totDrops{0};
348 int counter=0;
ecbe9133 349 auto states = g_dstates.getCopy();
e5a14b2b 350 for(const auto& s : states) {
df111b53 351 string status;
352 if(s->availability == DownstreamState::Availability::Up)
353 status = "UP";
354 else if(s->availability == DownstreamState::Availability::Down)
355 status = "DOWN";
356 else
357 status = (s->upStatus ? "up" : "down");
358
359 string pools;
360 for(auto& p : s->pools) {
361 if(!pools.empty())
362 pools+=" ";
363 pools+=p;
364 }
365
18eeccc9 366 ret << (fmt % counter % s->name % s->remote.toStringWithPort() %
df111b53 367 status %
368 s->queryLoad % s->qps.getRate() % s->order % s->weight % s->queries.load() % s->reuseds.load() % (s->dropRate) % (s->latencyUsec/1000.0) % pools) << endl;
369
370 totQPS += s->queryLoad;
371 totQueries += s->queries.load();
372 totDrops += s->reuseds.load();
373 ++counter;
374 }
18eeccc9 375 ret<< (fmt % "All" % "" % "" % ""
df111b53 376 %
377 (double)totQPS % "" % "" % "" % totQueries % totDrops % "" % "" % "" ) << endl;
378
379 g_outputBuffer=ret.str();
380 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
381 });
382
6bba426c 383 g_lua.writeFunction("addLuaAction", [](luadnsrule_t var, LuaAction::func_t func)
d8d85a30 384 {
385 auto rule=makeRule(var);
386 g_rulactions.modify([rule,func](decltype(g_rulactions)::value_type& rulactions){
387 rulactions.push_back({rule,
388 std::make_shared<LuaAction>(func)});
389 });
390 });
df111b53 391
d8d85a30 392
6bba426c 393 g_lua.writeFunction("NoRecurseAction", []() {
394 return std::shared_ptr<DNSAction>(new NoRecurseAction);
395 });
396
ae3dfa48 397 g_lua.writeFunction("DropAction", []() {
398 return std::shared_ptr<DNSAction>(new DropAction);
399 });
400
2332b03c 401 g_lua.writeFunction("TCAction", []() {
402 return std::shared_ptr<DNSAction>(new TCAction);
403 });
404
f39b7598
RG
405 g_lua.writeFunction("DisableValidationAction", []() {
406 return std::shared_ptr<DNSAction>(new DisableValidationAction);
407 });
408
ae3dfa48 409
1b726acf 410 g_lua.writeFunction("MaxQPSIPRule", [](unsigned int qps, boost::optional<int> ipv4trunc, boost::optional<int> ipv6trunc) {
411 return std::shared_ptr<DNSRule>(new MaxQPSIPRule(qps, ipv4trunc.get_value_or(32), ipv6trunc.get_value_or(64)));
6bba426c 412 });
413
414
2332b03c 415 g_lua.writeFunction("MaxQPSRule", [](unsigned int qps, boost::optional<int> burst) {
416 if(!burst)
417 return std::shared_ptr<DNSRule>(new MaxQPSRule(qps));
418 else
419 return std::shared_ptr<DNSRule>(new MaxQPSRule(qps, *burst));
420 });
421
422
6bba426c 423 g_lua.writeFunction("addAction", [](luadnsrule_t var, std::shared_ptr<DNSAction> ea)
424 {
425 auto rule=makeRule(var);
426 g_rulactions.modify([rule, ea](decltype(g_rulactions)::value_type& rulactions){
427 rulactions.push_back({rule, ea});
428 });
429 });
430
431
432 g_lua.writeFunction("addPoolRule", [](luadnsrule_t var, string pool) {
d8d85a30 433 auto rule=makeRule(var);
434 g_rulactions.modify([rule, pool](decltype(g_rulactions)::value_type& rulactions) {
0940e4eb 435 rulactions.push_back({
d8d85a30 436 rule,
437 std::make_shared<PoolAction>(pool) });
ecbe9133 438 });
df111b53 439 });
0570f37c 440
6bba426c 441 g_lua.writeFunction("addNoRecurseRule", [](luadnsrule_t var) {
0570f37c 442 auto rule=makeRule(var);
443 g_rulactions.modify([rule](decltype(g_rulactions)::value_type& rulactions) {
444 rulactions.push_back({
445 rule,
446 std::make_shared<NoRecurseAction>() });
447 });
448 });
449
f39b7598
RG
450 g_lua.writeFunction("addDisableValidationRule", [](luadnsrule_t var) {
451 auto rule=makeRule(var);
452 g_rulactions.modify([rule](decltype(g_rulactions)::value_type& rulactions) {
453 rulactions.push_back({
454 rule,
455 std::make_shared<DisableValidationAction>() });
456 });
457 });
458
0570f37c 459
6bba426c 460 g_lua.writeFunction("addQPSPoolRule", [](luadnsrule_t var, int limit, string pool) {
d8d85a30 461 auto rule = makeRule(var);
462 g_rulactions.modify([rule, pool,limit](decltype(g_rulactions)::value_type& rulactions) {
463 rulactions.push_back({
464 rule,
465 std::make_shared<QPSPoolAction>(limit, pool) });
466 });
fd010ca3 467 });
df111b53 468
520eb5a0 469 g_lua.writeFunction("setDNSSECPool", [](const std::string& pool) {
470 g_rulactions.modify([pool](decltype(g_rulactions)::value_type& rulactions) {
471 rulactions.push_back({std::make_shared<DNSSECRule>(),
472 std::make_shared<PoolAction>(pool)});
473 });
474 });
df111b53 475
6bba426c 476 g_lua.writeFunction("addQPSLimit", [](luadnsrule_t var, int lim) {
d8d85a30 477 auto rule = makeRule(var);
478 g_rulactions.modify([lim,rule](decltype(g_rulactions)::value_type& rulactions) {
479 rulactions.push_back({rule,
480 std::make_shared<QPSAction>(lim)});
481 });
df111b53 482 });
d8d85a30 483
6bba426c 484 g_lua.writeFunction("addDelay", [](luadnsrule_t var, int msec) {
7b3865cd 485 auto rule = makeRule(var);
486 g_rulactions.modify([msec,rule](decltype(g_rulactions)::value_type& rulactions) {
487 rulactions.push_back({rule,
488 std::make_shared<DelayAction>(msec)});
489 });
490 });
df111b53 491
df111b53 492
0940e4eb 493 g_lua.writeFunction("showRules", []() {
494 boost::format fmt("%-3d %9d %-50s %s\n");
495 g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
496 int num=0;
497 for(const auto& lim : g_rulactions.getCopy()) {
498 string name = lim.first->toString();
499 g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
df111b53 500 ++num;
501 }
502 });
503
df111b53 504 g_lua.writeFunction("getServers", []() {
505 vector<pair<int, std::shared_ptr<DownstreamState> > > ret;
506 int count=1;
e5a14b2b 507 for(const auto& s : g_dstates.getCopy()) {
df111b53 508 ret.push_back(make_pair(count++, s));
509 }
510 return ret;
511 });
512
da4e7813 513 g_lua.writeFunction("getPoolServers", [](string pool) {
514 return getDownstreamCandidates(g_dstates.getCopy(), pool);
515 });
516
e5a14b2b 517 g_lua.writeFunction("getServer", [](int i) { return g_dstates.getCopy().at(i); });
df111b53 518
df111b53 519 g_lua.registerFunction<void(DownstreamState::*)(int)>("setQPS", [](DownstreamState& s, int lim) { s.qps = lim ? QPSLimiter(lim, lim) : QPSLimiter(); });
520 g_lua.registerFunction<void(DownstreamState::*)(string)>("addPool", [](DownstreamState& s, string pool) { s.pools.insert(pool);});
521 g_lua.registerFunction<void(DownstreamState::*)(string)>("rmPool", [](DownstreamState& s, string pool) { s.pools.erase(pool);});
522
523 g_lua.registerFunction<void(DownstreamState::*)()>("getOutstanding", [](const DownstreamState& s) { g_outputBuffer=std::to_string(s.outstanding.load()); });
524
525
526 g_lua.registerFunction("isUp", &DownstreamState::isUp);
527 g_lua.registerFunction("setDown", &DownstreamState::setDown);
528 g_lua.registerFunction("setUp", &DownstreamState::setUp);
529 g_lua.registerFunction("setAuto", &DownstreamState::setAuto);
b0976f44 530 g_lua.registerMember("upStatus", &DownstreamState::upStatus);
df111b53 531 g_lua.registerMember("weight", &DownstreamState::weight);
532 g_lua.registerMember("order", &DownstreamState::order);
533
4c6f4321 534 g_lua.writeFunction("infolog", [](const string& arg) {
535 infolog("%s", arg);
536 });
537 g_lua.writeFunction("errlog", [](const string& arg) {
538 errlog("%s", arg);
539 });
540 g_lua.writeFunction("warnlog", [](const string& arg) {
541 warnlog("%s", arg);
542 });
543
544
df111b53 545 g_lua.writeFunction("show", [](const string& arg) {
546 g_outputBuffer+=arg;
547 g_outputBuffer+="\n";
548 });
549
550 g_lua.registerFunction<void(dnsheader::*)(bool)>("setRD", [](dnsheader& dh, bool v) {
551 dh.rd=v;
552 });
553
554 g_lua.registerFunction<bool(dnsheader::*)()>("getRD", [](dnsheader& dh) {
555 return (bool)dh.rd;
556 });
557
aeb36780
RG
558 g_lua.registerFunction<void(dnsheader::*)(bool)>("setCD", [](dnsheader& dh, bool v) {
559 dh.cd=v;
560 });
561
562 g_lua.registerFunction<bool(dnsheader::*)()>("getCD", [](dnsheader& dh) {
563 return (bool)dh.cd;
564 });
565
df111b53 566
567 g_lua.registerFunction<void(dnsheader::*)(bool)>("setTC", [](dnsheader& dh, bool v) {
568 dh.tc=v;
bde3ab96 569 if(v) dh.ra = dh.rd; // you'll always need this, otherwise TC=1 gets ignored
df111b53 570 });
571
572 g_lua.registerFunction<void(dnsheader::*)(bool)>("setQR", [](dnsheader& dh, bool v) {
573 dh.qr=v;
574 });
575
df111b53 576
577 g_lua.registerFunction("tostring", &ComboAddress::toString);
578
579 g_lua.registerFunction("isPartOf", &DNSName::isPartOf);
580 g_lua.registerFunction("tostring", &DNSName::toString);
581 g_lua.writeFunction("newDNSName", [](const std::string& name) { return DNSName(name); });
b0976f44 582 g_lua.writeFunction("newSuffixMatchNode", []() { return SuffixMatchNode(); });
df111b53 583
584 g_lua.registerFunction("add",(void (SuffixMatchNode::*)(const DNSName&)) &SuffixMatchNode::add);
585 g_lua.registerFunction("check",(bool (SuffixMatchNode::*)(const DNSName&) const) &SuffixMatchNode::check);
586
42fae326 587 g_lua.writeFunction("carbonServer", [](const std::string& address, boost::optional<string> ourName,
588 boost::optional<int> interval) {
589 auto ours = g_carbon.getCopy();
590 ours.server=ComboAddress(address, 2003);
591 if(ourName)
592 ours.ourname=*ourName;
593 if(interval)
594 ours.interval=*interval;
595 if(!ours.interval)
596 ours.interval=1;
597 g_carbon.setState(ours);
598 });
599
50bed881 600 g_lua.writeFunction("webserver", [client](const std::string& address, const std::string& password) {
601 if(client)
602 return;
603 ComboAddress local(address);
604 try {
605 int sock = socket(local.sin4.sin_family, SOCK_STREAM, 0);
606 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
607 SBind(sock, local);
608 SListen(sock, 5);
609 auto launch=[sock, local, password]() {
610 thread t(dnsdistWebserverThread, sock, local, password);
611 t.detach();
612 };
613 if(g_launchWork)
614 g_launchWork->push_back(launch);
615 else
616 launch();
617 }
618 catch(std::exception& e) {
619 errlog("Unable to bind to webserver socket on %s: %s", local.toStringWithPort(), e.what());
620 }
621
622 });
df111b53 623 g_lua.writeFunction("controlSocket", [client](const std::string& str) {
624 ComboAddress local(str, 5199);
625
626 if(client) {
627 g_serverControl = local;
628 return;
629 }
630
631 try {
632 int sock = socket(local.sin4.sin_family, SOCK_STREAM, 0);
633 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
634 SBind(sock, local);
635 SListen(sock, 5);
2e72cc0e 636 auto launch=[sock, local]() {
637 thread t(controlThread, sock, local);
638 t.detach();
639 };
640 if(g_launchWork)
641 g_launchWork->push_back(launch);
642 else
643 launch();
644
df111b53 645 }
646 catch(std::exception& e) {
647 errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what());
648 }
649 });
650
0e5b3cff 651 // something needs to be done about this, unlocked will 'mostly' work
652 g_lua.writeFunction("topClients", [](unsigned int top) {
653 map<ComboAddress, int,ComboAddress::addressOnlyLessThan > counts;
654 unsigned int total=0;
655 for(const auto& c : g_rings.clientRing) {
656 counts[c]++;
657 total++;
658 }
659 vector<pair<int, ComboAddress>> rcounts;
660 for(const auto& c : counts)
661 rcounts.push_back(make_pair(c.second, c.first));
662
663 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
664 const decltype(rcounts)::value_type& b) {
665 return b.first < a.first;
666 });
667 unsigned int count=1, rest=0;
668 boost::format fmt("%4d %-40s %4d %4.1f%%\n");
669 for(const auto& rc : rcounts) {
670 if(count==top+1)
671 rest+=rc.first;
672 else
673 g_outputBuffer += (fmt % (count++) % rc.second.toString() % rc.first % (100.0*rc.first/total)).str();
674 }
675 g_outputBuffer += (fmt % (count) % "Rest" % rest % (100.0*rest/total)).str();
676 });
677
df111b53 678 g_lua.writeFunction("getTopQueries", [](unsigned int top, boost::optional<int> labels) {
679 map<DNSName, int> counts;
680 unsigned int total=0;
681 if(!labels) {
682 for(const auto& a : g_rings.queryRing) {
683 counts[a]++;
684 total++;
685 }
686 }
687 else {
688 unsigned int lab = *labels;
689 for(auto a : g_rings.queryRing) {
690 a.trimToLabels(lab);
691 counts[a]++;
692 total++;
693 }
694
695 }
cffde2fd 696 // cout<<"Looked at "<<total<<" queries, "<<counts.size()<<" different ones"<<endl;
df111b53 697 vector<pair<int, DNSName>> rcounts;
698 for(const auto& c : counts)
699 rcounts.push_back(make_pair(c.second, c.first));
700
701 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
702 const decltype(rcounts)::value_type& b) {
703 return b.first < a.first;
704 });
705
706 std::unordered_map<int, vector<boost::variant<string,double>>> ret;
707 unsigned int count=1, rest=0;
708 for(const auto& rc : rcounts) {
709 if(count==top+1)
710 rest+=rc.first;
711 else
712 ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
713 }
714 ret.insert({count, {"Rest", rest, 100.0*rest/total}});
715 return ret;
716
717 });
718
a78a3a0e 719 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 720
520eb5a0 721
62edea30 722
520eb5a0 723 g_lua.writeFunction("getResponseRing", []() {
724 decltype(g_rings.respRing) ring;
725 {
726 std::lock_guard<std::mutex> lock(g_rings.respMutex);
727 ring = g_rings.respRing;
728 }
729 vector<std::unordered_map<string, boost::variant<string, unsigned int> > > ret;
730 ret.reserve(ring.size());
731 decltype(ret)::value_type item;
732 for(const auto& r : ring) {
733 item["name"]=r.name.toString();
734 item["qtype"]=r.qtype;
735 item["rcode"]=r.rcode;
736 item["usec"]=r.usec;
737 ret.push_back(item);
738 }
739 return ret;
740 });
f5b58807 741
df111b53 742 g_lua.writeFunction("getTopResponses", [](unsigned int top, unsigned int kind, boost::optional<int> labels) {
743 map<DNSName, int> counts;
744 unsigned int total=0;
745 {
746 std::lock_guard<std::mutex> lock(g_rings.respMutex);
747 if(!labels) {
748 for(const auto& a : g_rings.respRing) {
749 if(a.rcode!=kind)
750 continue;
751 counts[a.name]++;
752 total++;
753 }
754 }
755 else {
756 unsigned int lab = *labels;
757 for(auto a : g_rings.respRing) {
758 if(a.rcode!=kind)
759 continue;
760
761 a.name.trimToLabels(lab);
762 counts[a.name]++;
763 total++;
764 }
765
766 }
767 }
768 // cout<<"Looked at "<<total<<" responses, "<<counts.size()<<" different ones"<<endl;
769 vector<pair<int, DNSName>> rcounts;
770 for(const auto& c : counts)
771 rcounts.push_back(make_pair(c.second, c.first));
772
773 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
774 const decltype(rcounts)::value_type& b) {
775 return b.first < a.first;
776 });
777
778 std::unordered_map<int, vector<boost::variant<string,double>>> ret;
779 unsigned int count=1, rest=0;
780 for(const auto& rc : rcounts) {
781 if(count==top+1)
782 rest+=rc.first;
783 else
784 ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
785 }
786 ret.insert({count, {"Rest", rest, 100.0*rest/total}});
787 return ret;
788
789 });
62edea30 790
a78a3a0e 791 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 792
793
794 g_lua.writeFunction("showResponseLatency", []() {
795
796 map<double, unsigned int> histo;
797 double bin=100;
798 for(int i=0; i < 15; ++i) {
799 histo[bin];
800 bin*=2;
801 }
802
803 double totlat=0;
804 int size=0;
805 {
806 std::lock_guard<std::mutex> lock(g_rings.respMutex);
807 for(const auto& r : g_rings.respRing) {
808 ++size;
809 auto iter = histo.lower_bound(r.usec);
810 if(iter != histo.end())
811 iter->second++;
812 else
813 histo.rbegin()++;
814 totlat+=r.usec;
815 }
816 }
817
e12b3374
RG
818 if (size == 0) {
819 g_outputBuffer = "No traffic yet.\n";
820 return;
821 }
822
df111b53 823 g_outputBuffer = (boost::format("Average response latency: %.02f msec\n") % (0.001*totlat/size)).str();
824 double highest=0;
825
826 for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
827 highest=std::max(highest, iter->second*1.0);
828 }
829 boost::format fmt("%7.2f\t%s\n");
830 g_outputBuffer += (fmt % "msec" % "").str();
831
832 for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
833 int stars = (70.0 * iter->second/highest);
834 char c='*';
835 if(!stars && iter->second) {
836 stars=1; // you get 1 . to show something is there..
837 if(70.0*iter->second/highest > 0.5)
838 c=':';
839 else
840 c='.';
841 }
842 g_outputBuffer += (fmt % (iter->first/1000.0) % string(stars, c)).str();
843 }
844 });
845
846 g_lua.writeFunction("newQPSLimiter", [](int rate, int burst) { return QPSLimiter(rate, burst); });
847 g_lua.registerFunction("check", &QPSLimiter::check);
848
849
850 g_lua.writeFunction("makeKey", []() {
851 g_outputBuffer="setKey("+newKey()+")\n";
852 });
853
854 g_lua.writeFunction("setKey", [](const std::string& key) {
845a82c4 855 if(B64Decode(key, g_key) < 0) {
856 g_outputBuffer=string("Unable to decode ")+key+" as Base64";
857 errlog("%s", g_outputBuffer);
858 }
df111b53 859 });
860
861
862 g_lua.writeFunction("testCrypto", [](string testmsg)
863 {
864 try {
865 SodiumNonce sn, sn2;
866 sn.init();
867 sn2=sn;
868 string encrypted = sodEncryptSym(testmsg, g_key, sn);
869 string decrypted = sodDecryptSym(encrypted, g_key, sn2);
870
2e72cc0e 871 sn.increment();
872 sn2.increment();
873
874 encrypted = sodEncryptSym(testmsg, g_key, sn);
875 decrypted = sodDecryptSym(encrypted, g_key, sn2);
876
df111b53 877 if(testmsg == decrypted)
878 g_outputBuffer="Everything is ok!\n";
879 else
880 g_outputBuffer="Crypto failed..\n";
881
882 }
883 catch(...) {
884 g_outputBuffer="Crypto failed..\n";
885 }});
886
3f6d07a4
RG
887 g_lua.writeFunction("setTCPRecvTimeout", [](int timeout) { g_tcpRecvTimeout=timeout; });
888
889 g_lua.writeFunction("setTCPSendTimeout", [](int timeout) { g_tcpSendTimeout=timeout; });
2a817e5a 890
891 g_lua.writeFunction("dumpStats", [] {
892 vector<string> leftcolumn, rightcolumn;
893
894 boost::format fmt("%-23s\t%+11s");
895 g_outputBuffer.clear();
896 auto entries = g_stats.entries;
897 sort(entries.begin(), entries.end(),
898 [](const decltype(entries)::value_type& a, const decltype(entries)::value_type& b) {
899 return a.first < b.first;
900 });
901 boost::format flt(" %9.1f");
902 for(const auto& e : entries) {
903 string second;
904 if(const auto& val = boost::get<DNSDistStats::stat_t*>(&e.second))
905 second=std::to_string((*val)->load());
906 else if (const auto& val = boost::get<double*>(&e.second))
907 second=(flt % (**val)).str();
908 else
909 second=std::to_string((*boost::get<DNSDistStats::statfunction_t>(&e.second))(e.first));
910
911 if(leftcolumn.size() < g_stats.entries.size()/2)
912 leftcolumn.push_back((fmt % e.first % second).str());
913 else
914 rightcolumn.push_back((fmt % e.first % second).str());
915 }
916
917 auto leftiter=leftcolumn.begin(), rightiter=rightcolumn.begin();
918 boost::format clmn("%|0t|%1% %|39t|%2%\n");
919
920 for(;leftiter != leftcolumn.end() || rightiter != rightcolumn.end();) {
921 string lentry, rentry;
922 if(leftiter!= leftcolumn.end()) {
923 lentry = *leftiter;
924 leftiter++;
925 }
926 if(rightiter!= rightcolumn.end()) {
927 rentry = *rightiter;
928 rightiter++;
929 }
930 g_outputBuffer += (clmn % lentry % rentry).str();
931 }
932 });
df111b53 933
839f3021 934 std::ifstream ifs(config);
2e72cc0e 935 if(!ifs)
839f3021 936 warnlog("Unable to read configuration from '%s'", config);
2e72cc0e 937 else
839f3021 938 infolog("Read configuration from '%s'", config);
df111b53 939
940 g_lua.executeCode(ifs);
2e72cc0e 941 auto ret=*g_launchWork;
942 delete g_launchWork;
943 g_launchWork=0;
944 return ret;
df111b53 945}