]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-lua.cc
Add default values for topResponses() and topQueries() parameters. Fixes #2864.
[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)>>();
839f3021 69 typedef std::unordered_map<std::string, boost::variant<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
2e72cc0e 155 if(g_launchWork) {
156 g_launchWork->push_back([ret]() {
157 ret->tid = move(thread(responderThread, ret));
158 });
159 }
160 else {
161 ret->tid = move(thread(responderThread, ret));
162 }
df111b53 163
ecbe9133 164 auto states = g_dstates.getCopy();
e5a14b2b 165 states.push_back(ret);
166 std::stable_sort(states.begin(), states.end(), [](const decltype(ret)& a, const decltype(ret)& b) {
df111b53 167 return a->order < b->order;
168 });
ecbe9133 169 g_dstates.setState(states);
df111b53 170 return ret;
171 } );
172
d8d85a30 173
0940e4eb 174 g_lua.writeFunction("addAnyTCRule", []() {
175 auto rules=g_rulactions.getCopy();
176 rules.push_back({ std::make_shared<QTypeRule>(0xff), std::make_shared<TCAction>()});
177 g_rulactions.setState(rules);
178 });
df111b53 179
0940e4eb 180 g_lua.writeFunction("rmRule", [](unsigned int num) {
181 auto rules = g_rulactions.getCopy();
182 if(num >= rules.size()) {
183 g_outputBuffer = "Error: attempt to delete non-existing rule\n";
184 return;
185 }
186 rules.erase(rules.begin()+num);
187 g_rulactions.setState(rules);
188 });
189
b4fd86c3 190 g_lua.writeFunction("topRule", []() {
191 auto rules = g_rulactions.getCopy();
192 if(rules.empty())
193 return;
194 auto subject = *rules.rbegin();
195 rules.erase(std::prev(rules.end()));
196 rules.insert(rules.begin(), subject);
197 g_rulactions.setState(rules);
198 });
0940e4eb 199 g_lua.writeFunction("mvRule", [](unsigned int from, unsigned int to) {
200 auto rules = g_rulactions.getCopy();
201 if(from >= rules.size() || to > rules.size()) {
202 g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
203 return;
204 }
0940e4eb 205
206 auto subject = rules[from];
207 rules.erase(rules.begin()+from);
208 if(to == rules.size())
209 rules.push_back(subject);
210 else {
211 if(from < to)
212 --to;
213 rules.insert(rules.begin()+to, subject);
214 }
215 g_rulactions.setState(rules);
216 });
df111b53 217
218
219 g_lua.writeFunction("rmServer",
220 [](boost::variant<std::shared_ptr<DownstreamState>, int> var)
221 {
ecbe9133 222 auto states = g_dstates.getCopy();
df111b53 223 if(auto* rem = boost::get<shared_ptr<DownstreamState>>(&var))
e5a14b2b 224 states.erase(remove(states.begin(), states.end(), *rem), states.end());
df111b53 225 else
e5a14b2b 226 states.erase(states.begin() + boost::get<int>(var));
ecbe9133 227 g_dstates.setState(states);
df111b53 228 } );
229
230
231 g_lua.writeFunction("setServerPolicy", [](ServerPolicy policy) {
e5a14b2b 232 g_policy.setState(policy);
df111b53 233 });
df111b53 234 g_lua.writeFunction("setServerPolicyLua", [](string name, policy_t policy) {
e5a14b2b 235 g_policy.setState(ServerPolicy{name, policy});
df111b53 236 });
237
238 g_lua.writeFunction("showServerPolicy", []() {
ecbe9133 239 g_outputBuffer=g_policy.getLocal()->name+"\n";
df111b53 240 });
241
6ad8b29a 242 g_lua.writeFunction("truncateTC", [](bool tc) { g_truncateTC=tc; });
df111b53 243
244 g_lua.registerMember("name", &ServerPolicy::name);
245 g_lua.registerMember("policy", &ServerPolicy::policy);
246 g_lua.writeFunction("newServerPolicy", [](string name, policy_t policy) { return ServerPolicy{name, policy};});
247 g_lua.writeVariable("firstAvailable", ServerPolicy{"firstAvailable", firstAvailable});
248 g_lua.writeVariable("roundrobin", ServerPolicy{"roundrobin", roundrobin});
249 g_lua.writeVariable("wrandom", ServerPolicy{"wrandom", wrandom});
250 g_lua.writeVariable("leastOutstanding", ServerPolicy{"leastOutstanding", leastOutstanding});
251 g_lua.writeFunction("addACL", [](const std::string& domain) {
e5a14b2b 252 g_ACL.modify([domain](NetmaskGroup& nmg) { nmg.addMask(domain); });
df111b53 253 });
2e72cc0e 254
652a7355 255 g_lua.writeFunction("addLocal", [client](const std::string& addr, boost::optional<bool> doTCP) {
2e72cc0e 256 if(client)
257 return;
258 try {
259 ComboAddress loc(addr, 53);
652a7355 260 g_locals.push_back({loc, doTCP ? *doTCP : true}); /// only works pre-startup, so no sync necessary
2e72cc0e 261 }
262 catch(std::exception& e) {
263 g_outputBuffer="Error: "+string(e.what())+"\n";
264 }
265 });
df111b53 266 g_lua.writeFunction("setACL", [](const vector<pair<int, string>>& parts) {
e5a14b2b 267 NetmaskGroup nmg;
cffde2fd 268 for(const auto& p : parts) {
e5a14b2b 269 nmg.addMask(p.second);
cffde2fd 270 }
271 g_ACL.setState(nmg);
df111b53 272 });
273 g_lua.writeFunction("showACL", []() {
274 vector<string> vec;
cffde2fd 275
e5a14b2b 276 g_ACL.getCopy().toStringVector(&vec);
cffde2fd 277
df111b53 278 for(const auto& s : vec)
cffde2fd 279 g_outputBuffer+=s+"\n";
280
df111b53 281 });
282 g_lua.writeFunction("shutdown", []() { _exit(0);} );
283
284
ecbe9133 285 g_lua.writeFunction("addDomainBlock", [](const std::string& domain) {
0940e4eb 286 SuffixMatchNode smn;
1f9100a7 287 smn.add(DNSName(domain));
0940e4eb 288 g_rulactions.modify([smn](decltype(g_rulactions)::value_type& rulactions) {
289 rulactions.push_back({
290 std::make_shared<SuffixMatchNodeRule>(smn),
291 std::make_shared<DropAction>() });
292 });
293
ecbe9133 294 });
df111b53 295 g_lua.writeFunction("showServers", []() {
296 try {
297 ostringstream ret;
298
299 boost::format fmt("%1$-3d %2% %|30t|%3$5s %|36t|%4$7.1f %|41t|%5$7d %|44t|%6$3d %|53t|%7$2d %|55t|%8$10d %|61t|%9$7d %|76t|%10$5.1f %|84t|%11$5.1f %12%" );
300 // 1 2 3 4 5 6 7 8 9 10 11
301 ret << (fmt % "#" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Pools") << endl;
302
303 uint64_t totQPS{0}, totQueries{0}, totDrops{0};
304 int counter=0;
ecbe9133 305 auto states = g_dstates.getCopy();
e5a14b2b 306 for(const auto& s : states) {
df111b53 307 string status;
308 if(s->availability == DownstreamState::Availability::Up)
309 status = "UP";
310 else if(s->availability == DownstreamState::Availability::Down)
311 status = "DOWN";
312 else
313 status = (s->upStatus ? "up" : "down");
314
315 string pools;
316 for(auto& p : s->pools) {
317 if(!pools.empty())
318 pools+=" ";
319 pools+=p;
320 }
321
322 ret << (fmt % counter % s->remote.toStringWithPort() %
323 status %
324 s->queryLoad % s->qps.getRate() % s->order % s->weight % s->queries.load() % s->reuseds.load() % (s->dropRate) % (s->latencyUsec/1000.0) % pools) << endl;
325
326 totQPS += s->queryLoad;
327 totQueries += s->queries.load();
328 totDrops += s->reuseds.load();
329 ++counter;
330 }
331 ret<< (fmt % "All" % "" % ""
332 %
333 (double)totQPS % "" % "" % "" % totQueries % totDrops % "" % "" % "" ) << endl;
334
335 g_outputBuffer=ret.str();
336 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
337 });
338
6bba426c 339 g_lua.writeFunction("addLuaAction", [](luadnsrule_t var, LuaAction::func_t func)
d8d85a30 340 {
341 auto rule=makeRule(var);
342 g_rulactions.modify([rule,func](decltype(g_rulactions)::value_type& rulactions){
343 rulactions.push_back({rule,
344 std::make_shared<LuaAction>(func)});
345 });
346 });
df111b53 347
d8d85a30 348
6bba426c 349 g_lua.writeFunction("NoRecurseAction", []() {
350 return std::shared_ptr<DNSAction>(new NoRecurseAction);
351 });
352
ae3dfa48 353 g_lua.writeFunction("DropAction", []() {
354 return std::shared_ptr<DNSAction>(new DropAction);
355 });
356
2332b03c 357 g_lua.writeFunction("TCAction", []() {
358 return std::shared_ptr<DNSAction>(new TCAction);
359 });
360
ae3dfa48 361
1b726acf 362 g_lua.writeFunction("MaxQPSIPRule", [](unsigned int qps, boost::optional<int> ipv4trunc, boost::optional<int> ipv6trunc) {
363 return std::shared_ptr<DNSRule>(new MaxQPSIPRule(qps, ipv4trunc.get_value_or(32), ipv6trunc.get_value_or(64)));
6bba426c 364 });
365
366
2332b03c 367 g_lua.writeFunction("MaxQPSRule", [](unsigned int qps, boost::optional<int> burst) {
368 if(!burst)
369 return std::shared_ptr<DNSRule>(new MaxQPSRule(qps));
370 else
371 return std::shared_ptr<DNSRule>(new MaxQPSRule(qps, *burst));
372 });
373
374
6bba426c 375 g_lua.writeFunction("addAction", [](luadnsrule_t var, std::shared_ptr<DNSAction> ea)
376 {
377 auto rule=makeRule(var);
378 g_rulactions.modify([rule, ea](decltype(g_rulactions)::value_type& rulactions){
379 rulactions.push_back({rule, ea});
380 });
381 });
382
383
384 g_lua.writeFunction("addPoolRule", [](luadnsrule_t var, string pool) {
d8d85a30 385 auto rule=makeRule(var);
386 g_rulactions.modify([rule, pool](decltype(g_rulactions)::value_type& rulactions) {
0940e4eb 387 rulactions.push_back({
d8d85a30 388 rule,
389 std::make_shared<PoolAction>(pool) });
ecbe9133 390 });
df111b53 391 });
0570f37c 392
6bba426c 393 g_lua.writeFunction("addNoRecurseRule", [](luadnsrule_t var) {
0570f37c 394 auto rule=makeRule(var);
395 g_rulactions.modify([rule](decltype(g_rulactions)::value_type& rulactions) {
396 rulactions.push_back({
397 rule,
398 std::make_shared<NoRecurseAction>() });
399 });
400 });
401
402
6bba426c 403 g_lua.writeFunction("addQPSPoolRule", [](luadnsrule_t var, int limit, string pool) {
d8d85a30 404 auto rule = makeRule(var);
405 g_rulactions.modify([rule, pool,limit](decltype(g_rulactions)::value_type& rulactions) {
406 rulactions.push_back({
407 rule,
408 std::make_shared<QPSPoolAction>(limit, pool) });
409 });
fd010ca3 410 });
df111b53 411
520eb5a0 412 g_lua.writeFunction("setDNSSECPool", [](const std::string& pool) {
413 g_rulactions.modify([pool](decltype(g_rulactions)::value_type& rulactions) {
414 rulactions.push_back({std::make_shared<DNSSECRule>(),
415 std::make_shared<PoolAction>(pool)});
416 });
417 });
df111b53 418
6bba426c 419 g_lua.writeFunction("addQPSLimit", [](luadnsrule_t var, int lim) {
d8d85a30 420 auto rule = makeRule(var);
421 g_rulactions.modify([lim,rule](decltype(g_rulactions)::value_type& rulactions) {
422 rulactions.push_back({rule,
423 std::make_shared<QPSAction>(lim)});
424 });
df111b53 425 });
d8d85a30 426
6bba426c 427 g_lua.writeFunction("addDelay", [](luadnsrule_t var, int msec) {
7b3865cd 428 auto rule = makeRule(var);
429 g_rulactions.modify([msec,rule](decltype(g_rulactions)::value_type& rulactions) {
430 rulactions.push_back({rule,
431 std::make_shared<DelayAction>(msec)});
432 });
433 });
df111b53 434
df111b53 435
0940e4eb 436 g_lua.writeFunction("showRules", []() {
437 boost::format fmt("%-3d %9d %-50s %s\n");
438 g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
439 int num=0;
440 for(const auto& lim : g_rulactions.getCopy()) {
441 string name = lim.first->toString();
442 g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
df111b53 443 ++num;
444 }
445 });
446
df111b53 447 g_lua.writeFunction("getServers", []() {
448 vector<pair<int, std::shared_ptr<DownstreamState> > > ret;
449 int count=1;
e5a14b2b 450 for(const auto& s : g_dstates.getCopy()) {
df111b53 451 ret.push_back(make_pair(count++, s));
452 }
453 return ret;
454 });
455
da4e7813 456 g_lua.writeFunction("getPoolServers", [](string pool) {
457 return getDownstreamCandidates(g_dstates.getCopy(), pool);
458 });
459
e5a14b2b 460 g_lua.writeFunction("getServer", [](int i) { return g_dstates.getCopy().at(i); });
df111b53 461
df111b53 462 g_lua.registerFunction<void(DownstreamState::*)(int)>("setQPS", [](DownstreamState& s, int lim) { s.qps = lim ? QPSLimiter(lim, lim) : QPSLimiter(); });
463 g_lua.registerFunction<void(DownstreamState::*)(string)>("addPool", [](DownstreamState& s, string pool) { s.pools.insert(pool);});
464 g_lua.registerFunction<void(DownstreamState::*)(string)>("rmPool", [](DownstreamState& s, string pool) { s.pools.erase(pool);});
465
466 g_lua.registerFunction<void(DownstreamState::*)()>("getOutstanding", [](const DownstreamState& s) { g_outputBuffer=std::to_string(s.outstanding.load()); });
467
468
469 g_lua.registerFunction("isUp", &DownstreamState::isUp);
470 g_lua.registerFunction("setDown", &DownstreamState::setDown);
471 g_lua.registerFunction("setUp", &DownstreamState::setUp);
472 g_lua.registerFunction("setAuto", &DownstreamState::setAuto);
b0976f44 473 g_lua.registerMember("upStatus", &DownstreamState::upStatus);
df111b53 474 g_lua.registerMember("weight", &DownstreamState::weight);
475 g_lua.registerMember("order", &DownstreamState::order);
476
4c6f4321 477 g_lua.writeFunction("infolog", [](const string& arg) {
478 infolog("%s", arg);
479 });
480 g_lua.writeFunction("errlog", [](const string& arg) {
481 errlog("%s", arg);
482 });
483 g_lua.writeFunction("warnlog", [](const string& arg) {
484 warnlog("%s", arg);
485 });
486
487
df111b53 488 g_lua.writeFunction("show", [](const string& arg) {
489 g_outputBuffer+=arg;
490 g_outputBuffer+="\n";
491 });
492
493 g_lua.registerFunction<void(dnsheader::*)(bool)>("setRD", [](dnsheader& dh, bool v) {
494 dh.rd=v;
495 });
496
497 g_lua.registerFunction<bool(dnsheader::*)()>("getRD", [](dnsheader& dh) {
498 return (bool)dh.rd;
499 });
500
501
502 g_lua.registerFunction<void(dnsheader::*)(bool)>("setTC", [](dnsheader& dh, bool v) {
503 dh.tc=v;
bde3ab96 504 if(v) dh.ra = dh.rd; // you'll always need this, otherwise TC=1 gets ignored
df111b53 505 });
506
507 g_lua.registerFunction<void(dnsheader::*)(bool)>("setQR", [](dnsheader& dh, bool v) {
508 dh.qr=v;
509 });
510
df111b53 511
512 g_lua.registerFunction("tostring", &ComboAddress::toString);
513
514 g_lua.registerFunction("isPartOf", &DNSName::isPartOf);
515 g_lua.registerFunction("tostring", &DNSName::toString);
516 g_lua.writeFunction("newDNSName", [](const std::string& name) { return DNSName(name); });
b0976f44 517 g_lua.writeFunction("newSuffixMatchNode", []() { return SuffixMatchNode(); });
df111b53 518
519 g_lua.registerFunction("add",(void (SuffixMatchNode::*)(const DNSName&)) &SuffixMatchNode::add);
520 g_lua.registerFunction("check",(bool (SuffixMatchNode::*)(const DNSName&) const) &SuffixMatchNode::check);
521
42fae326 522 g_lua.writeFunction("carbonServer", [](const std::string& address, boost::optional<string> ourName,
523 boost::optional<int> interval) {
524 auto ours = g_carbon.getCopy();
525 ours.server=ComboAddress(address, 2003);
526 if(ourName)
527 ours.ourname=*ourName;
528 if(interval)
529 ours.interval=*interval;
530 if(!ours.interval)
531 ours.interval=1;
532 g_carbon.setState(ours);
533 });
534
50bed881 535 g_lua.writeFunction("webserver", [client](const std::string& address, const std::string& password) {
536 if(client)
537 return;
538 ComboAddress local(address);
539 try {
540 int sock = socket(local.sin4.sin_family, SOCK_STREAM, 0);
541 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
542 SBind(sock, local);
543 SListen(sock, 5);
544 auto launch=[sock, local, password]() {
545 thread t(dnsdistWebserverThread, sock, local, password);
546 t.detach();
547 };
548 if(g_launchWork)
549 g_launchWork->push_back(launch);
550 else
551 launch();
552 }
553 catch(std::exception& e) {
554 errlog("Unable to bind to webserver socket on %s: %s", local.toStringWithPort(), e.what());
555 }
556
557 });
df111b53 558 g_lua.writeFunction("controlSocket", [client](const std::string& str) {
559 ComboAddress local(str, 5199);
560
561 if(client) {
562 g_serverControl = local;
563 return;
564 }
565
566 try {
567 int sock = socket(local.sin4.sin_family, SOCK_STREAM, 0);
568 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
569 SBind(sock, local);
570 SListen(sock, 5);
2e72cc0e 571 auto launch=[sock, local]() {
572 thread t(controlThread, sock, local);
573 t.detach();
574 };
575 if(g_launchWork)
576 g_launchWork->push_back(launch);
577 else
578 launch();
579
df111b53 580 }
581 catch(std::exception& e) {
582 errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what());
583 }
584 });
585
0e5b3cff 586 // something needs to be done about this, unlocked will 'mostly' work
587 g_lua.writeFunction("topClients", [](unsigned int top) {
588 map<ComboAddress, int,ComboAddress::addressOnlyLessThan > counts;
589 unsigned int total=0;
590 for(const auto& c : g_rings.clientRing) {
591 counts[c]++;
592 total++;
593 }
594 vector<pair<int, ComboAddress>> rcounts;
595 for(const auto& c : counts)
596 rcounts.push_back(make_pair(c.second, c.first));
597
598 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
599 const decltype(rcounts)::value_type& b) {
600 return b.first < a.first;
601 });
602 unsigned int count=1, rest=0;
603 boost::format fmt("%4d %-40s %4d %4.1f%%\n");
604 for(const auto& rc : rcounts) {
605 if(count==top+1)
606 rest+=rc.first;
607 else
608 g_outputBuffer += (fmt % (count++) % rc.second.toString() % rc.first % (100.0*rc.first/total)).str();
609 }
610 g_outputBuffer += (fmt % (count) % "Rest" % rest % (100.0*rest/total)).str();
611 });
612
df111b53 613 g_lua.writeFunction("getTopQueries", [](unsigned int top, boost::optional<int> labels) {
614 map<DNSName, int> counts;
615 unsigned int total=0;
616 if(!labels) {
617 for(const auto& a : g_rings.queryRing) {
618 counts[a]++;
619 total++;
620 }
621 }
622 else {
623 unsigned int lab = *labels;
624 for(auto a : g_rings.queryRing) {
625 a.trimToLabels(lab);
626 counts[a]++;
627 total++;
628 }
629
630 }
cffde2fd 631 // cout<<"Looked at "<<total<<" queries, "<<counts.size()<<" different ones"<<endl;
df111b53 632 vector<pair<int, DNSName>> rcounts;
633 for(const auto& c : counts)
634 rcounts.push_back(make_pair(c.second, c.first));
635
636 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
637 const decltype(rcounts)::value_type& b) {
638 return b.first < a.first;
639 });
640
641 std::unordered_map<int, vector<boost::variant<string,double>>> ret;
642 unsigned int count=1, rest=0;
643 for(const auto& rc : rcounts) {
644 if(count==top+1)
645 rest+=rc.first;
646 else
647 ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
648 }
649 ret.insert({count, {"Rest", rest, 100.0*rest/total}});
650 return ret;
651
652 });
653
a78a3a0e 654 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 655
520eb5a0 656
62edea30 657
520eb5a0 658 g_lua.writeFunction("getResponseRing", []() {
659 decltype(g_rings.respRing) ring;
660 {
661 std::lock_guard<std::mutex> lock(g_rings.respMutex);
662 ring = g_rings.respRing;
663 }
664 vector<std::unordered_map<string, boost::variant<string, unsigned int> > > ret;
665 ret.reserve(ring.size());
666 decltype(ret)::value_type item;
667 for(const auto& r : ring) {
668 item["name"]=r.name.toString();
669 item["qtype"]=r.qtype;
670 item["rcode"]=r.rcode;
671 item["usec"]=r.usec;
672 ret.push_back(item);
673 }
674 return ret;
675 });
f5b58807 676
df111b53 677 g_lua.writeFunction("getTopResponses", [](unsigned int top, unsigned int kind, boost::optional<int> labels) {
678 map<DNSName, int> counts;
679 unsigned int total=0;
680 {
681 std::lock_guard<std::mutex> lock(g_rings.respMutex);
682 if(!labels) {
683 for(const auto& a : g_rings.respRing) {
684 if(a.rcode!=kind)
685 continue;
686 counts[a.name]++;
687 total++;
688 }
689 }
690 else {
691 unsigned int lab = *labels;
692 for(auto a : g_rings.respRing) {
693 if(a.rcode!=kind)
694 continue;
695
696 a.name.trimToLabels(lab);
697 counts[a.name]++;
698 total++;
699 }
700
701 }
702 }
703 // cout<<"Looked at "<<total<<" responses, "<<counts.size()<<" different ones"<<endl;
704 vector<pair<int, DNSName>> rcounts;
705 for(const auto& c : counts)
706 rcounts.push_back(make_pair(c.second, c.first));
707
708 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
709 const decltype(rcounts)::value_type& b) {
710 return b.first < a.first;
711 });
712
713 std::unordered_map<int, vector<boost::variant<string,double>>> ret;
714 unsigned int count=1, rest=0;
715 for(const auto& rc : rcounts) {
716 if(count==top+1)
717 rest+=rc.first;
718 else
719 ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
720 }
721 ret.insert({count, {"Rest", rest, 100.0*rest/total}});
722 return ret;
723
724 });
62edea30 725
a78a3a0e 726 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 727
728
729 g_lua.writeFunction("showResponseLatency", []() {
730
731 map<double, unsigned int> histo;
732 double bin=100;
733 for(int i=0; i < 15; ++i) {
734 histo[bin];
735 bin*=2;
736 }
737
738 double totlat=0;
739 int size=0;
740 {
741 std::lock_guard<std::mutex> lock(g_rings.respMutex);
742 for(const auto& r : g_rings.respRing) {
743 ++size;
744 auto iter = histo.lower_bound(r.usec);
745 if(iter != histo.end())
746 iter->second++;
747 else
748 histo.rbegin()++;
749 totlat+=r.usec;
750 }
751 }
752
753 g_outputBuffer = (boost::format("Average response latency: %.02f msec\n") % (0.001*totlat/size)).str();
754 double highest=0;
755
756 for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
757 highest=std::max(highest, iter->second*1.0);
758 }
759 boost::format fmt("%7.2f\t%s\n");
760 g_outputBuffer += (fmt % "msec" % "").str();
761
762 for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
763 int stars = (70.0 * iter->second/highest);
764 char c='*';
765 if(!stars && iter->second) {
766 stars=1; // you get 1 . to show something is there..
767 if(70.0*iter->second/highest > 0.5)
768 c=':';
769 else
770 c='.';
771 }
772 g_outputBuffer += (fmt % (iter->first/1000.0) % string(stars, c)).str();
773 }
774 });
775
776 g_lua.writeFunction("newQPSLimiter", [](int rate, int burst) { return QPSLimiter(rate, burst); });
777 g_lua.registerFunction("check", &QPSLimiter::check);
778
779
780 g_lua.writeFunction("makeKey", []() {
781 g_outputBuffer="setKey("+newKey()+")\n";
782 });
783
784 g_lua.writeFunction("setKey", [](const std::string& key) {
845a82c4 785 if(B64Decode(key, g_key) < 0) {
786 g_outputBuffer=string("Unable to decode ")+key+" as Base64";
787 errlog("%s", g_outputBuffer);
788 }
df111b53 789 });
790
791
792 g_lua.writeFunction("testCrypto", [](string testmsg)
793 {
794 try {
795 SodiumNonce sn, sn2;
796 sn.init();
797 sn2=sn;
798 string encrypted = sodEncryptSym(testmsg, g_key, sn);
799 string decrypted = sodDecryptSym(encrypted, g_key, sn2);
800
2e72cc0e 801 sn.increment();
802 sn2.increment();
803
804 encrypted = sodEncryptSym(testmsg, g_key, sn);
805 decrypted = sodDecryptSym(encrypted, g_key, sn2);
806
df111b53 807 if(testmsg == decrypted)
808 g_outputBuffer="Everything is ok!\n";
809 else
810 g_outputBuffer="Crypto failed..\n";
811
812 }
813 catch(...) {
814 g_outputBuffer="Crypto failed..\n";
815 }});
816
817
839f3021 818 std::ifstream ifs(config);
2e72cc0e 819 if(!ifs)
839f3021 820 warnlog("Unable to read configuration from '%s'", config);
2e72cc0e 821 else
839f3021 822 infolog("Read configuration from '%s'", config);
df111b53 823
824 g_lua.executeCode(ifs);
2e72cc0e 825 auto ret=*g_launchWork;
826 delete g_launchWork;
827 g_launchWork=0;
828 return ret;
df111b53 829}