]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-lua-inspection.cc
Limit the number of queries sent out to get NS addresses per query.
[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
92375564 120 root.submit(c.name, ((c.dh.rcode == 0 && c.usec == std::numeric_limits<unsigned int>::max()) ? -1 : c.dh.rcode), c.size, 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
f3c9f421 442 boost::format fmt("%-7.1f %-47s %-12s %-5d %-25s %-5s %-6.1f %-2s %-2s %-2s %-s\n");
6bb38cd6
RG
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);
f3c9f421
RG
454 std::string extra;
455 if (c.dh.opcode != 0) {
456 extra = " (" + Opcode::to_s(c.dh.opcode) + ")";
457 }
458 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" + extra)).str() )) ;
6bb38cd6
RG
459
460 if(limit && *limit==++num)
461 break;
462 }
463 }
464 }
465 num=0;
466
467
468 string extra;
469 for(const auto& c : rr) {
470 bool nmmatch=true, dnmatch=true, msecmatch=true;
471 if(nm)
472 nmmatch = nm->match(c.requestor);
473 if(dn)
474 dnmatch = c.name.isPartOf(*dn);
475 if(msec != -1)
476 msecmatch=(c.usec/1000 > (unsigned int)msec);
477
478 if(nmmatch && dnmatch && msecmatch) {
479 QType qt(c.qtype);
480 if(!c.dh.rcode)
481 extra=". " +std::to_string(htons(c.dh.ancount))+ " answers";
482 else
483 extra.clear();
484 if(c.usec != std::numeric_limits<decltype(c.usec)>::max())
485 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() )) ;
486 else
487 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() )) ;
488
489 if(limit && *limit==++num)
490 break;
491 }
492 }
493
494 for(const auto& p : out) {
495 g_outputBuffer+=p.second;
496 }
497 });
498
499 g_lua.writeFunction("showResponseLatency", []() {
500 setLuaNoSideEffect();
501 map<double, unsigned int> histo;
502 double bin=100;
503 for(int i=0; i < 15; ++i) {
504 histo[bin];
505 bin*=2;
506 }
507
508 double totlat=0;
509 unsigned int size=0;
510 {
cdcf7eeb 511 for (const auto& shard : g_rings.d_shards) {
cdcb5fbd
RG
512 std::lock_guard<std::mutex> rl(shard->respLock);
513 for(const auto& r : shard->respRing) {
01f6920b
RG
514 /* skip actively discovered timeouts */
515 if (r.usec == std::numeric_limits<unsigned int>::max())
516 continue;
517
518 ++size;
519 auto iter = histo.lower_bound(r.usec);
520 if(iter != histo.end())
521 iter->second++;
522 else
523 histo.rbegin()++;
524 totlat+=r.usec;
525 }
526 }
6bb38cd6
RG
527 }
528
529 if (size == 0) {
530 g_outputBuffer = "No traffic yet.\n";
531 return;
532 }
533
534 g_outputBuffer = (boost::format("Average response latency: %.02f msec\n") % (0.001*totlat/size)).str();
535 double highest=0;
536
537 for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
538 highest=std::max(highest, iter->second*1.0);
539 }
540 boost::format fmt("%7.2f\t%s\n");
541 g_outputBuffer += (fmt % "msec" % "").str();
542
543 for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
544 int stars = (70.0 * iter->second/highest);
545 char c='*';
546 if(!stars && iter->second) {
547 stars=1; // you get 1 . to show something is there..
548 if(70.0*iter->second/highest > 0.5)
549 c=':';
550 else
551 c='.';
552 }
553 g_outputBuffer += (fmt % (iter->first/1000.0) % string(stars, c)).str();
554 }
555 });
556
557 g_lua.writeFunction("showTCPStats", [] {
558 setLuaNoSideEffect();
781fd8fd 559 ostringstream ret;
2ce94dc3
RG
560 boost::format fmt("%-12d %-12d %-12d %-12d");
561 ret << (fmt % "Workers" % "Max Workers" % "Queued" % "Max Queued") << endl;
781fd8fd 562 ret << (fmt % g_tcpclientthreads->getThreadsCount() % g_maxTCPClientThreads % g_tcpclientthreads->getQueuedCount() % g_maxTCPQueuedConnections) << endl;
2ce94dc3 563 ret << endl;
781fd8fd
RG
564
565 ret << "Query distribution mode is: " << std::string(g_useTCPSinglePipe ? "single queue" : "per-thread queues") << endl;
566 ret << endl;
567
568 ret << "Frontends:" << endl;
2ce94dc3 569 fmt = boost::format("%-3d %-20.20s %-20d %-20d %-25d %-20d %-20d %-20d %-20f %-20f %-20d %-20d %-25d %-25d %-15d %-15d %-15d %-15d %-15d");
b608e6c6 570 ret << (fmt % "#" % "Address" % "Connections" % "Died reading query" % "Died sending response" % "Gave up" % "Client timeouts" % "Downstream timeouts" % "Avg queries/conn" % "Avg duration" % "TLS new sessions" % "TLS Resumptions" % "TLS unknown ticket keys" % "TLS inactive ticket keys" % "TLS 1.0" % "TLS 1.1" % "TLS 1.2" % "TLS 1.3" % "TLS other") << endl;
781fd8fd
RG
571
572 size_t counter = 0;
573 for(const auto& f : g_frontends) {
b608e6c6 574 ret << (fmt % counter % f->local.toStringWithPort() % f->tcpCurrentConnections % f->tcpDiedReadingQuery % f->tcpDiedSendingResponse % f->tcpGaveUp % f->tcpClientTimeouts % f->tcpDownstreamTimeouts % f->tcpAvgQueriesPerConnection % f->tcpAvgConnectionDuration % f->tlsNewSessions % f->tlsResumptions % f->tlsUnknownTicketKey % f->tlsInactiveTicketKey % f->tls10queries % f->tls11queries % f->tls12queries % f->tls13queries % f->tlsUnknownqueries) << endl;
781fd8fd
RG
575 ++counter;
576 }
577 ret << endl;
578
579 ret << "Backends:" << endl;
cff9aa03
RG
580 fmt = boost::format("%-3d %-20.20s %-20.20s %-20d %-20d %-25d %-20d %-20d %-20d %-20f %-20f");
581 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
582
583 auto states = g_dstates.getLocal();
584 counter = 0;
585 for(const auto& s : *states) {
be05aa91 586 ret << (fmt % counter % s->getName() % s->remote.toStringWithPort() % s->tcpCurrentConnections % s->tcpDiedSendingQuery % s->tcpDiedReadingResponse % s->tcpGaveUp % s->tcpReadTimeouts % s->tcpWriteTimeouts % s->tcpAvgQueriesPerConnection % s->tcpAvgConnectionDuration) << endl;
781fd8fd
RG
587 ++counter;
588 }
589
590 g_outputBuffer=ret.str();
6bb38cd6
RG
591 });
592
f34fdcc5
RG
593 g_lua.writeFunction("showTLSErrorCounters", [] {
594 setLuaNoSideEffect();
595 ostringstream ret;
596 boost::format fmt("%-3d %-20.20s %-23d %-23d %-23d %-23d %-23d %-23d %-23d %-23d");
597
598 ret << (fmt % "#" % "Address" % "DH key too small" % "Inappropriate fallback" % "No shared cipher" % "Unknown cipher type" % "Unknown exchange type" % "Unknown protocol" % "Unsupported EC" % "Unsupported protocol") << endl;
599
600 size_t counter = 0;
601 for(const auto& f : g_frontends) {
602 if (!f->hasTLS()) {
603 continue;
604 }
605 const TLSErrorCounters* errorCounters = nullptr;
606 if (f->tlsFrontend != nullptr) {
607 errorCounters = &f->tlsFrontend->d_tlsCounters;
608 }
609 else if (f->dohFrontend != nullptr) {
610 errorCounters = &f->dohFrontend->d_tlsCounters;
611 }
612 if (errorCounters == nullptr) {
613 continue;
614 }
615
616 ret << (fmt % counter % f->local.toStringWithPort() % errorCounters->d_dhKeyTooSmall % errorCounters->d_inappropriateFallBack % errorCounters->d_noSharedCipher % errorCounters->d_unknownCipherType % errorCounters->d_unknownKeyExchangeType % errorCounters->d_unknownProtocol % errorCounters->d_unsupportedEC % errorCounters->d_unsupportedProtocol) << endl;
617 ++counter;
618 }
619 ret << endl;
620
621 g_outputBuffer=ret.str();
622 });
623
6bb38cd6
RG
624 g_lua.writeFunction("dumpStats", [] {
625 setLuaNoSideEffect();
626 vector<string> leftcolumn, rightcolumn;
627
628 boost::format fmt("%-23s\t%+11s");
629 g_outputBuffer.clear();
630 auto entries = g_stats.entries;
631 sort(entries.begin(), entries.end(),
632 [](const decltype(entries)::value_type& a, const decltype(entries)::value_type& b) {
633 return a.first < b.first;
634 });
635 boost::format flt(" %9.1f");
636 for(const auto& e : entries) {
637 string second;
638 if(const auto& val = boost::get<DNSDistStats::stat_t*>(&e.second))
639 second=std::to_string((*val)->load());
640 else if (const auto& dval = boost::get<double*>(&e.second))
641 second=(flt % (**dval)).str();
642 else
643 second=std::to_string((*boost::get<DNSDistStats::statfunction_t>(&e.second))(e.first));
644
645 if(leftcolumn.size() < g_stats.entries.size()/2)
646 leftcolumn.push_back((fmt % e.first % second).str());
647 else
648 rightcolumn.push_back((fmt % e.first % second).str());
649 }
650
651 auto leftiter=leftcolumn.begin(), rightiter=rightcolumn.begin();
652 boost::format clmn("%|0t|%1% %|39t|%2%\n");
653
654 for(;leftiter != leftcolumn.end() || rightiter != rightcolumn.end();) {
655 string lentry, rentry;
656 if(leftiter!= leftcolumn.end()) {
657 lentry = *leftiter;
658 leftiter++;
659 }
660 if(rightiter!= rightcolumn.end()) {
661 rentry = *rightiter;
662 rightiter++;
663 }
664 g_outputBuffer += (clmn % lentry % rentry).str();
665 }
666 });
667
668 g_lua.writeFunction("exceedServFails", [](unsigned int rate, int seconds) {
669 setLuaNoSideEffect();
670 return exceedRCode(rate, seconds, RCode::ServFail);
671 });
672 g_lua.writeFunction("exceedNXDOMAINs", [](unsigned int rate, int seconds) {
673 setLuaNoSideEffect();
674 return exceedRCode(rate, seconds, RCode::NXDomain);
675 });
676
677 g_lua.writeFunction("exceedRespByterate", [](unsigned int rate, int seconds) {
678 setLuaNoSideEffect();
679 return exceedRespByterate(rate, seconds);
680 });
681
682 g_lua.writeFunction("exceedQTypeRate", [](uint16_t type, unsigned int rate, int seconds) {
683 setLuaNoSideEffect();
684 return exceedQueryGen(rate, seconds, [type](counts_t& counts, const Rings::Query& q) {
685 if(q.qtype==type)
686 counts[q.requestor]++;
687 });
688 });
689
690 g_lua.writeFunction("exceedQRate", [](unsigned int rate, int seconds) {
691 setLuaNoSideEffect();
692 return exceedQueryGen(rate, seconds, [](counts_t& counts, const Rings::Query& q) {
693 counts[q.requestor]++;
694 });
695 });
696
697 g_lua.writeFunction("getRespRing", getRespRing);
698
699 /* StatNode */
700 g_lua.registerFunction<StatNode, unsigned int()>("numChildren",
701 [](StatNode& sn) -> unsigned int {
702 return sn.children.size();
703 } );
704 g_lua.registerMember("fullname", &StatNode::fullname);
705 g_lua.registerMember("labelsCount", &StatNode::labelsCount);
706 g_lua.registerMember("servfails", &StatNode::Stat::servfails);
707 g_lua.registerMember("nxdomains", &StatNode::Stat::nxdomains);
708 g_lua.registerMember("queries", &StatNode::Stat::queries);
64f2368a
RG
709 g_lua.registerMember("noerrors", &StatNode::Stat::noerrors);
710 g_lua.registerMember("drops", &StatNode::Stat::drops);
92375564 711 g_lua.registerMember("bytes", &StatNode::Stat::bytes);
6bb38cd6
RG
712
713 g_lua.writeFunction("statNodeRespRing", [](statvisitor_t visitor, boost::optional<unsigned int> seconds) {
714 statNodeRespRing(visitor, seconds ? *seconds : 0);
715 });
dc2fd93a
RG
716
717 /* DynBlockRulesGroup */
718 g_lua.writeFunction("dynBlockRulesGroup", []() { return std::make_shared<DynBlockRulesGroup>(); });
1d3ba133 719 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 720 if (group) {
1d3ba133 721 group->setQueryRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
dc2fd93a
RG
722 }
723 });
1d3ba133 724 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 725 if (group) {
1d3ba133 726 group->setResponseByteRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
dc2fd93a
RG
727 }
728 });
23adffab
RG
729 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) {
730 if (group) {
731 group->setSuffixMatchRule(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, visitor);
732 }
733 });
861ce85b
RG
734 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) {
735 if (group) {
736 group->setSuffixMatchRuleFFI(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, visitor);
737 }
738 });
1d3ba133 739 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 740 if (group) {
1d3ba133 741 group->setRCodeRate(rcode, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
dc2fd93a
RG
742 }
743 });
838c2f00
RG
744 g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, double, unsigned int, const std::string&, unsigned int, size_t, boost::optional<DNSAction::Action>, boost::optional<double>)>("setRCodeRatio", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t rcode, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, boost::optional<DNSAction::Action> action, boost::optional<double> warningRatio) {
745 if (group) {
746 group->setRCodeRatio(rcode, ratio, warningRatio ? *warningRatio : 0.0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, minimumNumberOfResponses);
747 }
748 });
1d3ba133 749 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 750 if (group) {
1d3ba133 751 group->setQTypeRate(qtype, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
dc2fd93a
RG
752 }
753 });
b718792f
RG
754 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) {
755 if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) {
756 for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) {
757 group->excludeRange(Netmask(range.second));
758 }
759 }
760 else {
761 group->excludeRange(Netmask(*boost::get<std::string>(&ranges)));
762 }
763 });
764 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) {
765 if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) {
766 for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) {
767 group->includeRange(Netmask(range.second));
768 }
769 }
770 else {
771 group->includeRange(Netmask(*boost::get<std::string>(&ranges)));
772 }
773 });
23adffab
RG
774 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) {
775 if (domains.type() == typeid(std::vector<std::pair<int, std::string>>)) {
776 for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&domains)) {
777 group->excludeDomain(DNSName(range.second));
778 }
779 }
780 else {
781 group->excludeDomain(DNSName(*boost::get<std::string>(&domains)));
782 }
783 });
1d3ba133
RG
784 g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)()>("apply", [](std::shared_ptr<DynBlockRulesGroup>& group) {
785 group->apply();
786 });
2bd700b1 787 g_lua.registerFunction("setQuiet", &DynBlockRulesGroup::setQuiet);
b718792f 788 g_lua.registerFunction("toString", &DynBlockRulesGroup::toString);
6bb38cd6 789}