]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-lua-inspection.cc
dnsdist: Refactoring to merge the UDP and TCP paths
[thirdparty/pdns.git] / pdns / dnsdist-lua-inspection.cc
CommitLineData
6bb38cd6
RG
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22#include "dnsdist.hh"
23#include "dnsdist-lua.hh"
dc2fd93a 24#include "dnsdist-dynblocks.hh"
03b00917 25#include "dnsdist-rings.hh"
6bb38cd6
RG
26
27#include "statnode.hh"
28
90b1e92f 29static std::unordered_map<unsigned int, vector<boost::variant<string,double>>> getGenResponses(unsigned int top, boost::optional<int> labels, std::function<bool(const Rings::Response&)> pred)
6bb38cd6
RG
30{
31 setLuaNoSideEffect();
90b1e92f 32 map<DNSName, unsigned int> counts;
6bb38cd6
RG
33 unsigned int total=0;
34 {
cdcf7eeb 35 for (const auto& shard : g_rings.d_shards) {
cdcb5fbd 36 std::lock_guard<std::mutex> rl(shard->respLock);
01f6920b 37 if(!labels) {
cdcb5fbd 38 for(const auto& a : shard->respRing) {
01f6920b
RG
39 if(!pred(a))
40 continue;
41 counts[a.name]++;
42 total++;
43 }
6bb38cd6 44 }
01f6920b
RG
45 else {
46 unsigned int lab = *labels;
b4b213da 47 for(const auto& a : shard->respRing) {
01f6920b
RG
48 if(!pred(a))
49 continue;
6bb38cd6 50
b4b213da
RG
51 DNSName temp(a.name);
52 temp.trimToLabels(lab);
53 counts[temp]++;
01f6920b
RG
54 total++;
55 }
56 }
6bb38cd6
RG
57 }
58 }
59 // cout<<"Looked at "<<total<<" responses, "<<counts.size()<<" different ones"<<endl;
90b1e92f 60 vector<pair<unsigned int, DNSName>> rcounts;
6bb38cd6
RG
61 rcounts.reserve(counts.size());
62 for(const auto& c : counts)
63 rcounts.push_back(make_pair(c.second, c.first.makeLowerCase()));
64
65 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
66 const decltype(rcounts)::value_type& b) {
67 return b.first < a.first;
68 });
69
90b1e92f 70 std::unordered_map<unsigned int, vector<boost::variant<string,double>>> ret;
6bb38cd6
RG
71 unsigned int count=1, rest=0;
72 for(const auto& rc : rcounts) {
73 if(count==top+1)
74 rest+=rc.first;
75 else
76 ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
77 }
78 ret.insert({count, {"Rest", rest, total > 0 ? 100.0*rest/total : 100.0}});
79 return ret;
80}
81
f8327125
RG
82typedef std::unordered_map<ComboAddress, unsigned int, ComboAddress::addressOnlyHash, ComboAddress::addressOnlyEqual> counts_t;
83
84static counts_t filterScore(const counts_t& counts,
90b1e92f 85 double delta, unsigned int rate)
6bb38cd6 86{
f8327125
RG
87 counts_t ret;
88
6bb38cd6 89 double lim = delta*rate;
f8327125
RG
90 for(const auto& c : counts) {
91 if (c.second > lim) {
92 ret[c.first] = c.second;
90b1e92f 93 }
6bb38cd6 94 }
90b1e92f 95
f8327125 96 return ret;
6bb38cd6
RG
97}
98
99
100typedef std::function<void(const StatNode&, const StatNode::Stat&, const StatNode::Stat&)> statvisitor_t;
101
102static void statNodeRespRing(statvisitor_t visitor, unsigned int seconds)
103{
104 struct timespec cutoff, now;
105 gettime(&now);
7ff8246f
RG
106 cutoff = now;
107 cutoff.tv_sec -= seconds;
6bb38cd6 108
6bb38cd6 109 StatNode root;
cdcf7eeb 110 for (const auto& shard : g_rings.d_shards) {
cdcb5fbd 111 std::lock_guard<std::mutex> rl(shard->respLock);
01f6920b 112
cdcb5fbd 113 for(const auto& c : shard->respRing) {
90b1e92f
RG
114 if (now < c.when)
115 continue;
6bb38cd6 116
90b1e92f
RG
117 if (seconds && c.when < cutoff)
118 continue;
6bb38cd6 119
7d07129c 120 root.submit(c.name, c.dh.rcode, boost::none);
90b1e92f 121 }
6bb38cd6 122 }
6bb38cd6 123
90b1e92f
RG
124 StatNode::Stat node;
125 root.visit([visitor](const StatNode* node_, const StatNode::Stat& self, const StatNode::Stat& children) {
6bb38cd6 126 visitor(*node_, self, children);}, node);
6bb38cd6
RG
127}
128
129static vector<pair<unsigned int, std::unordered_map<string,string> > > getRespRing(boost::optional<int> rcode)
130{
131 typedef std::unordered_map<string,string> entry_t;
132 vector<pair<unsigned int, entry_t > > ret;
01f6920b 133
cdcf7eeb 134 for (const auto& shard : g_rings.d_shards) {
cdcb5fbd 135 std::lock_guard<std::mutex> rl(shard->respLock);
01f6920b
RG
136
137 entry_t e;
138 unsigned int count=1;
cdcb5fbd 139 for(const auto& c : shard->respRing) {
01f6920b
RG
140 if(rcode && (rcode.get() != c.dh.rcode))
141 continue;
142 e["qname"]=c.name.toString();
143 e["rcode"]=std::to_string(c.dh.rcode);
144 ret.push_back(std::make_pair(count,e));
145 count++;
146 }
6bb38cd6 147 }
01f6920b 148
6bb38cd6
RG
149 return ret;
150}
151
90b1e92f 152static counts_t exceedRespGen(unsigned int rate, int seconds, std::function<void(counts_t&, const Rings::Response&)> T)
6bb38cd6
RG
153{
154 counts_t counts;
155 struct timespec cutoff, mintime, now;
156 gettime(&now);
157 cutoff = mintime = now;
158 cutoff.tv_sec -= seconds;
159
cfe4b655 160 counts.reserve(g_rings.getNumberOfResponseEntries());
01f6920b 161
cdcf7eeb 162 for (const auto& shard : g_rings.d_shards) {
cdcb5fbd
RG
163 std::lock_guard<std::mutex> rl(shard->respLock);
164 for(const auto& c : shard->respRing) {
01f6920b 165
90b1e92f
RG
166 if(seconds && c.when < cutoff)
167 continue;
168 if(now < c.when)
169 continue;
170
171 T(counts, c);
172 if(c.when < mintime)
173 mintime = c.when;
174 }
6bb38cd6 175 }
90b1e92f 176
6bb38cd6 177 double delta = seconds ? seconds : DiffTime(now, mintime);
f8327125 178 return filterScore(counts, delta, rate);
6bb38cd6
RG
179}
180
90b1e92f 181static counts_t exceedQueryGen(unsigned int rate, int seconds, std::function<void(counts_t&, const Rings::Query&)> T)
6bb38cd6
RG
182{
183 counts_t counts;
184 struct timespec cutoff, mintime, now;
185 gettime(&now);
186 cutoff = mintime = now;
187 cutoff.tv_sec -= seconds;
188
cfe4b655 189 counts.reserve(g_rings.getNumberOfQueryEntries());
01f6920b 190
cdcf7eeb 191 for (const auto& shard : g_rings.d_shards) {
cdcb5fbd
RG
192 std::lock_guard<std::mutex> rl(shard->queryLock);
193 for(const auto& c : shard->queryRing) {
90b1e92f
RG
194 if(seconds && c.when < cutoff)
195 continue;
196 if(now < c.when)
197 continue;
198 T(counts, c);
199 if(c.when < mintime)
200 mintime = c.when;
201 }
6bb38cd6 202 }
90b1e92f 203
6bb38cd6 204 double delta = seconds ? seconds : DiffTime(now, mintime);
f8327125 205 return filterScore(counts, delta, rate);
6bb38cd6
RG
206}
207
208
90b1e92f 209static counts_t exceedRCode(unsigned int rate, int seconds, int rcode)
6bb38cd6
RG
210{
211 return exceedRespGen(rate, seconds, [rcode](counts_t& counts, const Rings::Response& r)
212 {
213 if(r.dh.rcode == rcode)
214 counts[r.requestor]++;
215 });
216}
217
90b1e92f 218static counts_t exceedRespByterate(unsigned int rate, int seconds)
6bb38cd6
RG
219{
220 return exceedRespGen(rate, seconds, [](counts_t& counts, const Rings::Response& r)
221 {
222 counts[r.requestor]+=r.size;
223 });
224}
225
226void setupLuaInspection()
227{
228 g_lua.writeFunction("topClients", [](boost::optional<unsigned int> top_) {
229 setLuaNoSideEffect();
230 auto top = top_.get_value_or(10);
90b1e92f 231 map<ComboAddress, unsigned int,ComboAddress::addressOnlyLessThan > counts;
6bb38cd6
RG
232 unsigned int total=0;
233 {
cdcf7eeb 234 for (const auto& shard : g_rings.d_shards) {
cdcb5fbd
RG
235 std::lock_guard<std::mutex> rl(shard->queryLock);
236 for(const auto& c : shard->queryRing) {
01f6920b
RG
237 counts[c.requestor]++;
238 total++;
239 }
6bb38cd6
RG
240 }
241 }
90b1e92f 242 vector<pair<unsigned int, ComboAddress>> rcounts;
6bb38cd6
RG
243 rcounts.reserve(counts.size());
244 for(const auto& c : counts)
245 rcounts.push_back(make_pair(c.second, c.first));
246
247 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
248 const decltype(rcounts)::value_type& b) {
249 return b.first < a.first;
250 });
251 unsigned int count=1, rest=0;
252 boost::format fmt("%4d %-40s %4d %4.1f%%\n");
253 for(const auto& rc : rcounts) {
254 if(count==top+1)
255 rest+=rc.first;
256 else
257 g_outputBuffer += (fmt % (count++) % rc.second.toString() % rc.first % (100.0*rc.first/total)).str();
258 }
259 g_outputBuffer += (fmt % (count) % "Rest" % rest % (total > 0 ? 100.0*rest/total : 100.0)).str();
260 });
261
262 g_lua.writeFunction("getTopQueries", [](unsigned int top, boost::optional<int> labels) {
263 setLuaNoSideEffect();
90b1e92f 264 map<DNSName, unsigned int> counts;
6bb38cd6
RG
265 unsigned int total=0;
266 if(!labels) {
cdcf7eeb 267 for (const auto& shard : g_rings.d_shards) {
cdcb5fbd
RG
268 std::lock_guard<std::mutex> rl(shard->queryLock);
269 for(const auto& a : shard->queryRing) {
01f6920b
RG
270 counts[a.name]++;
271 total++;
272 }
273 }
6bb38cd6
RG
274 }
275 else {
276 unsigned int lab = *labels;
cdcf7eeb 277 for (const auto& shard : g_rings.d_shards) {
cdcb5fbd
RG
278 std::lock_guard<std::mutex> rl(shard->queryLock);
279 for(auto a : shard->queryRing) {
01f6920b
RG
280 a.name.trimToLabels(lab);
281 counts[a.name]++;
282 total++;
283 }
284 }
6bb38cd6
RG
285 }
286 // cout<<"Looked at "<<total<<" queries, "<<counts.size()<<" different ones"<<endl;
90b1e92f 287 vector<pair<unsigned int, DNSName>> rcounts;
6bb38cd6
RG
288 rcounts.reserve(counts.size());
289 for(const auto& c : counts)
290 rcounts.push_back(make_pair(c.second, c.first.makeLowerCase()));
291
292 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
293 const decltype(rcounts)::value_type& b) {
294 return b.first < a.first;
295 });
296
90b1e92f 297 std::unordered_map<unsigned int, vector<boost::variant<string,double>>> ret;
6bb38cd6
RG
298 unsigned int count=1, rest=0;
299 for(const auto& rc : rcounts) {
300 if(count==top+1)
301 rest+=rc.first;
302 else
303 ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
304 }
305 ret.insert({count, {"Rest", rest, total > 0 ? 100.0*rest/total : 100.0}});
306 return ret;
307
308 });
309
310 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)");
311
312 g_lua.writeFunction("getResponseRing", []() {
313 setLuaNoSideEffect();
01f6920b
RG
314 size_t totalEntries = 0;
315 std::vector<boost::circular_buffer<Rings::Response>> rings;
316 rings.reserve(g_rings.getNumberOfShards());
cdcf7eeb 317 for (const auto& shard : g_rings.d_shards) {
01f6920b 318 {
cdcb5fbd
RG
319 std::lock_guard<std::mutex> rl(shard->respLock);
320 rings.push_back(shard->respRing);
01f6920b 321 }
cdcb5fbd 322 totalEntries += rings.back().size();
6bb38cd6
RG
323 }
324 vector<std::unordered_map<string, boost::variant<string, unsigned int> > > ret;
01f6920b 325 ret.reserve(totalEntries);
6bb38cd6 326 decltype(ret)::value_type item;
01f6920b
RG
327 for (size_t idx = 0; idx < rings.size(); idx++) {
328 for(const auto& r : rings[idx]) {
329 item["name"]=r.name.toString();
330 item["qtype"]=r.qtype;
331 item["rcode"]=r.dh.rcode;
332 item["usec"]=r.usec;
333 ret.push_back(item);
334 }
6bb38cd6
RG
335 }
336 return ret;
337 });
338
339 g_lua.writeFunction("getTopResponses", [](unsigned int top, unsigned int kind, boost::optional<int> labels) {
340 return getGenResponses(top, labels, [kind](const Rings::Response& r) { return r.dh.rcode == kind; });
341 });
342
343 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)");
344
345
346 g_lua.writeFunction("getSlowResponses", [](unsigned int top, unsigned int msec, boost::optional<int> labels) {
347 return getGenResponses(top, labels, [msec](const Rings::Response& r) { return r.usec > msec*1000; });
348 });
349
350
351 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)");
352
353 g_lua.writeFunction("getTopBandwidth", [](unsigned int top) {
354 setLuaNoSideEffect();
355 return g_rings.getTopBandwidth(top);
356 });
357
358 g_lua.executeCode(R"(function topBandwidth(top) top = top or 10; for k,v in ipairs(getTopBandwidth(top)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
359
360 g_lua.writeFunction("delta", []() {
361 setLuaNoSideEffect();
362 // we hold the lua lock already!
363 for(const auto& d : g_confDelta) {
364 struct tm tm;
365 localtime_r(&d.first.tv_sec, &tm);
366 char date[80];
367 strftime(date, sizeof(date)-1, "-- %a %b %d %Y %H:%M:%S %Z\n", &tm);
368 g_outputBuffer += date;
369 g_outputBuffer += d.second + "\n";
370 }
371 });
372
373 g_lua.writeFunction("grepq", [](boost::variant<string, vector<pair<int,string> > > inp, boost::optional<unsigned int> limit) {
374 setLuaNoSideEffect();
375 boost::optional<Netmask> nm;
376 boost::optional<DNSName> dn;
377 int msec=-1;
378
379 vector<string> vec;
380 auto str=boost::get<string>(&inp);
381 if(str)
382 vec.push_back(*str);
383 else {
384 auto v = boost::get<vector<pair<int, string> > >(inp);
385 for(const auto& a: v)
386 vec.push_back(a.second);
387 }
388
389 for(const auto& s : vec) {
390 try
391 {
392 nm = Netmask(s);
393 }
394 catch(...) {
395 if(boost::ends_with(s,"ms") && sscanf(s.c_str(), "%ums", &msec)) {
396 ;
397 }
398 else {
399 try { dn=DNSName(s); }
400 catch(...)
401 {
402 g_outputBuffer = "Could not parse '"+s+"' as domain name or netmask";
403 return;
404 }
405 }
406 }
407 }
408
01f6920b
RG
409 std::vector<Rings::Query> qr;
410 std::vector<Rings::Response> rr;
cfe4b655
RG
411 qr.reserve(g_rings.getNumberOfQueryEntries());
412 rr.reserve(g_rings.getNumberOfResponseEntries());
cdcf7eeb 413 for (const auto& shard : g_rings.d_shards) {
01f6920b 414 {
cdcb5fbd
RG
415 std::lock_guard<std::mutex> rl(shard->queryLock);
416 for (const auto& entry : shard->queryRing) {
01f6920b
RG
417 qr.push_back(entry);
418 }
419 }
420 {
cdcb5fbd
RG
421 std::lock_guard<std::mutex> rl(shard->respLock);
422 for (const auto& entry : shard->respRing) {
01f6920b
RG
423 rr.push_back(entry);
424 }
425 }
6bb38cd6 426 }
01f6920b 427
6bb38cd6
RG
428 sort(qr.begin(), qr.end(), [](const decltype(qr)::value_type& a, const decltype(qr)::value_type& b) {
429 return b.when < a.when;
430 });
6bb38cd6
RG
431
432 sort(rr.begin(), rr.end(), [](const decltype(rr)::value_type& a, const decltype(rr)::value_type& b) {
433 return b.when < a.when;
434 });
435
436 unsigned int num=0;
437 struct timespec now;
438 gettime(&now);
439
440 std::multimap<struct timespec, string> out;
441
442 boost::format fmt("%-7.1f %-47s %-12s %-5d %-25s %-5s %-6.1f %-2s %-2s %-2s %s\n");
443 g_outputBuffer+= (fmt % "Time" % "Client" % "Server" % "ID" % "Name" % "Type" % "Lat." % "TC" % "RD" % "AA" % "Rcode").str();
444
445 if(msec==-1) {
446 for(const auto& c : qr) {
447 bool nmmatch=true, dnmatch=true;
448 if(nm)
449 nmmatch = nm->match(c.requestor);
450 if(dn)
451 dnmatch = c.name.isPartOf(*dn);
452 if(nmmatch && dnmatch) {
453 QType qt(c.qtype);
454 out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % "" % htons(c.dh.id) % c.name.toString() % qt.getName() % "" % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % "Question").str() )) ;
455
456 if(limit && *limit==++num)
457 break;
458 }
459 }
460 }
461 num=0;
462
463
464 string extra;
465 for(const auto& c : rr) {
466 bool nmmatch=true, dnmatch=true, msecmatch=true;
467 if(nm)
468 nmmatch = nm->match(c.requestor);
469 if(dn)
470 dnmatch = c.name.isPartOf(*dn);
471 if(msec != -1)
472 msecmatch=(c.usec/1000 > (unsigned int)msec);
473
474 if(nmmatch && dnmatch && msecmatch) {
475 QType qt(c.qtype);
476 if(!c.dh.rcode)
477 extra=". " +std::to_string(htons(c.dh.ancount))+ " answers";
478 else
479 extra.clear();
480 if(c.usec != std::numeric_limits<decltype(c.usec)>::max())
481 out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % c.ds.toStringWithPort() % htons(c.dh.id) % c.name.toString() % qt.getName() % (c.usec/1000.0) % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str() )) ;
482 else
483 out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % c.ds.toStringWithPort() % htons(c.dh.id) % c.name.toString() % qt.getName() % "T.O" % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str() )) ;
484
485 if(limit && *limit==++num)
486 break;
487 }
488 }
489
490 for(const auto& p : out) {
491 g_outputBuffer+=p.second;
492 }
493 });
494
495 g_lua.writeFunction("showResponseLatency", []() {
496 setLuaNoSideEffect();
497 map<double, unsigned int> histo;
498 double bin=100;
499 for(int i=0; i < 15; ++i) {
500 histo[bin];
501 bin*=2;
502 }
503
504 double totlat=0;
505 unsigned int size=0;
506 {
cdcf7eeb 507 for (const auto& shard : g_rings.d_shards) {
cdcb5fbd
RG
508 std::lock_guard<std::mutex> rl(shard->respLock);
509 for(const auto& r : shard->respRing) {
01f6920b
RG
510 /* skip actively discovered timeouts */
511 if (r.usec == std::numeric_limits<unsigned int>::max())
512 continue;
513
514 ++size;
515 auto iter = histo.lower_bound(r.usec);
516 if(iter != histo.end())
517 iter->second++;
518 else
519 histo.rbegin()++;
520 totlat+=r.usec;
521 }
522 }
6bb38cd6
RG
523 }
524
525 if (size == 0) {
526 g_outputBuffer = "No traffic yet.\n";
527 return;
528 }
529
530 g_outputBuffer = (boost::format("Average response latency: %.02f msec\n") % (0.001*totlat/size)).str();
531 double highest=0;
532
533 for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
534 highest=std::max(highest, iter->second*1.0);
535 }
536 boost::format fmt("%7.2f\t%s\n");
537 g_outputBuffer += (fmt % "msec" % "").str();
538
539 for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
540 int stars = (70.0 * iter->second/highest);
541 char c='*';
542 if(!stars && iter->second) {
543 stars=1; // you get 1 . to show something is there..
544 if(70.0*iter->second/highest > 0.5)
545 c=':';
546 else
547 c='.';
548 }
549 g_outputBuffer += (fmt % (iter->first/1000.0) % string(stars, c)).str();
550 }
551 });
552
553 g_lua.writeFunction("showTCPStats", [] {
554 setLuaNoSideEffect();
555 boost::format fmt("%-10d %-10d %-10d %-10d\n");
556 g_outputBuffer += (fmt % "Clients" % "MaxClients" % "Queued" % "MaxQueued").str();
557 g_outputBuffer += (fmt % g_tcpclientthreads->getThreadsCount() % g_maxTCPClientThreads % g_tcpclientthreads->getQueuedCount() % g_maxTCPQueuedConnections).str();
558 g_outputBuffer += "Query distribution mode is: " + std::string(g_useTCPSinglePipe ? "single queue" : "per-thread queues") + "\n";
559 });
560
561 g_lua.writeFunction("dumpStats", [] {
562 setLuaNoSideEffect();
563 vector<string> leftcolumn, rightcolumn;
564
565 boost::format fmt("%-23s\t%+11s");
566 g_outputBuffer.clear();
567 auto entries = g_stats.entries;
568 sort(entries.begin(), entries.end(),
569 [](const decltype(entries)::value_type& a, const decltype(entries)::value_type& b) {
570 return a.first < b.first;
571 });
572 boost::format flt(" %9.1f");
573 for(const auto& e : entries) {
574 string second;
575 if(const auto& val = boost::get<DNSDistStats::stat_t*>(&e.second))
576 second=std::to_string((*val)->load());
577 else if (const auto& dval = boost::get<double*>(&e.second))
578 second=(flt % (**dval)).str();
579 else
580 second=std::to_string((*boost::get<DNSDistStats::statfunction_t>(&e.second))(e.first));
581
582 if(leftcolumn.size() < g_stats.entries.size()/2)
583 leftcolumn.push_back((fmt % e.first % second).str());
584 else
585 rightcolumn.push_back((fmt % e.first % second).str());
586 }
587
588 auto leftiter=leftcolumn.begin(), rightiter=rightcolumn.begin();
589 boost::format clmn("%|0t|%1% %|39t|%2%\n");
590
591 for(;leftiter != leftcolumn.end() || rightiter != rightcolumn.end();) {
592 string lentry, rentry;
593 if(leftiter!= leftcolumn.end()) {
594 lentry = *leftiter;
595 leftiter++;
596 }
597 if(rightiter!= rightcolumn.end()) {
598 rentry = *rightiter;
599 rightiter++;
600 }
601 g_outputBuffer += (clmn % lentry % rentry).str();
602 }
603 });
604
605 g_lua.writeFunction("exceedServFails", [](unsigned int rate, int seconds) {
606 setLuaNoSideEffect();
607 return exceedRCode(rate, seconds, RCode::ServFail);
608 });
609 g_lua.writeFunction("exceedNXDOMAINs", [](unsigned int rate, int seconds) {
610 setLuaNoSideEffect();
611 return exceedRCode(rate, seconds, RCode::NXDomain);
612 });
613
614 g_lua.writeFunction("exceedRespByterate", [](unsigned int rate, int seconds) {
615 setLuaNoSideEffect();
616 return exceedRespByterate(rate, seconds);
617 });
618
619 g_lua.writeFunction("exceedQTypeRate", [](uint16_t type, unsigned int rate, int seconds) {
620 setLuaNoSideEffect();
621 return exceedQueryGen(rate, seconds, [type](counts_t& counts, const Rings::Query& q) {
622 if(q.qtype==type)
623 counts[q.requestor]++;
624 });
625 });
626
627 g_lua.writeFunction("exceedQRate", [](unsigned int rate, int seconds) {
628 setLuaNoSideEffect();
629 return exceedQueryGen(rate, seconds, [](counts_t& counts, const Rings::Query& q) {
630 counts[q.requestor]++;
631 });
632 });
633
634 g_lua.writeFunction("getRespRing", getRespRing);
635
636 /* StatNode */
637 g_lua.registerFunction<StatNode, unsigned int()>("numChildren",
638 [](StatNode& sn) -> unsigned int {
639 return sn.children.size();
640 } );
641 g_lua.registerMember("fullname", &StatNode::fullname);
642 g_lua.registerMember("labelsCount", &StatNode::labelsCount);
643 g_lua.registerMember("servfails", &StatNode::Stat::servfails);
644 g_lua.registerMember("nxdomains", &StatNode::Stat::nxdomains);
645 g_lua.registerMember("queries", &StatNode::Stat::queries);
646
647 g_lua.writeFunction("statNodeRespRing", [](statvisitor_t visitor, boost::optional<unsigned int> seconds) {
648 statNodeRespRing(visitor, seconds ? *seconds : 0);
649 });
dc2fd93a
RG
650
651 /* DynBlockRulesGroup */
652 g_lua.writeFunction("dynBlockRulesGroup", []() { return std::make_shared<DynBlockRulesGroup>(); });
1d3ba133 653 g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setQueryRate", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
dc2fd93a 654 if (group) {
1d3ba133 655 group->setQueryRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
dc2fd93a
RG
656 }
657 });
1d3ba133 658 g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setResponseByteRate", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
dc2fd93a 659 if (group) {
1d3ba133 660 group->setResponseByteRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
dc2fd93a
RG
661 }
662 });
23adffab
RG
663 g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, DynBlockRulesGroup::smtVisitor_t)>("setSuffixMatchRule", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, DynBlockRulesGroup::smtVisitor_t visitor) {
664 if (group) {
665 group->setSuffixMatchRule(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, visitor);
666 }
667 });
861ce85b
RG
668 g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, dnsdist_ffi_stat_node_visitor_t)>("setSuffixMatchRuleFFI", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, dnsdist_ffi_stat_node_visitor_t visitor) {
669 if (group) {
670 group->setSuffixMatchRuleFFI(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, visitor);
671 }
672 });
1d3ba133 673 g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setRCodeRate", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t rcode, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
dc2fd93a 674 if (group) {
1d3ba133 675 group->setRCodeRate(rcode, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
dc2fd93a
RG
676 }
677 });
1d3ba133 678 g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint16_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setQTypeRate", [](std::shared_ptr<DynBlockRulesGroup>& group, uint16_t qtype, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
dc2fd93a 679 if (group) {
1d3ba133 680 group->setQTypeRate(qtype, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
dc2fd93a
RG
681 }
682 });
b718792f
RG
683 g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("excludeRange", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) {
684 if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) {
685 for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) {
686 group->excludeRange(Netmask(range.second));
687 }
688 }
689 else {
690 group->excludeRange(Netmask(*boost::get<std::string>(&ranges)));
691 }
692 });
693 g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("includeRange", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) {
694 if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) {
695 for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) {
696 group->includeRange(Netmask(range.second));
697 }
698 }
699 else {
700 group->includeRange(Netmask(*boost::get<std::string>(&ranges)));
701 }
702 });
23adffab
RG
703 g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("excludeDomains", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, std::vector<std::pair<int, std::string>>> domains) {
704 if (domains.type() == typeid(std::vector<std::pair<int, std::string>>)) {
705 for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&domains)) {
706 group->excludeDomain(DNSName(range.second));
707 }
708 }
709 else {
710 group->excludeDomain(DNSName(*boost::get<std::string>(&domains)));
711 }
712 });
1d3ba133
RG
713 g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)()>("apply", [](std::shared_ptr<DynBlockRulesGroup>& group) {
714 group->apply();
715 });
b718792f 716 g_lua.registerFunction("toString", &DynBlockRulesGroup::toString);
6bb38cd6 717}