]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-lua-inspection.cc
minicurl: correct comment
[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();
781fd8fd 555 ostringstream ret;
6bb38cd6 556 boost::format fmt("%-10d %-10d %-10d %-10d\n");
781fd8fd
RG
557 ret << (fmt % "Clients" % "MaxClients" % "Queued" % "MaxQueued") << endl;
558 ret << (fmt % g_tcpclientthreads->getThreadsCount() % g_maxTCPClientThreads % g_tcpclientthreads->getQueuedCount() % g_maxTCPQueuedConnections) << endl;
559 ret <<endl;
560
561 ret << "Query distribution mode is: " << std::string(g_useTCPSinglePipe ? "single queue" : "per-thread queues") << endl;
562 ret << endl;
563
564 ret << "Frontends:" << endl;
cff9aa03
RG
565 fmt = boost::format("%-3d %-20.20s %-20d %-20d %-25d %-20d %-20d %-20d %-20f %-20f");
566 ret << (fmt % "#" % "Address" % "Connnections" % "Died reading query" % "Died sending response" % "Gave up" % "Client timeouts" % "Downstream timeouts" % "Avg queries/conn" % "Avg duration") << endl;
781fd8fd
RG
567
568 size_t counter = 0;
569 for(const auto& f : g_frontends) {
cff9aa03 570 ret << (fmt % counter % f->local.toStringWithPort() % f->tcpCurrentConnections % f->tcpDiedReadingQuery % f->tcpDiedSendingResponse % f->tcpGaveUp % f->tcpClientTimeouts % f->tcpDownstreamTimeouts % f->tcpAvgQueriesPerConnection % f->tcpAvgConnectionDuration) << endl;
781fd8fd
RG
571 ++counter;
572 }
573 ret << endl;
574
575 ret << "Backends:" << endl;
cff9aa03
RG
576 fmt = boost::format("%-3d %-20.20s %-20.20s %-20d %-20d %-25d %-20d %-20d %-20d %-20f %-20f");
577 ret << (fmt % "#" % "Name" % "Address" % "Connections" % "Died sending query" % "Died reading response" % "Gave up" % "Read timeouts" % "Write timeouts" % "Avg queries/conn" % "Avg duration") << endl;
781fd8fd
RG
578
579 auto states = g_dstates.getLocal();
580 counter = 0;
581 for(const auto& s : *states) {
cff9aa03 582 ret << (fmt % counter % s->name % s->remote.toStringWithPort() % s->tcpCurrentConnections % s->tcpDiedSendingQuery % s->tcpDiedReadingResponse % s->tcpGaveUp % s->tcpReadTimeouts % s->tcpWriteTimeouts % s->tcpAvgQueriesPerConnection % s->tcpAvgConnectionDuration) << endl;
781fd8fd
RG
583 ++counter;
584 }
585
586 g_outputBuffer=ret.str();
6bb38cd6
RG
587 });
588
589 g_lua.writeFunction("dumpStats", [] {
590 setLuaNoSideEffect();
591 vector<string> leftcolumn, rightcolumn;
592
593 boost::format fmt("%-23s\t%+11s");
594 g_outputBuffer.clear();
595 auto entries = g_stats.entries;
596 sort(entries.begin(), entries.end(),
597 [](const decltype(entries)::value_type& a, const decltype(entries)::value_type& b) {
598 return a.first < b.first;
599 });
600 boost::format flt(" %9.1f");
601 for(const auto& e : entries) {
602 string second;
603 if(const auto& val = boost::get<DNSDistStats::stat_t*>(&e.second))
604 second=std::to_string((*val)->load());
605 else if (const auto& dval = boost::get<double*>(&e.second))
606 second=(flt % (**dval)).str();
607 else
608 second=std::to_string((*boost::get<DNSDistStats::statfunction_t>(&e.second))(e.first));
609
610 if(leftcolumn.size() < g_stats.entries.size()/2)
611 leftcolumn.push_back((fmt % e.first % second).str());
612 else
613 rightcolumn.push_back((fmt % e.first % second).str());
614 }
615
616 auto leftiter=leftcolumn.begin(), rightiter=rightcolumn.begin();
617 boost::format clmn("%|0t|%1% %|39t|%2%\n");
618
619 for(;leftiter != leftcolumn.end() || rightiter != rightcolumn.end();) {
620 string lentry, rentry;
621 if(leftiter!= leftcolumn.end()) {
622 lentry = *leftiter;
623 leftiter++;
624 }
625 if(rightiter!= rightcolumn.end()) {
626 rentry = *rightiter;
627 rightiter++;
628 }
629 g_outputBuffer += (clmn % lentry % rentry).str();
630 }
631 });
632
633 g_lua.writeFunction("exceedServFails", [](unsigned int rate, int seconds) {
634 setLuaNoSideEffect();
635 return exceedRCode(rate, seconds, RCode::ServFail);
636 });
637 g_lua.writeFunction("exceedNXDOMAINs", [](unsigned int rate, int seconds) {
638 setLuaNoSideEffect();
639 return exceedRCode(rate, seconds, RCode::NXDomain);
640 });
641
642 g_lua.writeFunction("exceedRespByterate", [](unsigned int rate, int seconds) {
643 setLuaNoSideEffect();
644 return exceedRespByterate(rate, seconds);
645 });
646
647 g_lua.writeFunction("exceedQTypeRate", [](uint16_t type, unsigned int rate, int seconds) {
648 setLuaNoSideEffect();
649 return exceedQueryGen(rate, seconds, [type](counts_t& counts, const Rings::Query& q) {
650 if(q.qtype==type)
651 counts[q.requestor]++;
652 });
653 });
654
655 g_lua.writeFunction("exceedQRate", [](unsigned int rate, int seconds) {
656 setLuaNoSideEffect();
657 return exceedQueryGen(rate, seconds, [](counts_t& counts, const Rings::Query& q) {
658 counts[q.requestor]++;
659 });
660 });
661
662 g_lua.writeFunction("getRespRing", getRespRing);
663
664 /* StatNode */
665 g_lua.registerFunction<StatNode, unsigned int()>("numChildren",
666 [](StatNode& sn) -> unsigned int {
667 return sn.children.size();
668 } );
669 g_lua.registerMember("fullname", &StatNode::fullname);
670 g_lua.registerMember("labelsCount", &StatNode::labelsCount);
671 g_lua.registerMember("servfails", &StatNode::Stat::servfails);
672 g_lua.registerMember("nxdomains", &StatNode::Stat::nxdomains);
673 g_lua.registerMember("queries", &StatNode::Stat::queries);
674
675 g_lua.writeFunction("statNodeRespRing", [](statvisitor_t visitor, boost::optional<unsigned int> seconds) {
676 statNodeRespRing(visitor, seconds ? *seconds : 0);
677 });
dc2fd93a
RG
678
679 /* DynBlockRulesGroup */
680 g_lua.writeFunction("dynBlockRulesGroup", []() { return std::make_shared<DynBlockRulesGroup>(); });
1d3ba133 681 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 682 if (group) {
1d3ba133 683 group->setQueryRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
dc2fd93a
RG
684 }
685 });
1d3ba133 686 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 687 if (group) {
1d3ba133 688 group->setResponseByteRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
dc2fd93a
RG
689 }
690 });
23adffab
RG
691 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) {
692 if (group) {
693 group->setSuffixMatchRule(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, visitor);
694 }
695 });
861ce85b
RG
696 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) {
697 if (group) {
698 group->setSuffixMatchRuleFFI(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, visitor);
699 }
700 });
1d3ba133 701 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 702 if (group) {
1d3ba133 703 group->setRCodeRate(rcode, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
dc2fd93a
RG
704 }
705 });
1d3ba133 706 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 707 if (group) {
1d3ba133 708 group->setQTypeRate(qtype, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
dc2fd93a
RG
709 }
710 });
b718792f
RG
711 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) {
712 if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) {
713 for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) {
714 group->excludeRange(Netmask(range.second));
715 }
716 }
717 else {
718 group->excludeRange(Netmask(*boost::get<std::string>(&ranges)));
719 }
720 });
721 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) {
722 if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) {
723 for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) {
724 group->includeRange(Netmask(range.second));
725 }
726 }
727 else {
728 group->includeRange(Netmask(*boost::get<std::string>(&ranges)));
729 }
730 });
23adffab
RG
731 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) {
732 if (domains.type() == typeid(std::vector<std::pair<int, std::string>>)) {
733 for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&domains)) {
734 group->excludeDomain(DNSName(range.second));
735 }
736 }
737 else {
738 group->excludeDomain(DNSName(*boost::get<std::string>(&domains)));
739 }
740 });
1d3ba133
RG
741 g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)()>("apply", [](std::shared_ptr<DynBlockRulesGroup>& group) {
742 group->apply();
743 });
b718792f 744 g_lua.registerFunction("toString", &DynBlockRulesGroup::toString);
6bb38cd6 745}