]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsdist-lua-inspection.cc
auth: switch circleci mssql image
[thirdparty/pdns.git] / pdns / dnsdist-lua-inspection.cc
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"
24 #include "dnsdist-dynblocks.hh"
25 #include "dnsdist-rings.hh"
26
27 #include "statnode.hh"
28
29 static 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)
30 {
31 setLuaNoSideEffect();
32 map<DNSName, unsigned int> counts;
33 unsigned int total=0;
34 {
35 for (const auto& shard : g_rings.d_shards) {
36 std::lock_guard<std::mutex> rl(shard->respLock);
37 if(!labels) {
38 for(const auto& a : shard->respRing) {
39 if(!pred(a))
40 continue;
41 counts[a.name]++;
42 total++;
43 }
44 }
45 else {
46 unsigned int lab = *labels;
47 for(const auto& a : shard->respRing) {
48 if(!pred(a))
49 continue;
50
51 DNSName temp(a.name);
52 temp.trimToLabels(lab);
53 counts[temp]++;
54 total++;
55 }
56 }
57 }
58 }
59 // cout<<"Looked at "<<total<<" responses, "<<counts.size()<<" different ones"<<endl;
60 vector<pair<unsigned int, DNSName>> rcounts;
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
70 std::unordered_map<unsigned int, vector<boost::variant<string,double>>> ret;
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
82 typedef std::unordered_map<ComboAddress, unsigned int, ComboAddress::addressOnlyHash, ComboAddress::addressOnlyEqual> counts_t;
83
84 static counts_t filterScore(const counts_t& counts,
85 double delta, unsigned int rate)
86 {
87 counts_t ret;
88
89 double lim = delta*rate;
90 for(const auto& c : counts) {
91 if (c.second > lim) {
92 ret[c.first] = c.second;
93 }
94 }
95
96 return ret;
97 }
98
99
100 typedef std::function<void(const StatNode&, const StatNode::Stat&, const StatNode::Stat&)> statvisitor_t;
101
102 static void statNodeRespRing(statvisitor_t visitor, unsigned int seconds)
103 {
104 struct timespec cutoff, now;
105 gettime(&now);
106 cutoff = now;
107 cutoff.tv_sec -= seconds;
108
109 StatNode root;
110 for (const auto& shard : g_rings.d_shards) {
111 std::lock_guard<std::mutex> rl(shard->respLock);
112
113 for(const auto& c : shard->respRing) {
114 if (now < c.when)
115 continue;
116
117 if (seconds && c.when < cutoff)
118 continue;
119
120 root.submit(c.name, c.dh.rcode, boost::none);
121 }
122 }
123
124 StatNode::Stat node;
125 root.visit([visitor](const StatNode* node_, const StatNode::Stat& self, const StatNode::Stat& children) {
126 visitor(*node_, self, children);}, node);
127 }
128
129 static 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;
133
134 for (const auto& shard : g_rings.d_shards) {
135 std::lock_guard<std::mutex> rl(shard->respLock);
136
137 entry_t e;
138 unsigned int count=1;
139 for(const auto& c : shard->respRing) {
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 }
147 }
148
149 return ret;
150 }
151
152 static counts_t exceedRespGen(unsigned int rate, int seconds, std::function<void(counts_t&, const Rings::Response&)> T)
153 {
154 counts_t counts;
155 struct timespec cutoff, mintime, now;
156 gettime(&now);
157 cutoff = mintime = now;
158 cutoff.tv_sec -= seconds;
159
160 counts.reserve(g_rings.getNumberOfResponseEntries());
161
162 for (const auto& shard : g_rings.d_shards) {
163 std::lock_guard<std::mutex> rl(shard->respLock);
164 for(const auto& c : shard->respRing) {
165
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 }
175 }
176
177 double delta = seconds ? seconds : DiffTime(now, mintime);
178 return filterScore(counts, delta, rate);
179 }
180
181 static counts_t exceedQueryGen(unsigned int rate, int seconds, std::function<void(counts_t&, const Rings::Query&)> T)
182 {
183 counts_t counts;
184 struct timespec cutoff, mintime, now;
185 gettime(&now);
186 cutoff = mintime = now;
187 cutoff.tv_sec -= seconds;
188
189 counts.reserve(g_rings.getNumberOfQueryEntries());
190
191 for (const auto& shard : g_rings.d_shards) {
192 std::lock_guard<std::mutex> rl(shard->queryLock);
193 for(const auto& c : shard->queryRing) {
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 }
202 }
203
204 double delta = seconds ? seconds : DiffTime(now, mintime);
205 return filterScore(counts, delta, rate);
206 }
207
208
209 static counts_t exceedRCode(unsigned int rate, int seconds, int rcode)
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
218 static counts_t exceedRespByterate(unsigned int rate, int seconds)
219 {
220 return exceedRespGen(rate, seconds, [](counts_t& counts, const Rings::Response& r)
221 {
222 counts[r.requestor]+=r.size;
223 });
224 }
225
226 void setupLuaInspection()
227 {
228 g_lua.writeFunction("topClients", [](boost::optional<unsigned int> top_) {
229 setLuaNoSideEffect();
230 auto top = top_.get_value_or(10);
231 map<ComboAddress, unsigned int,ComboAddress::addressOnlyLessThan > counts;
232 unsigned int total=0;
233 {
234 for (const auto& shard : g_rings.d_shards) {
235 std::lock_guard<std::mutex> rl(shard->queryLock);
236 for(const auto& c : shard->queryRing) {
237 counts[c.requestor]++;
238 total++;
239 }
240 }
241 }
242 vector<pair<unsigned int, ComboAddress>> rcounts;
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();
264 map<DNSName, unsigned int> counts;
265 unsigned int total=0;
266 if(!labels) {
267 for (const auto& shard : g_rings.d_shards) {
268 std::lock_guard<std::mutex> rl(shard->queryLock);
269 for(const auto& a : shard->queryRing) {
270 counts[a.name]++;
271 total++;
272 }
273 }
274 }
275 else {
276 unsigned int lab = *labels;
277 for (const auto& shard : g_rings.d_shards) {
278 std::lock_guard<std::mutex> rl(shard->queryLock);
279 for(auto a : shard->queryRing) {
280 a.name.trimToLabels(lab);
281 counts[a.name]++;
282 total++;
283 }
284 }
285 }
286 // cout<<"Looked at "<<total<<" queries, "<<counts.size()<<" different ones"<<endl;
287 vector<pair<unsigned int, DNSName>> rcounts;
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
297 std::unordered_map<unsigned int, vector<boost::variant<string,double>>> ret;
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();
314 size_t totalEntries = 0;
315 std::vector<boost::circular_buffer<Rings::Response>> rings;
316 rings.reserve(g_rings.getNumberOfShards());
317 for (const auto& shard : g_rings.d_shards) {
318 {
319 std::lock_guard<std::mutex> rl(shard->respLock);
320 rings.push_back(shard->respRing);
321 }
322 totalEntries += rings.back().size();
323 }
324 vector<std::unordered_map<string, boost::variant<string, unsigned int> > > ret;
325 ret.reserve(totalEntries);
326 decltype(ret)::value_type item;
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 }
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
409 std::vector<Rings::Query> qr;
410 std::vector<Rings::Response> rr;
411 qr.reserve(g_rings.getNumberOfQueryEntries());
412 rr.reserve(g_rings.getNumberOfResponseEntries());
413 for (const auto& shard : g_rings.d_shards) {
414 {
415 std::lock_guard<std::mutex> rl(shard->queryLock);
416 for (const auto& entry : shard->queryRing) {
417 qr.push_back(entry);
418 }
419 }
420 {
421 std::lock_guard<std::mutex> rl(shard->respLock);
422 for (const auto& entry : shard->respRing) {
423 rr.push_back(entry);
424 }
425 }
426 }
427
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 });
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 {
507 for (const auto& shard : g_rings.d_shards) {
508 std::lock_guard<std::mutex> rl(shard->respLock);
509 for(const auto& r : shard->respRing) {
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 }
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 ostringstream ret;
556 boost::format fmt("%-10d %-10d %-10d %-10d\n");
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;
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;
567
568 size_t counter = 0;
569 for(const auto& f : g_frontends) {
570 ret << (fmt % counter % f->local.toStringWithPort() % f->tcpCurrentConnections % f->tcpDiedReadingQuery % f->tcpDiedSendingResponse % f->tcpGaveUp % f->tcpClientTimeouts % f->tcpDownstreamTimeouts % f->tcpAvgQueriesPerConnection % f->tcpAvgConnectionDuration) << endl;
571 ++counter;
572 }
573 ret << endl;
574
575 ret << "Backends:" << endl;
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;
578
579 auto states = g_dstates.getLocal();
580 counter = 0;
581 for(const auto& s : *states) {
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;
583 ++counter;
584 }
585
586 g_outputBuffer=ret.str();
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 });
678
679 /* DynBlockRulesGroup */
680 g_lua.writeFunction("dynBlockRulesGroup", []() { return std::make_shared<DynBlockRulesGroup>(); });
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) {
682 if (group) {
683 group->setQueryRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
684 }
685 });
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) {
687 if (group) {
688 group->setResponseByteRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
689 }
690 });
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 });
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 });
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) {
702 if (group) {
703 group->setRCodeRate(rcode, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
704 }
705 });
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) {
707 if (group) {
708 group->setQTypeRate(qtype, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
709 }
710 });
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 });
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 });
741 g_lua.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)()>("apply", [](std::shared_ptr<DynBlockRulesGroup>& group) {
742 group->apply();
743 });
744 g_lua.registerFunction("toString", &DynBlockRulesGroup::toString);
745 }