2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
23 #include "dnsdist-lua.hh"
24 #include "dnsdist-dynblocks.hh"
25 #include "dnsdist-rings.hh"
27 #include "statnode.hh"
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
)
32 map
<DNSName
, unsigned int> counts
;
35 for (const auto& shard
: g_rings
.d_shards
) {
36 std::lock_guard
<std::mutex
> rl(shard
->respLock
);
38 for(const auto& a
: shard
->respRing
) {
46 unsigned int lab
= *labels
;
47 for(const auto& a
: shard
->respRing
) {
52 temp
.trimToLabels(lab
);
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()));
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
;
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
) {
76 ret
.insert({count
++, {rc
.second
.toString(), rc
.first
, 100.0*rc
.first
/total
}});
78 ret
.insert({count
, {"Rest", rest
, total
> 0 ? 100.0*rest
/total
: 100.0}});
82 typedef std::unordered_map
<ComboAddress
, unsigned int, ComboAddress::addressOnlyHash
, ComboAddress::addressOnlyEqual
> counts_t
;
84 static counts_t
filterScore(const counts_t
& counts
,
85 double delta
, unsigned int rate
)
89 double lim
= delta
*rate
;
90 for(const auto& c
: counts
) {
92 ret
[c
.first
] = c
.second
;
100 typedef std::function
<void(const StatNode
&, const StatNode::Stat
&, const StatNode::Stat
&)> statvisitor_t
;
102 static void statNodeRespRing(statvisitor_t visitor
, unsigned int seconds
)
104 struct timespec cutoff
, now
;
107 cutoff
.tv_sec
-= seconds
;
110 for (const auto& shard
: g_rings
.d_shards
) {
111 std::lock_guard
<std::mutex
> rl(shard
->respLock
);
113 for(const auto& c
: shard
->respRing
) {
117 if (seconds
&& c
.when
< cutoff
)
120 root
.submit(c
.name
, c
.dh
.rcode
, boost::none
);
125 root
.visit([visitor
](const StatNode
* node_
, const StatNode::Stat
& self
, const StatNode::Stat
& children
) {
126 visitor(*node_
, self
, children
);}, node
);
129 static vector
<pair
<unsigned int, std::unordered_map
<string
,string
> > > getRespRing(boost::optional
<int> rcode
)
131 typedef std::unordered_map
<string
,string
> entry_t
;
132 vector
<pair
<unsigned int, entry_t
> > ret
;
134 for (const auto& shard
: g_rings
.d_shards
) {
135 std::lock_guard
<std::mutex
> rl(shard
->respLock
);
138 unsigned int count
=1;
139 for(const auto& c
: shard
->respRing
) {
140 if(rcode
&& (rcode
.get() != c
.dh
.rcode
))
142 e
["qname"]=c
.name
.toString();
143 e
["rcode"]=std::to_string(c
.dh
.rcode
);
144 ret
.push_back(std::make_pair(count
,e
));
152 static counts_t
exceedRespGen(unsigned int rate
, int seconds
, std::function
<void(counts_t
&, const Rings::Response
&)> T
)
155 struct timespec cutoff
, mintime
, now
;
157 cutoff
= mintime
= now
;
158 cutoff
.tv_sec
-= seconds
;
160 counts
.reserve(g_rings
.getNumberOfResponseEntries());
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
) {
166 if(seconds
&& c
.when
< cutoff
)
177 double delta
= seconds
? seconds
: DiffTime(now
, mintime
);
178 return filterScore(counts
, delta
, rate
);
181 static counts_t
exceedQueryGen(unsigned int rate
, int seconds
, std::function
<void(counts_t
&, const Rings::Query
&)> T
)
184 struct timespec cutoff
, mintime
, now
;
186 cutoff
= mintime
= now
;
187 cutoff
.tv_sec
-= seconds
;
189 counts
.reserve(g_rings
.getNumberOfQueryEntries());
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
)
204 double delta
= seconds
? seconds
: DiffTime(now
, mintime
);
205 return filterScore(counts
, delta
, rate
);
209 static counts_t
exceedRCode(unsigned int rate
, int seconds
, int rcode
)
211 return exceedRespGen(rate
, seconds
, [rcode
](counts_t
& counts
, const Rings::Response
& r
)
213 if(r
.dh
.rcode
== rcode
)
214 counts
[r
.requestor
]++;
218 static counts_t
exceedRespByterate(unsigned int rate
, int seconds
)
220 return exceedRespGen(rate
, seconds
, [](counts_t
& counts
, const Rings::Response
& r
)
222 counts
[r
.requestor
]+=r
.size
;
226 void setupLuaInspection()
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;
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
]++;
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
));
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
;
251 unsigned int count
=1, rest
=0;
252 boost::format
fmt("%4d %-40s %4d %4.1f%%\n");
253 for(const auto& rc
: rcounts
) {
257 g_outputBuffer
+= (fmt
% (count
++) % rc
.second
.toString() % rc
.first
% (100.0*rc
.first
/total
)).str();
259 g_outputBuffer
+= (fmt
% (count
) % "Rest" % rest
% (total
> 0 ? 100.0*rest
/total
: 100.0)).str();
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;
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
) {
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
);
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()));
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
;
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
) {
303 ret
.insert({count
++, {rc
.second
.toString(), rc
.first
, 100.0*rc
.first
/total
}});
305 ret
.insert({count
, {"Rest", rest
, total
> 0 ? 100.0*rest
/total
: 100.0}});
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)");
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
) {
319 std::lock_guard
<std::mutex
> rl(shard
->respLock
);
320 rings
.push_back(shard
->respRing
);
322 totalEntries
+= rings
.back().size();
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
;
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
; });
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)");
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; });
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)");
353 g_lua
.writeFunction("getTopBandwidth", [](unsigned int top
) {
354 setLuaNoSideEffect();
355 return g_rings
.getTopBandwidth(top
);
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)");
360 g_lua
.writeFunction("delta", []() {
361 setLuaNoSideEffect();
362 // we hold the lua lock already!
363 for(const auto& d
: g_confDelta
) {
365 localtime_r(&d
.first
.tv_sec
, &tm
);
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";
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
;
380 auto str
=boost::get
<string
>(&inp
);
384 auto v
= boost::get
<vector
<pair
<int, string
> > >(inp
);
385 for(const auto& a
: v
)
386 vec
.push_back(a
.second
);
389 for(const auto& s
: vec
) {
395 if(boost::ends_with(s
,"ms") && sscanf(s
.c_str(), "%ums", &msec
)) {
399 try { dn
=DNSName(s
); }
402 g_outputBuffer
= "Could not parse '"+s
+"' as domain name or netmask";
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
) {
415 std::lock_guard
<std::mutex
> rl(shard
->queryLock
);
416 for (const auto& entry
: shard
->queryRing
) {
421 std::lock_guard
<std::mutex
> rl(shard
->respLock
);
422 for (const auto& entry
: shard
->respRing
) {
428 sort(qr
.begin(), qr
.end(), [](const decltype(qr
)::value_type
& a
, const decltype(qr
)::value_type
& b
) {
429 return b
.when
< a
.when
;
432 sort(rr
.begin(), rr
.end(), [](const decltype(rr
)::value_type
& a
, const decltype(rr
)::value_type
& b
) {
433 return b
.when
< a
.when
;
440 std::multimap
<struct timespec
, string
> out
;
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();
446 for(const auto& c
: qr
) {
447 bool nmmatch
=true, dnmatch
=true;
449 nmmatch
= nm
->match(c
.requestor
);
451 dnmatch
= c
.name
.isPartOf(*dn
);
452 if(nmmatch
&& dnmatch
) {
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() )) ;
456 if(limit
&& *limit
==++num
)
465 for(const auto& c
: rr
) {
466 bool nmmatch
=true, dnmatch
=true, msecmatch
=true;
468 nmmatch
= nm
->match(c
.requestor
);
470 dnmatch
= c
.name
.isPartOf(*dn
);
472 msecmatch
=(c
.usec
/1000 > (unsigned int)msec
);
474 if(nmmatch
&& dnmatch
&& msecmatch
) {
477 extra
=". " +std::to_string(htons(c
.dh
.ancount
))+ " answers";
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() )) ;
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() )) ;
485 if(limit
&& *limit
==++num
)
490 for(const auto& p
: out
) {
491 g_outputBuffer
+=p
.second
;
495 g_lua
.writeFunction("showResponseLatency", []() {
496 setLuaNoSideEffect();
497 map
<double, unsigned int> histo
;
499 for(int i
=0; i
< 15; ++i
) {
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())
515 auto iter
= histo
.lower_bound(r
.usec
);
516 if(iter
!= histo
.end())
526 g_outputBuffer
= "No traffic yet.\n";
530 g_outputBuffer
= (boost::format("Average response latency: %.02f msec\n") % (0.001*totlat
/size
)).str();
533 for(auto iter
= histo
.cbegin(); iter
!= histo
.cend(); ++iter
) {
534 highest
=std::max(highest
, iter
->second
*1.0);
536 boost::format
fmt("%7.2f\t%s\n");
537 g_outputBuffer
+= (fmt
% "msec" % "").str();
539 for(auto iter
= histo
.cbegin(); iter
!= histo
.cend(); ++iter
) {
540 int stars
= (70.0 * iter
->second
/highest
);
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)
549 g_outputBuffer
+= (fmt
% (iter
->first
/1000.0) % string(stars
, c
)).str();
553 g_lua
.writeFunction("showTCPStats", [] {
554 setLuaNoSideEffect();
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
;
561 ret
<< "Query distribution mode is: " << std::string(g_useTCPSinglePipe
? "single queue" : "per-thread queues") << endl
;
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
;
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
;
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
;
579 auto states
= g_dstates
.getLocal();
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
;
586 g_outputBuffer
=ret
.str();
589 g_lua
.writeFunction("dumpStats", [] {
590 setLuaNoSideEffect();
591 vector
<string
> leftcolumn
, rightcolumn
;
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
;
600 boost::format
flt(" %9.1f");
601 for(const auto& e
: entries
) {
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();
608 second
=std::to_string((*boost::get
<DNSDistStats::statfunction_t
>(&e
.second
))(e
.first
));
610 if(leftcolumn
.size() < g_stats
.entries
.size()/2)
611 leftcolumn
.push_back((fmt
% e
.first
% second
).str());
613 rightcolumn
.push_back((fmt
% e
.first
% second
).str());
616 auto leftiter
=leftcolumn
.begin(), rightiter
=rightcolumn
.begin();
617 boost::format
clmn("%|0t|%1% %|39t|%2%\n");
619 for(;leftiter
!= leftcolumn
.end() || rightiter
!= rightcolumn
.end();) {
620 string lentry
, rentry
;
621 if(leftiter
!= leftcolumn
.end()) {
625 if(rightiter
!= rightcolumn
.end()) {
629 g_outputBuffer
+= (clmn
% lentry
% rentry
).str();
633 g_lua
.writeFunction("exceedServFails", [](unsigned int rate
, int seconds
) {
634 setLuaNoSideEffect();
635 return exceedRCode(rate
, seconds
, RCode::ServFail
);
637 g_lua
.writeFunction("exceedNXDOMAINs", [](unsigned int rate
, int seconds
) {
638 setLuaNoSideEffect();
639 return exceedRCode(rate
, seconds
, RCode::NXDomain
);
642 g_lua
.writeFunction("exceedRespByterate", [](unsigned int rate
, int seconds
) {
643 setLuaNoSideEffect();
644 return exceedRespByterate(rate
, seconds
);
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
) {
651 counts
[q
.requestor
]++;
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
]++;
662 g_lua
.writeFunction("getRespRing", getRespRing
);
665 g_lua
.registerFunction
<StatNode
, unsigned int()>("numChildren",
666 [](StatNode
& sn
) -> unsigned int {
667 return sn
.children
.size();
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
);
675 g_lua
.writeFunction("statNodeRespRing", [](statvisitor_t visitor
, boost::optional
<unsigned int> seconds
) {
676 statNodeRespRing(visitor
, seconds
? *seconds
: 0);
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
) {
683 group
->setQueryRate(rate
, warningRate
? *warningRate
: 0, seconds
, reason
, blockDuration
, action
? *action
: DNSAction::Action::None
);
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
) {
688 group
->setResponseByteRate(rate
, warningRate
? *warningRate
: 0, seconds
, reason
, blockDuration
, action
? *action
: DNSAction::Action::None
);
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
) {
693 group
->setSuffixMatchRule(seconds
, reason
, blockDuration
, action
? *action
: DNSAction::Action::None
, visitor
);
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
) {
698 group
->setSuffixMatchRuleFFI(seconds
, reason
, blockDuration
, action
? *action
: DNSAction::Action::None
, visitor
);
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
) {
703 group
->setRCodeRate(rcode
, rate
, warningRate
? *warningRate
: 0, seconds
, reason
, blockDuration
, action
? *action
: DNSAction::Action::None
);
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
) {
708 group
->setQTypeRate(qtype
, rate
, warningRate
? *warningRate
: 0, seconds
, reason
, blockDuration
, action
? *action
: DNSAction::Action::None
);
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
));
718 group
->excludeRange(Netmask(*boost::get
<std::string
>(&ranges
)));
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
));
728 group
->includeRange(Netmask(*boost::get
<std::string
>(&ranges
)));
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
));
738 group
->excludeDomain(DNSName(*boost::get
<std::string
>(&domains
)));
741 g_lua
.registerFunction
<void(std::shared_ptr
<DynBlockRulesGroup
>::*)()>("apply", [](std::shared_ptr
<DynBlockRulesGroup
>& group
) {
744 g_lua
.registerFunction("toString", &DynBlockRulesGroup::toString
);