]>
Commit | Line | Data |
---|---|---|
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 | ||
9 | using std::thread; | |
10 | ||
2e72cc0e | 11 | static vector<std::function<void(void)>>* g_launchWork; |
12 | ||
13 | vector<std::function<void(void)>> setupLua(bool client) | |
df111b53 | 14 | { |
2e72cc0e | 15 | g_launchWork= new vector<std::function<void(void)>>(); |
df111b53 | 16 | g_lua.writeFunction("newServer", |
2e72cc0e | 17 | [client](boost::variant<string,std::unordered_map<std::string, std::string>> pvars, boost::optional<int> qps) |
df111b53 | 18 | { |
2e72cc0e | 19 | if(client) { |
20 | return shared_ptr<DownstreamState>(); | |
21 | } | |
df111b53 | 22 | if(auto address = boost::get<string>(&pvars)) { |
23 | auto ret=std::make_shared<DownstreamState>(ComboAddress(*address, 53)); | |
2e72cc0e | 24 | |
df111b53 | 25 | if(qps) { |
26 | ret->qps=QPSLimiter(*qps, *qps); | |
27 | } | |
ecbe9133 | 28 | g_dstates.modify([ret](servers_t& servers) { |
29 | servers.push_back(ret); | |
30 | std::stable_sort(servers.begin(), servers.end(), [](const decltype(ret)& a, const decltype(ret)& b) { | |
31 | return a->order < b->order; | |
32 | }); | |
33 | ||
34 | }); | |
2e72cc0e | 35 | |
36 | if(g_launchWork) { | |
37 | g_launchWork->push_back([ret]() { | |
38 | ret->tid = move(thread(responderThread, ret)); | |
39 | }); | |
40 | } | |
41 | else { | |
42 | ret->tid = move(thread(responderThread, ret)); | |
43 | } | |
44 | ||
df111b53 | 45 | return ret; |
46 | } | |
47 | auto vars=boost::get<std::unordered_map<std::string, std::string>>(pvars); | |
48 | auto ret=std::make_shared<DownstreamState>(ComboAddress(vars["address"], 53)); | |
2e72cc0e | 49 | |
df111b53 | 50 | if(vars.count("qps")) { |
51 | ret->qps=QPSLimiter(boost::lexical_cast<int>(vars["qps"]),boost::lexical_cast<int>(vars["qps"])); | |
52 | } | |
53 | ||
54 | if(vars.count("pool")) { | |
55 | ret->pools.insert(vars["pool"]); | |
56 | } | |
57 | ||
58 | if(vars.count("order")) { | |
59 | ret->order=boost::lexical_cast<int>(vars["order"]); | |
60 | } | |
61 | ||
62 | if(vars.count("weight")) { | |
63 | ret->weight=boost::lexical_cast<int>(vars["weight"]); | |
64 | } | |
65 | ||
2e72cc0e | 66 | if(g_launchWork) { |
67 | g_launchWork->push_back([ret]() { | |
68 | ret->tid = move(thread(responderThread, ret)); | |
69 | }); | |
70 | } | |
71 | else { | |
72 | ret->tid = move(thread(responderThread, ret)); | |
73 | } | |
df111b53 | 74 | |
ecbe9133 | 75 | auto states = g_dstates.getCopy(); |
e5a14b2b | 76 | states.push_back(ret); |
77 | std::stable_sort(states.begin(), states.end(), [](const decltype(ret)& a, const decltype(ret)& b) { | |
df111b53 | 78 | return a->order < b->order; |
79 | }); | |
ecbe9133 | 80 | g_dstates.setState(states); |
df111b53 | 81 | return ret; |
82 | } ); | |
83 | ||
0940e4eb | 84 | g_lua.writeFunction("addAnyTCRule", []() { |
85 | auto rules=g_rulactions.getCopy(); | |
86 | rules.push_back({ std::make_shared<QTypeRule>(0xff), std::make_shared<TCAction>()}); | |
87 | g_rulactions.setState(rules); | |
88 | }); | |
df111b53 | 89 | |
0940e4eb | 90 | g_lua.writeFunction("rmRule", [](unsigned int num) { |
91 | auto rules = g_rulactions.getCopy(); | |
92 | if(num >= rules.size()) { | |
93 | g_outputBuffer = "Error: attempt to delete non-existing rule\n"; | |
94 | return; | |
95 | } | |
96 | rules.erase(rules.begin()+num); | |
97 | g_rulactions.setState(rules); | |
98 | }); | |
99 | ||
100 | g_lua.writeFunction("mvRule", [](unsigned int from, unsigned int to) { | |
101 | auto rules = g_rulactions.getCopy(); | |
102 | if(from >= rules.size() || to > rules.size()) { | |
103 | g_outputBuffer = "Error: attempt to move rules from/to invalid index\n"; | |
104 | return; | |
105 | } | |
0940e4eb | 106 | |
107 | auto subject = rules[from]; | |
108 | rules.erase(rules.begin()+from); | |
109 | if(to == rules.size()) | |
110 | rules.push_back(subject); | |
111 | else { | |
112 | if(from < to) | |
113 | --to; | |
114 | rules.insert(rules.begin()+to, subject); | |
115 | } | |
116 | g_rulactions.setState(rules); | |
117 | }); | |
df111b53 | 118 | |
119 | ||
120 | g_lua.writeFunction("rmServer", | |
121 | [](boost::variant<std::shared_ptr<DownstreamState>, int> var) | |
122 | { | |
ecbe9133 | 123 | auto states = g_dstates.getCopy(); |
df111b53 | 124 | if(auto* rem = boost::get<shared_ptr<DownstreamState>>(&var)) |
e5a14b2b | 125 | states.erase(remove(states.begin(), states.end(), *rem), states.end()); |
df111b53 | 126 | else |
e5a14b2b | 127 | states.erase(states.begin() + boost::get<int>(var)); |
ecbe9133 | 128 | g_dstates.setState(states); |
df111b53 | 129 | } ); |
130 | ||
131 | ||
132 | g_lua.writeFunction("setServerPolicy", [](ServerPolicy policy) { | |
e5a14b2b | 133 | g_policy.setState(policy); |
df111b53 | 134 | }); |
df111b53 | 135 | g_lua.writeFunction("setServerPolicyLua", [](string name, policy_t policy) { |
e5a14b2b | 136 | g_policy.setState(ServerPolicy{name, policy}); |
df111b53 | 137 | }); |
138 | ||
139 | g_lua.writeFunction("showServerPolicy", []() { | |
ecbe9133 | 140 | g_outputBuffer=g_policy.getLocal()->name+"\n"; |
df111b53 | 141 | }); |
142 | ||
143 | ||
144 | g_lua.registerMember("name", &ServerPolicy::name); | |
145 | g_lua.registerMember("policy", &ServerPolicy::policy); | |
146 | g_lua.writeFunction("newServerPolicy", [](string name, policy_t policy) { return ServerPolicy{name, policy};}); | |
147 | g_lua.writeVariable("firstAvailable", ServerPolicy{"firstAvailable", firstAvailable}); | |
148 | g_lua.writeVariable("roundrobin", ServerPolicy{"roundrobin", roundrobin}); | |
149 | g_lua.writeVariable("wrandom", ServerPolicy{"wrandom", wrandom}); | |
150 | g_lua.writeVariable("leastOutstanding", ServerPolicy{"leastOutstanding", leastOutstanding}); | |
151 | g_lua.writeFunction("addACL", [](const std::string& domain) { | |
e5a14b2b | 152 | g_ACL.modify([domain](NetmaskGroup& nmg) { nmg.addMask(domain); }); |
df111b53 | 153 | }); |
2e72cc0e | 154 | |
155 | g_lua.writeFunction("addLocal", [client](const std::string& addr) { | |
156 | if(client) | |
157 | return; | |
158 | try { | |
159 | ComboAddress loc(addr, 53); | |
ecbe9133 | 160 | g_locals.push_back(loc); /// only works pre-startup, so no sync necessary |
2e72cc0e | 161 | } |
162 | catch(std::exception& e) { | |
163 | g_outputBuffer="Error: "+string(e.what())+"\n"; | |
164 | } | |
165 | }); | |
df111b53 | 166 | g_lua.writeFunction("setACL", [](const vector<pair<int, string>>& parts) { |
e5a14b2b | 167 | NetmaskGroup nmg; |
cffde2fd | 168 | for(const auto& p : parts) { |
e5a14b2b | 169 | nmg.addMask(p.second); |
cffde2fd | 170 | } |
171 | g_ACL.setState(nmg); | |
df111b53 | 172 | }); |
173 | g_lua.writeFunction("showACL", []() { | |
174 | vector<string> vec; | |
cffde2fd | 175 | |
e5a14b2b | 176 | g_ACL.getCopy().toStringVector(&vec); |
cffde2fd | 177 | |
df111b53 | 178 | for(const auto& s : vec) |
cffde2fd | 179 | g_outputBuffer+=s+"\n"; |
180 | ||
df111b53 | 181 | }); |
182 | g_lua.writeFunction("shutdown", []() { _exit(0);} ); | |
183 | ||
184 | ||
ecbe9133 | 185 | g_lua.writeFunction("addDomainBlock", [](const std::string& domain) { |
0940e4eb | 186 | SuffixMatchNode smn; |
187 | smn.add(domain); | |
188 | g_rulactions.modify([smn](decltype(g_rulactions)::value_type& rulactions) { | |
189 | rulactions.push_back({ | |
190 | std::make_shared<SuffixMatchNodeRule>(smn), | |
191 | std::make_shared<DropAction>() }); | |
192 | }); | |
193 | ||
ecbe9133 | 194 | }); |
df111b53 | 195 | g_lua.writeFunction("showServers", []() { |
196 | try { | |
197 | ostringstream ret; | |
198 | ||
199 | 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%" ); | |
200 | // 1 2 3 4 5 6 7 8 9 10 11 | |
201 | ret << (fmt % "#" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Pools") << endl; | |
202 | ||
203 | uint64_t totQPS{0}, totQueries{0}, totDrops{0}; | |
204 | int counter=0; | |
ecbe9133 | 205 | auto states = g_dstates.getCopy(); |
e5a14b2b | 206 | for(const auto& s : states) { |
df111b53 | 207 | string status; |
208 | if(s->availability == DownstreamState::Availability::Up) | |
209 | status = "UP"; | |
210 | else if(s->availability == DownstreamState::Availability::Down) | |
211 | status = "DOWN"; | |
212 | else | |
213 | status = (s->upStatus ? "up" : "down"); | |
214 | ||
215 | string pools; | |
216 | for(auto& p : s->pools) { | |
217 | if(!pools.empty()) | |
218 | pools+=" "; | |
219 | pools+=p; | |
220 | } | |
221 | ||
222 | ret << (fmt % counter % s->remote.toStringWithPort() % | |
223 | status % | |
224 | s->queryLoad % s->qps.getRate() % s->order % s->weight % s->queries.load() % s->reuseds.load() % (s->dropRate) % (s->latencyUsec/1000.0) % pools) << endl; | |
225 | ||
226 | totQPS += s->queryLoad; | |
227 | totQueries += s->queries.load(); | |
228 | totDrops += s->reuseds.load(); | |
229 | ++counter; | |
230 | } | |
231 | ret<< (fmt % "All" % "" % "" | |
232 | % | |
233 | (double)totQPS % "" % "" % "" % totQueries % totDrops % "" % "" % "" ) << endl; | |
234 | ||
235 | g_outputBuffer=ret.str(); | |
236 | }catch(std::exception& e) { g_outputBuffer=e.what(); throw; } | |
237 | }); | |
238 | ||
239 | g_lua.writeFunction("addPoolRule", [](boost::variant<string,vector<pair<int, string>> > var, string pool) { | |
240 | SuffixMatchNode smn; | |
241 | NetmaskGroup nmg; | |
242 | ||
243 | auto add=[&](string src) { | |
244 | try { | |
245 | smn.add(DNSName(src)); | |
246 | } catch(...) { | |
247 | nmg.addMask(src); | |
248 | } | |
249 | }; | |
250 | if(auto src = boost::get<string>(&var)) | |
251 | add(*src); | |
252 | else { | |
253 | for(auto& a : boost::get<vector<pair<int, string>>>(var)) { | |
254 | add(a.second); | |
255 | } | |
256 | } | |
257 | if(nmg.empty()) | |
0940e4eb | 258 | g_rulactions.modify([smn, pool](decltype(g_rulactions)::value_type& rulactions) { |
259 | rulactions.push_back({ | |
260 | std::make_shared<SuffixMatchNodeRule>(smn), | |
261 | std::make_shared<PoolAction>(pool) }); | |
ecbe9133 | 262 | }); |
df111b53 | 263 | else |
0940e4eb | 264 | g_rulactions.modify([nmg,pool](decltype(g_rulactions)::value_type& rulactions) { |
265 | rulactions.push_back({std::make_shared<NetmaskGroupRule>(nmg), | |
266 | std::make_shared<PoolAction>(pool)}); | |
ecbe9133 | 267 | }); |
df111b53 | 268 | |
269 | }); | |
270 | ||
520eb5a0 | 271 | g_lua.writeFunction("setDNSSECPool", [](const std::string& pool) { |
272 | g_rulactions.modify([pool](decltype(g_rulactions)::value_type& rulactions) { | |
273 | rulactions.push_back({std::make_shared<DNSSECRule>(), | |
274 | std::make_shared<PoolAction>(pool)}); | |
275 | }); | |
276 | }); | |
df111b53 | 277 | |
278 | g_lua.writeFunction("addQPSLimit", [](boost::variant<string,vector<pair<int, string>> > var, int lim) { | |
279 | SuffixMatchNode smn; | |
280 | NetmaskGroup nmg; | |
281 | ||
282 | auto add=[&](string src) { | |
283 | try { | |
284 | smn.add(DNSName(src)); | |
285 | } catch(...) { | |
286 | nmg.addMask(src); | |
287 | } | |
288 | }; | |
289 | if(auto src = boost::get<string>(&var)) | |
290 | add(*src); | |
291 | else { | |
292 | for(auto& a : boost::get<vector<pair<int, string>>>(var)) { | |
293 | add(a.second); | |
294 | } | |
295 | } | |
296 | if(nmg.empty()) | |
0940e4eb | 297 | g_rulactions.modify([smn, lim](decltype(g_rulactions)::value_type& rulactions) { |
298 | rulactions.push_back({std::make_shared<SuffixMatchNodeRule>(smn), | |
299 | std::make_shared<QPSAction>(lim)}); | |
ecbe9133 | 300 | }); |
df111b53 | 301 | else |
0940e4eb | 302 | g_rulactions.modify([nmg, lim](decltype(g_rulactions)::value_type& rulactions) { |
303 | rulactions.push_back({std::make_shared<NetmaskGroupRule>(nmg), | |
304 | std::make_shared<QPSAction>(lim)}); | |
ecbe9133 | 305 | }); |
df111b53 | 306 | }); |
307 | ||
df111b53 | 308 | |
0940e4eb | 309 | g_lua.writeFunction("showRules", []() { |
310 | boost::format fmt("%-3d %9d %-50s %s\n"); | |
311 | g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str(); | |
312 | int num=0; | |
313 | for(const auto& lim : g_rulactions.getCopy()) { | |
314 | string name = lim.first->toString(); | |
315 | g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str(); | |
df111b53 | 316 | ++num; |
317 | } | |
318 | }); | |
319 | ||
df111b53 | 320 | g_lua.writeFunction("getServers", []() { |
321 | vector<pair<int, std::shared_ptr<DownstreamState> > > ret; | |
322 | int count=1; | |
e5a14b2b | 323 | for(const auto& s : g_dstates.getCopy()) { |
df111b53 | 324 | ret.push_back(make_pair(count++, s)); |
325 | } | |
326 | return ret; | |
327 | }); | |
328 | ||
da4e7813 | 329 | g_lua.writeFunction("getPoolServers", [](string pool) { |
330 | return getDownstreamCandidates(g_dstates.getCopy(), pool); | |
331 | }); | |
332 | ||
e5a14b2b | 333 | g_lua.writeFunction("getServer", [](int i) { return g_dstates.getCopy().at(i); }); |
df111b53 | 334 | |
df111b53 | 335 | g_lua.registerFunction<void(DownstreamState::*)(int)>("setQPS", [](DownstreamState& s, int lim) { s.qps = lim ? QPSLimiter(lim, lim) : QPSLimiter(); }); |
336 | g_lua.registerFunction<void(DownstreamState::*)(string)>("addPool", [](DownstreamState& s, string pool) { s.pools.insert(pool);}); | |
337 | g_lua.registerFunction<void(DownstreamState::*)(string)>("rmPool", [](DownstreamState& s, string pool) { s.pools.erase(pool);}); | |
338 | ||
339 | g_lua.registerFunction<void(DownstreamState::*)()>("getOutstanding", [](const DownstreamState& s) { g_outputBuffer=std::to_string(s.outstanding.load()); }); | |
340 | ||
341 | ||
342 | g_lua.registerFunction("isUp", &DownstreamState::isUp); | |
343 | g_lua.registerFunction("setDown", &DownstreamState::setDown); | |
344 | g_lua.registerFunction("setUp", &DownstreamState::setUp); | |
345 | g_lua.registerFunction("setAuto", &DownstreamState::setAuto); | |
b0976f44 | 346 | g_lua.registerMember("upStatus", &DownstreamState::upStatus); |
df111b53 | 347 | g_lua.registerMember("weight", &DownstreamState::weight); |
348 | g_lua.registerMember("order", &DownstreamState::order); | |
349 | ||
4c6f4321 | 350 | g_lua.writeFunction("infolog", [](const string& arg) { |
351 | infolog("%s", arg); | |
352 | }); | |
353 | g_lua.writeFunction("errlog", [](const string& arg) { | |
354 | errlog("%s", arg); | |
355 | }); | |
356 | g_lua.writeFunction("warnlog", [](const string& arg) { | |
357 | warnlog("%s", arg); | |
358 | }); | |
359 | ||
360 | ||
df111b53 | 361 | g_lua.writeFunction("show", [](const string& arg) { |
362 | g_outputBuffer+=arg; | |
363 | g_outputBuffer+="\n"; | |
364 | }); | |
365 | ||
366 | g_lua.registerFunction<void(dnsheader::*)(bool)>("setRD", [](dnsheader& dh, bool v) { | |
367 | dh.rd=v; | |
368 | }); | |
369 | ||
370 | g_lua.registerFunction<bool(dnsheader::*)()>("getRD", [](dnsheader& dh) { | |
371 | return (bool)dh.rd; | |
372 | }); | |
373 | ||
374 | ||
375 | g_lua.registerFunction<void(dnsheader::*)(bool)>("setTC", [](dnsheader& dh, bool v) { | |
376 | dh.tc=v; | |
377 | }); | |
378 | ||
379 | g_lua.registerFunction<void(dnsheader::*)(bool)>("setQR", [](dnsheader& dh, bool v) { | |
380 | dh.qr=v; | |
381 | }); | |
382 | ||
df111b53 | 383 | |
384 | g_lua.registerFunction("tostring", &ComboAddress::toString); | |
385 | ||
386 | g_lua.registerFunction("isPartOf", &DNSName::isPartOf); | |
387 | g_lua.registerFunction("tostring", &DNSName::toString); | |
388 | g_lua.writeFunction("newDNSName", [](const std::string& name) { return DNSName(name); }); | |
b0976f44 | 389 | g_lua.writeFunction("newSuffixMatchNode", []() { return SuffixMatchNode(); }); |
df111b53 | 390 | |
391 | g_lua.registerFunction("add",(void (SuffixMatchNode::*)(const DNSName&)) &SuffixMatchNode::add); | |
392 | g_lua.registerFunction("check",(bool (SuffixMatchNode::*)(const DNSName&) const) &SuffixMatchNode::check); | |
393 | ||
394 | g_lua.writeFunction("controlSocket", [client](const std::string& str) { | |
395 | ComboAddress local(str, 5199); | |
396 | ||
397 | if(client) { | |
398 | g_serverControl = local; | |
399 | return; | |
400 | } | |
401 | ||
402 | try { | |
403 | int sock = socket(local.sin4.sin_family, SOCK_STREAM, 0); | |
404 | SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1); | |
405 | SBind(sock, local); | |
406 | SListen(sock, 5); | |
2e72cc0e | 407 | auto launch=[sock, local]() { |
408 | thread t(controlThread, sock, local); | |
409 | t.detach(); | |
410 | }; | |
411 | if(g_launchWork) | |
412 | g_launchWork->push_back(launch); | |
413 | else | |
414 | launch(); | |
415 | ||
df111b53 | 416 | } |
417 | catch(std::exception& e) { | |
418 | errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what()); | |
419 | } | |
420 | }); | |
421 | ||
0e5b3cff | 422 | // something needs to be done about this, unlocked will 'mostly' work |
423 | g_lua.writeFunction("topClients", [](unsigned int top) { | |
424 | map<ComboAddress, int,ComboAddress::addressOnlyLessThan > counts; | |
425 | unsigned int total=0; | |
426 | for(const auto& c : g_rings.clientRing) { | |
427 | counts[c]++; | |
428 | total++; | |
429 | } | |
430 | vector<pair<int, ComboAddress>> rcounts; | |
431 | for(const auto& c : counts) | |
432 | rcounts.push_back(make_pair(c.second, c.first)); | |
433 | ||
434 | sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a, | |
435 | const decltype(rcounts)::value_type& b) { | |
436 | return b.first < a.first; | |
437 | }); | |
438 | unsigned int count=1, rest=0; | |
439 | boost::format fmt("%4d %-40s %4d %4.1f%%\n"); | |
440 | for(const auto& rc : rcounts) { | |
441 | if(count==top+1) | |
442 | rest+=rc.first; | |
443 | else | |
444 | g_outputBuffer += (fmt % (count++) % rc.second.toString() % rc.first % (100.0*rc.first/total)).str(); | |
445 | } | |
446 | g_outputBuffer += (fmt % (count) % "Rest" % rest % (100.0*rest/total)).str(); | |
447 | }); | |
448 | ||
ecbe9133 | 449 | |
450 | // something needs to be done about this, unlocked will 'mostly' work | |
df111b53 | 451 | g_lua.writeFunction("getTopQueries", [](unsigned int top, boost::optional<int> labels) { |
452 | map<DNSName, int> counts; | |
453 | unsigned int total=0; | |
454 | if(!labels) { | |
455 | for(const auto& a : g_rings.queryRing) { | |
456 | counts[a]++; | |
457 | total++; | |
458 | } | |
459 | } | |
460 | else { | |
461 | unsigned int lab = *labels; | |
462 | for(auto a : g_rings.queryRing) { | |
463 | a.trimToLabels(lab); | |
464 | counts[a]++; | |
465 | total++; | |
466 | } | |
467 | ||
468 | } | |
cffde2fd | 469 | // cout<<"Looked at "<<total<<" queries, "<<counts.size()<<" different ones"<<endl; |
df111b53 | 470 | vector<pair<int, DNSName>> rcounts; |
471 | for(const auto& c : counts) | |
472 | rcounts.push_back(make_pair(c.second, c.first)); | |
473 | ||
474 | sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a, | |
475 | const decltype(rcounts)::value_type& b) { | |
476 | return b.first < a.first; | |
477 | }); | |
478 | ||
479 | std::unordered_map<int, vector<boost::variant<string,double>>> ret; | |
480 | unsigned int count=1, rest=0; | |
481 | for(const auto& rc : rcounts) { | |
482 | if(count==top+1) | |
483 | rest+=rc.first; | |
484 | else | |
485 | ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}}); | |
486 | } | |
487 | ret.insert({count, {"Rest", rest, 100.0*rest/total}}); | |
488 | return ret; | |
489 | ||
490 | }); | |
491 | ||
492 | g_lua.executeCode(R"(function topQueries(top, labels) 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)"); | |
493 | ||
520eb5a0 | 494 | |
495 | g_lua.writeFunction("getResponseRing", []() { | |
496 | decltype(g_rings.respRing) ring; | |
497 | { | |
498 | std::lock_guard<std::mutex> lock(g_rings.respMutex); | |
499 | ring = g_rings.respRing; | |
500 | } | |
501 | vector<std::unordered_map<string, boost::variant<string, unsigned int> > > ret; | |
502 | ret.reserve(ring.size()); | |
503 | decltype(ret)::value_type item; | |
504 | for(const auto& r : ring) { | |
505 | item["name"]=r.name.toString(); | |
506 | item["qtype"]=r.qtype; | |
507 | item["rcode"]=r.rcode; | |
508 | item["usec"]=r.usec; | |
509 | ret.push_back(item); | |
510 | } | |
511 | return ret; | |
512 | }); | |
513 | ||
df111b53 | 514 | g_lua.writeFunction("getTopResponses", [](unsigned int top, unsigned int kind, boost::optional<int> labels) { |
515 | map<DNSName, int> counts; | |
516 | unsigned int total=0; | |
517 | { | |
518 | std::lock_guard<std::mutex> lock(g_rings.respMutex); | |
519 | if(!labels) { | |
520 | for(const auto& a : g_rings.respRing) { | |
521 | if(a.rcode!=kind) | |
522 | continue; | |
523 | counts[a.name]++; | |
524 | total++; | |
525 | } | |
526 | } | |
527 | else { | |
528 | unsigned int lab = *labels; | |
529 | for(auto a : g_rings.respRing) { | |
530 | if(a.rcode!=kind) | |
531 | continue; | |
532 | ||
533 | a.name.trimToLabels(lab); | |
534 | counts[a.name]++; | |
535 | total++; | |
536 | } | |
537 | ||
538 | } | |
539 | } | |
540 | // cout<<"Looked at "<<total<<" responses, "<<counts.size()<<" different ones"<<endl; | |
541 | vector<pair<int, DNSName>> rcounts; | |
542 | for(const auto& c : counts) | |
543 | rcounts.push_back(make_pair(c.second, c.first)); | |
544 | ||
545 | sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a, | |
546 | const decltype(rcounts)::value_type& b) { | |
547 | return b.first < a.first; | |
548 | }); | |
549 | ||
550 | std::unordered_map<int, vector<boost::variant<string,double>>> ret; | |
551 | unsigned int count=1, rest=0; | |
552 | for(const auto& rc : rcounts) { | |
553 | if(count==top+1) | |
554 | rest+=rc.first; | |
555 | else | |
556 | ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}}); | |
557 | } | |
558 | ret.insert({count, {"Rest", rest, 100.0*rest/total}}); | |
559 | return ret; | |
560 | ||
561 | }); | |
562 | ||
563 | g_lua.executeCode(R"(function topResponses(top, kind, labels) 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)"); | |
564 | ||
565 | ||
566 | g_lua.writeFunction("showResponseLatency", []() { | |
567 | ||
568 | map<double, unsigned int> histo; | |
569 | double bin=100; | |
570 | for(int i=0; i < 15; ++i) { | |
571 | histo[bin]; | |
572 | bin*=2; | |
573 | } | |
574 | ||
575 | double totlat=0; | |
576 | int size=0; | |
577 | { | |
578 | std::lock_guard<std::mutex> lock(g_rings.respMutex); | |
579 | for(const auto& r : g_rings.respRing) { | |
580 | ++size; | |
581 | auto iter = histo.lower_bound(r.usec); | |
582 | if(iter != histo.end()) | |
583 | iter->second++; | |
584 | else | |
585 | histo.rbegin()++; | |
586 | totlat+=r.usec; | |
587 | } | |
588 | } | |
589 | ||
590 | g_outputBuffer = (boost::format("Average response latency: %.02f msec\n") % (0.001*totlat/size)).str(); | |
591 | double highest=0; | |
592 | ||
593 | for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) { | |
594 | highest=std::max(highest, iter->second*1.0); | |
595 | } | |
596 | boost::format fmt("%7.2f\t%s\n"); | |
597 | g_outputBuffer += (fmt % "msec" % "").str(); | |
598 | ||
599 | for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) { | |
600 | int stars = (70.0 * iter->second/highest); | |
601 | char c='*'; | |
602 | if(!stars && iter->second) { | |
603 | stars=1; // you get 1 . to show something is there.. | |
604 | if(70.0*iter->second/highest > 0.5) | |
605 | c=':'; | |
606 | else | |
607 | c='.'; | |
608 | } | |
609 | g_outputBuffer += (fmt % (iter->first/1000.0) % string(stars, c)).str(); | |
610 | } | |
611 | }); | |
612 | ||
613 | g_lua.writeFunction("newQPSLimiter", [](int rate, int burst) { return QPSLimiter(rate, burst); }); | |
614 | g_lua.registerFunction("check", &QPSLimiter::check); | |
615 | ||
616 | ||
617 | g_lua.writeFunction("makeKey", []() { | |
618 | g_outputBuffer="setKey("+newKey()+")\n"; | |
619 | }); | |
620 | ||
621 | g_lua.writeFunction("setKey", [](const std::string& key) { | |
845a82c4 | 622 | if(B64Decode(key, g_key) < 0) { |
623 | g_outputBuffer=string("Unable to decode ")+key+" as Base64"; | |
624 | errlog("%s", g_outputBuffer); | |
625 | } | |
df111b53 | 626 | }); |
627 | ||
628 | ||
629 | g_lua.writeFunction("testCrypto", [](string testmsg) | |
630 | { | |
631 | try { | |
632 | SodiumNonce sn, sn2; | |
633 | sn.init(); | |
634 | sn2=sn; | |
635 | string encrypted = sodEncryptSym(testmsg, g_key, sn); | |
636 | string decrypted = sodDecryptSym(encrypted, g_key, sn2); | |
637 | ||
2e72cc0e | 638 | sn.increment(); |
639 | sn2.increment(); | |
640 | ||
641 | encrypted = sodEncryptSym(testmsg, g_key, sn); | |
642 | decrypted = sodDecryptSym(encrypted, g_key, sn2); | |
643 | ||
df111b53 | 644 | if(testmsg == decrypted) |
645 | g_outputBuffer="Everything is ok!\n"; | |
646 | else | |
647 | g_outputBuffer="Crypto failed..\n"; | |
648 | ||
649 | } | |
650 | catch(...) { | |
651 | g_outputBuffer="Crypto failed..\n"; | |
652 | }}); | |
653 | ||
654 | ||
2e72cc0e | 655 | std::ifstream ifs(g_vm["config"].as<string>()); |
656 | if(!ifs) | |
657 | warnlog("Unable to read configuration from '%s'", g_vm["config"].as<string>()); | |
658 | else | |
659 | infolog("Read configuration from '%s'", g_vm["config"].as<string>()); | |
df111b53 | 660 | |
661 | g_lua.executeCode(ifs); | |
2e72cc0e | 662 | auto ret=*g_launchWork; |
663 | delete g_launchWork; | |
664 | g_launchWork=0; | |
665 | return ret; | |
df111b53 | 666 | } |