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-cache.hh"
24 #include "dnsrulactions.hh"
27 #include "sodcrypto.hh"
33 #include <boost/logic/tribool.hpp>
34 #include "statnode.hh"
36 boost::tribool g_noLuaSideEffect
;
38 /* this is a best effort way to prevent logging calls with no side-effects in the output of delta()
39 Functions can declare setLuaNoSideEffect() and if nothing else does declare a side effect, or nothing
40 has done so before on this invocation, this call won't be part of delta() output */
41 void setLuaNoSideEffect()
43 if(g_noLuaSideEffect
==false) // there has been a side effect already
45 g_noLuaSideEffect
=true;
48 void setLuaSideEffect()
50 g_noLuaSideEffect
=false;
53 bool getLuaNoSideEffect()
55 return g_noLuaSideEffect
==true;
58 void resetLuaSideEffect()
60 g_noLuaSideEffect
= boost::logic::indeterminate
;
63 map
<ComboAddress
,int> filterScore(const map
<ComboAddress
, unsigned int,ComboAddress::addressOnlyLessThan
>& counts
,
64 double delta
, int rate
)
66 std::multimap
<unsigned int,ComboAddress
> score
;
67 for(const auto& e
: counts
)
68 score
.insert({e
.second
, e
.first
});
70 map
<ComboAddress
,int> ret
;
72 double lim
= delta
*rate
;
73 for(auto s
= score
.crbegin(); s
!= score
.crend() && s
->first
> lim
; ++s
) {
74 ret
[s
->second
]=s
->first
;
80 typedef std::function
<void(const StatNode
&, const StatNode::Stat
&, const StatNode::Stat
&)> statvisitor_t
;
82 void statNodeRespRing(statvisitor_t visitor
)
84 std::lock_guard
<std::mutex
> lock(g_rings
.respMutex
);
87 for(const auto& c
: g_rings
.respRing
) {
88 root
.submit(c
.name
, c
.dh
.rcode
, c
.requestor
);
92 root
.visit([&visitor
](const StatNode
* node
, const StatNode::Stat
& self
, const StatNode::Stat
& children
) {
93 visitor(*node
, self
, children
);}, node
);
97 vector
<pair
<unsigned int, std::unordered_map
<string
,string
> > > getRespRing(boost::optional
<int> rcode
)
99 typedef std::unordered_map
<string
,string
> entry_t
;
100 vector
<pair
<unsigned int, entry_t
> > ret
;
101 std::lock_guard
<std::mutex
> lock(g_rings
.respMutex
);
104 unsigned int count
=1;
105 for(const auto& c
: g_rings
.respRing
) {
106 if(rcode
&& (rcode
.get() != c
.dh
.rcode
))
108 e
["qname"]=c
.name
.toString();
109 e
["rcode"]=std::to_string(c
.dh
.rcode
);
110 ret
.push_back(std::make_pair(count
,e
));
116 typedef map
<ComboAddress
, unsigned int,ComboAddress::addressOnlyLessThan
> counts_t
;
117 map
<ComboAddress
,int> exceedRespGen(int rate
, int seconds
, std::function
<void(counts_t
&, const Rings::Response
&)> T
)
120 struct timespec cutoff
, mintime
, now
;
122 cutoff
= mintime
= now
;
123 cutoff
.tv_sec
-= seconds
;
125 std::lock_guard
<std::mutex
> lock(g_rings
.respMutex
);
126 for(const auto& c
: g_rings
.respRing
) {
127 if(seconds
&& c
.when
< cutoff
)
136 double delta
= seconds
? seconds
: DiffTime(now
, mintime
);
137 return filterScore(counts
, delta
, rate
);
140 map
<ComboAddress
,int> exceedQueryGen(int rate
, int seconds
, std::function
<void(counts_t
&, const Rings::Query
&)> T
)
143 struct timespec cutoff
, mintime
, now
;
145 cutoff
= mintime
= now
;
146 cutoff
.tv_sec
-= seconds
;
148 ReadLock
rl(&g_rings
.queryLock
);
149 for(const auto& c
: g_rings
.queryRing
) {
150 if(seconds
&& c
.when
< cutoff
)
158 double delta
= seconds
? seconds
: DiffTime(now
, mintime
);
159 return filterScore(counts
, delta
, rate
);
163 map
<ComboAddress
,int> exceedRCode(int rate
, int seconds
, int rcode
)
165 return exceedRespGen(rate
, seconds
, [rcode
](counts_t
& counts
, const Rings::Response
& r
)
167 if(r
.dh
.rcode
== rcode
)
168 counts
[r
.requestor
]++;
172 map
<ComboAddress
,int> exceedRespByterate(int rate
, int seconds
)
174 return exceedRespGen(rate
, seconds
, [](counts_t
& counts
, const Rings::Response
& r
)
176 counts
[r
.requestor
]+=r
.size
;
182 void moreLua(bool client
)
184 typedef NetmaskTree
<DynBlock
> nmts_t
;
185 g_lua
.writeFunction("newCA", [](const std::string
& name
) { return ComboAddress(name
); });
188 g_lua
.writeFunction("newNMG", []() { return NetmaskGroup(); });
189 g_lua
.registerFunction
<void(NetmaskGroup::*)(const std::string
&mask
)>("addMask", [](NetmaskGroup
&nmg
, const std::string
& mask
)
194 g_lua
.registerFunction("match", (bool (NetmaskGroup::*)(const ComboAddress
&) const)&NetmaskGroup::match
);
195 g_lua
.registerFunction("size", &NetmaskGroup::size
);
196 g_lua
.registerFunction("clear", &NetmaskGroup::clear
);
199 g_lua
.writeFunction("showDynBlocks", []() {
200 setLuaNoSideEffect();
201 auto slow
= g_dynblockNMG
.getCopy();
204 boost::format
fmt("%-24s %8d %8d %s\n");
205 g_outputBuffer
= (fmt
% "What" % "Seconds" % "Blocks" % "Reason").str();
206 for(const auto& e
: slow
) {
207 if(now
< e
->second
.until
)
208 g_outputBuffer
+= (fmt
% e
->first
.toString() % (e
->second
.until
.tv_sec
- now
.tv_sec
) % e
->second
.blocks
% e
->second
.reason
).str();
210 auto slow2
= g_dynblockSMT
.getCopy();
211 slow2
.visit([&now
, &fmt
](const SuffixMatchTree
<DynBlock
>& node
) {
212 if(now
<node
.d_value
.until
) {
214 if(!node
.d_value
.domain
.empty())
215 dom
= node
.d_value
.domain
.toString();
216 g_outputBuffer
+= (fmt
% dom
% (node
.d_value
.until
.tv_sec
- now
.tv_sec
) % node
.d_value
.blocks
% node
.d_value
.reason
).str();
222 g_lua
.writeFunction("clearDynBlocks", []() {
225 g_dynblockNMG
.setState(nmg
);
226 SuffixMatchTree
<DynBlock
> smt
;
227 g_dynblockSMT
.setState(smt
);
230 g_lua
.writeFunction("addDynBlocks",
231 [](const map
<ComboAddress
,int>& m
, const std::string
& msg
, boost::optional
<int> seconds
) {
233 auto slow
= g_dynblockNMG
.getCopy();
234 struct timespec until
, now
;
237 int actualSeconds
= seconds
? *seconds
: 10;
238 until
.tv_sec
+= actualSeconds
;
239 for(const auto& capair
: m
) {
240 unsigned int count
= 0;
241 auto got
= slow
.lookup(Netmask(capair
.first
));
244 if(until
< got
->second
.until
) // had a longer policy
246 if(now
< got
->second
.until
) // only inherit count on fresh query we are extending
247 count
=got
->second
.blocks
;
251 DynBlock db
{msg
,until
};
254 warnlog("Inserting dynamic block for %s for %d seconds: %s", capair
.first
.toString(), actualSeconds
, msg
);
255 slow
.insert(Netmask(capair
.first
)).second
=db
;
257 g_dynblockNMG
.setState(slow
);
260 g_lua
.writeFunction("addDynBlockSMT",
261 [](const vector
<pair
<unsigned int, string
> >&names
, const std::string
& msg
, boost::optional
<int> seconds
) {
263 auto slow
= g_dynblockSMT
.getCopy();
264 struct timespec until
, now
;
267 int actualSeconds
= seconds
? *seconds
: 10;
268 until
.tv_sec
+= actualSeconds
;
270 for(const auto& capair
: names
) {
271 unsigned int count
= 0;
272 DNSName
domain(capair
.second
);
273 auto got
= slow
.lookup(domain
);
276 if(until
< got
->until
) // had a longer policy
278 if(now
< got
->until
) // only inherit count on fresh query we are extending
284 DynBlock db
{msg
,until
,domain
};
287 warnlog("Inserting dynamic block for %s for %d seconds: %s", domain
, actualSeconds
, msg
);
288 slow
.add(domain
, db
);
290 g_dynblockSMT
.setState(slow
);
295 g_lua
.registerFunction
<bool(nmts_t::*)(const ComboAddress
&)>("match",
296 [](nmts_t
& s
, const ComboAddress
& ca
) { return s
.match(ca
); });
298 g_lua
.writeFunction("exceedServFails", [](unsigned int rate
, int seconds
) {
299 setLuaNoSideEffect();
300 return exceedRCode(rate
, seconds
, RCode::ServFail
);
302 g_lua
.writeFunction("exceedNXDOMAINs", [](unsigned int rate
, int seconds
) {
303 setLuaNoSideEffect();
304 return exceedRCode(rate
, seconds
, RCode::NXDomain
);
309 g_lua
.writeFunction("exceedRespByterate", [](unsigned int rate
, int seconds
) {
310 setLuaNoSideEffect();
311 return exceedRespByterate(rate
, seconds
);
314 g_lua
.writeFunction("exceedQTypeRate", [](uint16_t type
, unsigned int rate
, int seconds
) {
315 setLuaNoSideEffect();
316 return exceedQueryGen(rate
, seconds
, [type
](counts_t
& counts
, const Rings::Query
& q
) {
318 counts
[q
.requestor
]++;
322 g_lua
.writeFunction("exceedQRate", [](unsigned int rate
, int seconds
) {
323 setLuaNoSideEffect();
324 return exceedQueryGen(rate
, seconds
, [](counts_t
& counts
, const Rings::Query
& q
) {
325 counts
[q
.requestor
]++;
329 g_lua
.writeFunction("getRespRing", getRespRing
);
331 g_lua
.registerFunction
<StatNode
, unsigned int()>("numChildren",
332 [](StatNode
& sn
) -> unsigned int {
334 return sn
.children
.size();
337 g_lua
.registerMember("fullname", &StatNode::fullname
);
338 g_lua
.registerMember("servfails", &StatNode::Stat::servfails
);
339 g_lua
.registerMember("nxdomains", &StatNode::Stat::nxdomains
);
340 g_lua
.registerMember("queries", &StatNode::Stat::queries
);
342 g_lua
.writeFunction("statNodeRespRing", statNodeRespRing
);
344 g_lua
.writeFunction("getTopBandwidth", [](unsigned int top
) {
345 setLuaNoSideEffect();
346 return g_rings
.getTopBandwidth(top
);
348 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)");
350 g_lua
.writeFunction("delta", []() {
351 setLuaNoSideEffect();
352 // we hold the lua lock already!
353 for(const auto& d
: g_confDelta
) {
355 localtime_r(&d
.first
.tv_sec
, &tm
);
357 strftime(date
, sizeof(date
)-1, "-- %a %b %d %Y %H:%M:%S %Z\n", &tm
);
358 g_outputBuffer
+= date
;
359 g_outputBuffer
+= d
.second
+ "\n";
363 g_lua
.writeFunction("grepq", [](boost::variant
<string
, vector
<pair
<int,string
> > > inp
, boost::optional
<unsigned int> limit
) {
364 setLuaNoSideEffect();
365 boost::optional
<Netmask
> nm
;
366 boost::optional
<DNSName
> dn
;
370 auto str
=boost::get
<string
>(&inp
);
374 auto v
= boost::get
<vector
<pair
<int, string
> > >(inp
);
375 for(const auto& a
: v
)
376 vec
.push_back(a
.second
);
379 for(const auto& s
: vec
) {
385 if(boost::ends_with(s
,"ms") && sscanf(s
.c_str(), "%ums", &msec
)) {
389 try { dn
=DNSName(s
); }
392 g_outputBuffer
= "Could not parse '"+s
+"' as domain name or netmask";
399 decltype(g_rings
.queryRing
) qr
;
400 decltype(g_rings
.respRing
) rr
;
402 ReadLock
rl(&g_rings
.queryLock
);
403 qr
=g_rings
.queryRing
;
405 sort(qr
.begin(), qr
.end(), [](const decltype(qr
)::value_type
& a
, const decltype(qr
)::value_type
& b
) {
406 return b
.when
< a
.when
;
409 std::lock_guard
<std::mutex
> lock(g_rings
.respMutex
);
413 sort(rr
.begin(), rr
.end(), [](const decltype(rr
)::value_type
& a
, const decltype(rr
)::value_type
& b
) {
414 return b
.when
< a
.when
;
421 std::multimap
<struct timespec
, string
> out
;
423 boost::format
fmt("%-7.1f %-47s %-12s %-5d %-25s %-5s %-6.1f %-2s %-2s %-2s %s\n");
424 g_outputBuffer
+= (fmt
% "Time" % "Client" % "Server" % "ID" % "Name" % "Type" % "Lat." % "TC" % "RD" % "AA" % "Rcode").str();
427 for(const auto& c
: qr
) {
428 bool nmmatch
=true, dnmatch
=true;
430 nmmatch
= nm
->match(c
.requestor
);
432 dnmatch
= c
.name
.isPartOf(*dn
);
433 if(nmmatch
&& dnmatch
) {
435 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() )) ;
437 if(limit
&& *limit
==++num
)
446 for(const auto& c
: rr
) {
447 bool nmmatch
=true, dnmatch
=true, msecmatch
=true;
449 nmmatch
= nm
->match(c
.requestor
);
451 dnmatch
= c
.name
.isPartOf(*dn
);
453 msecmatch
=(c
.usec
/1000 > (unsigned int)msec
);
455 if(nmmatch
&& dnmatch
&& msecmatch
) {
458 extra
=". " +std::to_string(htons(c
.dh
.ancount
))+ " answers";
461 if(c
.usec
!= std::numeric_limits
<decltype(c
.usec
)>::max())
462 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() )) ;
464 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() )) ;
466 if(limit
&& *limit
==++num
)
471 for(const auto& p
: out
) {
472 g_outputBuffer
+=p
.second
;
476 g_lua
.writeFunction("addDNSCryptBind", [](const std::string
& addr
, const std::string
& providerName
, const std::string
& certFile
, const std::string keyFile
, boost::optional
<bool> reusePort
, boost::optional
<int> tcpFastOpenQueueSize
) {
477 if (g_configurationDone
) {
478 g_outputBuffer
="addDNSCryptBind cannot be used at runtime!\n";
483 DnsCryptContext
ctx(providerName
, certFile
, keyFile
);
484 g_dnsCryptLocals
.push_back(std::make_tuple(ComboAddress(addr
, 443), ctx
, reusePort
? *reusePort
: false, tcpFastOpenQueueSize
? *tcpFastOpenQueueSize
: 0));
486 catch(std::exception
& e
) {
488 g_outputBuffer
="Error: "+string(e
.what())+"\n";
491 g_outputBuffer
="Error: DNSCrypt support is not enabled.\n";
495 g_lua
.writeFunction("showDNSCryptBinds", []() {
496 setLuaNoSideEffect();
499 boost::format
fmt("%1$-3d %2% %|25t|%3$-20.20s %|26t|%4$-8d %|35t|%5$-21.21s %|56t|%6$-9d %|66t|%7$-21.21s" );
500 ret
<< (fmt
% "#" % "Address" % "Provider Name" % "Serial" % "Validity" % "P. Serial" % "P. Validity") << endl
;
503 for (const auto& local
: g_dnsCryptLocals
) {
504 const DnsCryptContext
& ctx
= std::get
<1>(local
);
505 bool const hasOldCert
= ctx
.hadOldCertificate();
506 const DnsCryptCert
& cert
= ctx
.getCurrentCertificate();
507 const DnsCryptCert
& oldCert
= ctx
.getOldCertificate();
509 ret
<< (fmt
% idx
% std::get
<0>(local
).toStringWithPort() % ctx
.getProviderName() % cert
.signedData
.serial
% DnsCryptContext::certificateDateToStr(cert
.signedData
.tsEnd
) % (hasOldCert
? oldCert
.signedData
.serial
: 0) % (hasOldCert
? DnsCryptContext::certificateDateToStr(oldCert
.signedData
.tsEnd
) : "-")) << endl
;
513 g_outputBuffer
=ret
.str();
515 g_outputBuffer
="Error: DNSCrypt support is not enabled.\n";
519 g_lua
.writeFunction("generateDNSCryptProviderKeys", [](const std::string
& publicKeyFile
, const std::string privateKeyFile
) {
520 setLuaNoSideEffect();
522 unsigned char publicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
];
523 unsigned char privateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
];
524 sodium_mlock(privateKey
, sizeof(privateKey
));
527 DnsCryptContext::generateProviderKeys(publicKey
, privateKey
);
529 ofstream
pubKStream(publicKeyFile
);
530 pubKStream
.write((char*) publicKey
, sizeof(publicKey
));
533 ofstream
privKStream(privateKeyFile
);
534 privKStream
.write((char*) privateKey
, sizeof(privateKey
));
537 g_outputBuffer
="Provider fingerprint is: " + DnsCryptContext::getProviderFingerprint(publicKey
) + "\n";
539 catch(std::exception
& e
) {
541 g_outputBuffer
="Error: "+string(e
.what())+"\n";
544 sodium_memzero(privateKey
, sizeof(privateKey
));
545 sodium_munlock(privateKey
, sizeof(privateKey
));
547 g_outputBuffer
="Error: DNSCrypt support is not enabled.\n";
551 g_lua
.writeFunction("printDNSCryptProviderFingerprint", [](const std::string
& publicKeyFile
) {
552 setLuaNoSideEffect();
554 unsigned char publicKey
[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE
];
557 ifstream
file(publicKeyFile
);
558 file
.read((char *) &publicKey
, sizeof(publicKey
));
561 throw std::runtime_error("Invalid dnscrypt provider public key file " + publicKeyFile
);
564 g_outputBuffer
="Provider fingerprint is: " + DnsCryptContext::getProviderFingerprint(publicKey
) + "\n";
566 catch(std::exception
& e
) {
568 g_outputBuffer
="Error: "+string(e
.what())+"\n";
571 g_outputBuffer
="Error: DNSCrypt support is not enabled.\n";
575 g_lua
.writeFunction("generateDNSCryptCertificate", [](const std::string
& providerPrivateKeyFile
, const std::string
& certificateFile
, const std::string privateKeyFile
, uint32_t serial
, time_t begin
, time_t end
) {
576 setLuaNoSideEffect();
578 unsigned char providerPrivateKey
[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE
];
579 sodium_mlock(providerPrivateKey
, sizeof(providerPrivateKey
));
580 sodium_memzero(providerPrivateKey
, sizeof(providerPrivateKey
));
583 DnsCryptPrivateKey privateKey
;
585 ifstream
providerKStream(providerPrivateKeyFile
);
586 providerKStream
.read((char*) providerPrivateKey
, sizeof(providerPrivateKey
));
587 if (providerKStream
.fail()) {
588 providerKStream
.close();
589 throw std::runtime_error("Invalid DNSCrypt provider key file " + providerPrivateKeyFile
);
592 DnsCryptContext::generateCertificate(serial
, begin
, end
, providerPrivateKey
, privateKey
, cert
);
594 privateKey
.saveToFile(privateKeyFile
);
595 DnsCryptContext::saveCertFromFile(cert
, certificateFile
);
597 catch(std::exception
& e
) {
599 g_outputBuffer
="Error: "+string(e
.what())+"\n";
602 sodium_memzero(providerPrivateKey
, sizeof(providerPrivateKey
));
603 sodium_munlock(providerPrivateKey
, sizeof(providerPrivateKey
));
605 g_outputBuffer
="Error: DNSCrypt support is not enabled.\n";
609 g_lua
.writeFunction("showPools", []() {
610 setLuaNoSideEffect();
613 boost::format
fmt("%1$-20.20s %|25t|%2$20s %|50t|%3%" );
615 ret
<< (fmt
% "Name" % "Cache" % "Servers" ) << endl
;
617 const auto localPools
= g_pools
.getCopy();
618 for (const auto& entry
: localPools
) {
619 const string
& name
= entry
.first
;
620 const std::shared_ptr
<ServerPool
> pool
= entry
.second
;
621 string cache
= pool
->packetCache
!= nullptr ? pool
->packetCache
->toString() : "";
624 for (const auto& server
: pool
->servers
) {
625 if (!servers
.empty()) {
628 if (!server
.second
->name
.empty()) {
629 servers
+= server
.second
->name
;
632 servers
+= server
.second
->remote
.toStringWithPort();
635 ret
<< (fmt
% name
% cache
% servers
) << endl
;
637 g_outputBuffer
=ret
.str();
638 }catch(std::exception
& e
) { g_outputBuffer
=e
.what(); throw; }
641 g_lua
.registerFunction
<void(std::shared_ptr
<ServerPool
>::*)(std::shared_ptr
<DNSDistPacketCache
>)>("setCache", [](std::shared_ptr
<ServerPool
> pool
, std::shared_ptr
<DNSDistPacketCache
> cache
) {
643 pool
->packetCache
= cache
;
646 g_lua
.registerFunction("getCache", &ServerPool::getCache
);
647 g_lua
.registerFunction
<void(std::shared_ptr
<ServerPool
>::*)()>("unsetCache", [](std::shared_ptr
<ServerPool
> pool
) {
649 pool
->packetCache
= nullptr;
653 g_lua
.writeFunction("newPacketCache", [client
](size_t maxEntries
, boost::optional
<uint32_t> maxTTL
, boost::optional
<uint32_t> minTTL
, boost::optional
<uint32_t> servFailTTL
, boost::optional
<uint32_t> staleTTL
) {
654 return std::make_shared
<DNSDistPacketCache
>(maxEntries
, maxTTL
? *maxTTL
: 86400, minTTL
? *minTTL
: 0, servFailTTL
? *servFailTTL
: 60, staleTTL
? *staleTTL
: 60);
656 g_lua
.registerFunction("toString", &DNSDistPacketCache::toString
);
657 g_lua
.registerFunction("isFull", &DNSDistPacketCache::isFull
);
658 g_lua
.registerFunction("purgeExpired", &DNSDistPacketCache::purgeExpired
);
659 g_lua
.registerFunction("expunge", &DNSDistPacketCache::expunge
);
660 g_lua
.registerFunction
<void(std::shared_ptr
<DNSDistPacketCache
>::*)(const DNSName
& dname
, boost::optional
<uint16_t> qtype
)>("expungeByName", [](std::shared_ptr
<DNSDistPacketCache
> cache
, const DNSName
& dname
, boost::optional
<uint16_t> qtype
) {
662 cache
->expungeByName(dname
, qtype
? *qtype
: QType::ANY
);
665 g_lua
.registerFunction
<void(std::shared_ptr
<DNSDistPacketCache
>::*)()>("printStats", [](const std::shared_ptr
<DNSDistPacketCache
> cache
) {
667 g_outputBuffer
="Entries: " + std::to_string(cache
->getEntriesCount()) + "/" + std::to_string(cache
->getMaxEntries()) + "\n";
668 g_outputBuffer
+="Hits: " + std::to_string(cache
->getHits()) + "\n";
669 g_outputBuffer
+="Misses: " + std::to_string(cache
->getMisses()) + "\n";
670 g_outputBuffer
+="Deferred inserts: " + std::to_string(cache
->getDeferredInserts()) + "\n";
671 g_outputBuffer
+="Deferred lookups: " + std::to_string(cache
->getDeferredLookups()) + "\n";
672 g_outputBuffer
+="Lookup Collisions: " + std::to_string(cache
->getLookupCollisions()) + "\n";
673 g_outputBuffer
+="Insert Collisions: " + std::to_string(cache
->getInsertCollisions()) + "\n";
674 g_outputBuffer
+="TTL Too Shorts: " + std::to_string(cache
->getTTLTooShorts()) + "\n";
678 g_lua
.writeFunction("getPool", [client
](const string
& poolName
) {
680 return std::make_shared
<ServerPool
>();
682 auto localPools
= g_pools
.getCopy();
683 std::shared_ptr
<ServerPool
> pool
= createPoolIfNotExists(localPools
, poolName
);
684 g_pools
.setState(localPools
);
688 g_lua
.writeFunction("setVerboseHealthChecks", [](bool verbose
) { g_verboseHealthChecks
=verbose
; });
689 g_lua
.writeFunction("setStaleCacheEntriesTTL", [](uint32_t ttl
) { g_staleCacheEntriesTTL
= ttl
; });
691 g_lua
.writeFunction("RemoteLogAction", [](std::shared_ptr
<RemoteLogger
> logger
) {
693 return std::shared_ptr
<DNSAction
>(new RemoteLogAction(logger
));
695 throw std::runtime_error("Protobuf support is required to use RemoteLogAction");
698 g_lua
.writeFunction("RemoteLogResponseAction", [](std::shared_ptr
<RemoteLogger
> logger
) {
700 return std::shared_ptr
<DNSResponseAction
>(new RemoteLogResponseAction(logger
));
702 throw std::runtime_error("Protobuf support is required to use RemoteLogResponseAction");
705 g_lua
.writeFunction("newRemoteLogger", [client
](const std::string
& remote
, boost::optional
<uint16_t> timeout
, boost::optional
<uint64_t> maxQueuedEntries
, boost::optional
<uint8_t> reconnectWaitTime
) {
706 return std::make_shared
<RemoteLogger
>(ComboAddress(remote
), timeout
? *timeout
: 2, maxQueuedEntries
? *maxQueuedEntries
: 100, reconnectWaitTime
? *reconnectWaitTime
: 1);
709 g_lua
.writeFunction("TeeAction", [](const std::string
& remote
, boost::optional
<bool> addECS
) {
710 return std::shared_ptr
<DNSAction
>(new TeeAction(ComboAddress(remote
, 53), addECS
? *addECS
: false));
713 g_lua
.registerFunction
<void(DNSAction::*)()>("printStats", [](const DNSAction
& ta
) {
714 setLuaNoSideEffect();
715 auto stats
= ta
.getStats();
716 for(const auto& s
: stats
) {
717 g_outputBuffer
+=s
.first
+"\t";
718 if((uint64_t)s
.second
== s
.second
)
719 g_outputBuffer
+= std::to_string((uint64_t)s
.second
)+"\n";
721 g_outputBuffer
+= std::to_string(s
.second
)+"\n";
725 g_lua
.writeFunction("getAction", [](unsigned int num
) {
726 setLuaNoSideEffect();
727 boost::optional
<std::shared_ptr
<DNSAction
>> ret
;
728 auto rulactions
= g_rulactions
.getCopy();
729 if(num
< rulactions
.size())
730 ret
=rulactions
[num
].second
;
734 g_lua
.registerFunction("getStats", &DNSAction::getStats
);
736 g_lua
.writeFunction("showResponseRules", []() {
737 setLuaNoSideEffect();
738 boost::format
fmt("%-3d %9d %-50s %s\n");
739 g_outputBuffer
+= (fmt
% "#" % "Matches" % "Rule" % "Action").str();
741 for(const auto& lim
: g_resprulactions
.getCopy()) {
742 string name
= lim
.first
->toString();
743 g_outputBuffer
+= (fmt
% num
% lim
.first
->d_matches
% name
% lim
.second
->toString()).str();
748 g_lua
.writeFunction("rmResponseRule", [](unsigned int num
) {
750 auto rules
= g_resprulactions
.getCopy();
751 if(num
>= rules
.size()) {
752 g_outputBuffer
= "Error: attempt to delete non-existing rule\n";
755 rules
.erase(rules
.begin()+num
);
756 g_resprulactions
.setState(rules
);
759 g_lua
.writeFunction("topResponseRule", []() {
761 auto rules
= g_resprulactions
.getCopy();
764 auto subject
= *rules
.rbegin();
765 rules
.erase(std::prev(rules
.end()));
766 rules
.insert(rules
.begin(), subject
);
767 g_resprulactions
.setState(rules
);
770 g_lua
.writeFunction("mvResponseRule", [](unsigned int from
, unsigned int to
) {
772 auto rules
= g_resprulactions
.getCopy();
773 if(from
>= rules
.size() || to
> rules
.size()) {
774 g_outputBuffer
= "Error: attempt to move rules from/to invalid index\n";
777 auto subject
= rules
[from
];
778 rules
.erase(rules
.begin()+from
);
779 if(to
== rules
.size())
780 rules
.push_back(subject
);
784 rules
.insert(rules
.begin()+to
, subject
);
786 g_resprulactions
.setState(rules
);
789 g_lua
.writeFunction("showBinds", []() {
790 setLuaNoSideEffect();
793 boost::format
fmt("%1$-3d %2$-20.20s %|25t|%3$-8.8s %|35t|%4%" );
795 ret
<< (fmt
% "#" % "Address" % "Protocol" % "Queries" ) << endl
;
798 for (const auto& front
: g_frontends
) {
799 ret
<< (fmt
% counter
% front
->local
.toStringWithPort() % (front
->udpFD
!= -1 ? "UDP" : "TCP") % front
->queries
) << endl
;
802 g_outputBuffer
=ret
.str();
803 }catch(std::exception
& e
) { g_outputBuffer
=e
.what(); throw; }
806 g_lua
.writeFunction("getBind", [](size_t num
) {
807 setLuaNoSideEffect();
808 ClientState
* ret
= nullptr;
809 if(num
< g_frontends
.size()) {
810 ret
=g_frontends
[num
];
815 g_lua
.registerFunction
<std::string(ClientState::*)()>("toString", [](const ClientState
& fe
) {
816 setLuaNoSideEffect();
817 return fe
.local
.toStringWithPort();
820 g_lua
.writeFunction("help", [](boost::optional
<std::string
> command
) {
821 setLuaNoSideEffect();
823 for (const auto& keyword
: g_consoleKeywords
) {
825 g_outputBuffer
+= keyword
.toString() + "\n";
827 else if (keyword
.name
== command
) {
828 g_outputBuffer
= keyword
.toString() + "\n";
833 g_outputBuffer
= "Nothing found for " + *command
+ "\n";
837 g_lua
.writeFunction("showVersion", []() {
838 setLuaNoSideEffect();
839 g_outputBuffer
= "dnsdist " + std::string(VERSION
) + "\n";
843 g_lua
.writeFunction("newBPFFilter", [](uint32_t maxV4
, uint32_t maxV6
, uint32_t maxQNames
) {
844 return std::make_shared
<BPFFilter
>(maxV4
, maxV6
, maxQNames
);
847 g_lua
.registerFunction
<void(std::shared_ptr
<BPFFilter
>::*)(const ComboAddress
& ca
)>("block", [](std::shared_ptr
<BPFFilter
> bpf
, const ComboAddress
& ca
) {
849 return bpf
->block(ca
);
853 g_lua
.registerFunction
<void(std::shared_ptr
<BPFFilter
>::*)(const DNSName
& qname
, boost::optional
<uint16_t> qtype
)>("blockQName", [](std::shared_ptr
<BPFFilter
> bpf
, const DNSName
& qname
, boost::optional
<uint16_t> qtype
) {
855 return bpf
->block(qname
, qtype
? *qtype
: 255);
859 g_lua
.registerFunction
<void(std::shared_ptr
<BPFFilter
>::*)(const ComboAddress
& ca
)>("unblock", [](std::shared_ptr
<BPFFilter
> bpf
, const ComboAddress
& ca
) {
861 return bpf
->unblock(ca
);
865 g_lua
.registerFunction
<void(std::shared_ptr
<BPFFilter
>::*)(const DNSName
& qname
, boost::optional
<uint16_t> qtype
)>("unblockQName", [](std::shared_ptr
<BPFFilter
> bpf
, const DNSName
& qname
, boost::optional
<uint16_t> qtype
) {
867 return bpf
->unblock(qname
, qtype
? *qtype
: 255);
871 g_lua
.registerFunction
<std::string(std::shared_ptr
<BPFFilter
>::*)()>("getStats", [](const std::shared_ptr
<BPFFilter
> bpf
) {
872 setLuaNoSideEffect();
875 std::vector
<std::pair
<ComboAddress
, uint64_t> > stats
= bpf
->getAddrStats();
876 for (const auto& value
: stats
) {
877 if (value
.first
.sin4
.sin_family
== AF_INET
) {
878 res
+= value
.first
.toString() + ": " + std::to_string(value
.second
) + "\n";
880 else if (value
.first
.sin4
.sin_family
== AF_INET6
) {
881 res
+= "[" + value
.first
.toString() + "]: " + std::to_string(value
.second
) + "\n";
884 std::vector
<std::tuple
<DNSName
, uint16_t, uint64_t> > qstats
= bpf
->getQNameStats();
885 for (const auto& value
: qstats
) {
886 res
+= std::get
<0>(value
).toString() + " " + std::to_string(std::get
<1>(value
)) + ": " + std::to_string(std::get
<2>(value
)) + "\n";
892 g_lua
.registerFunction
<void(std::shared_ptr
<BPFFilter
>::*)()>("attachToAllBinds", [](std::shared_ptr
<BPFFilter
> bpf
) {
895 for (const auto& front
: g_frontends
) {
896 bpf
->addSocket(front
->udpFD
!= -1 ? front
->udpFD
: front
->tcpFD
);
901 g_lua
.registerFunction
<void(ClientState::*)(std::shared_ptr
<BPFFilter
>)>("attachFilter", [](ClientState
& frontend
, std::shared_ptr
<BPFFilter
> bpf
) {
903 bpf
->addSocket(frontend
.udpFD
!= -1 ? frontend
.udpFD
: frontend
.tcpFD
);
907 g_lua
.writeFunction("setDefaultBPFFilter", [](std::shared_ptr
<BPFFilter
> bpf
) {
908 if (g_configurationDone
) {
909 g_outputBuffer
="setDefaultBPFFilter() cannot be used at runtime!\n";
912 g_defaultBPFFilter
= bpf
;
915 g_lua
.writeFunction("newDynBPFFilter", [](std::shared_ptr
<BPFFilter
> bpf
) {
916 return std::make_shared
<DynBPFFilter
>(bpf
);
919 g_lua
.registerFunction
<void(std::shared_ptr
<DynBPFFilter
>::*)(const ComboAddress
& addr
, boost::optional
<int> seconds
)>("block", [](std::shared_ptr
<DynBPFFilter
> dbpf
, const ComboAddress
& addr
, boost::optional
<int> seconds
) {
921 struct timespec until
;
922 clock_gettime(CLOCK_MONOTONIC
, &until
);
923 until
.tv_sec
+= seconds
? *seconds
: 10;
924 dbpf
->block(addr
, until
);
928 g_lua
.registerFunction
<void(std::shared_ptr
<DynBPFFilter
>::*)()>("purgeExpired", [](std::shared_ptr
<DynBPFFilter
> dbpf
) {
931 clock_gettime(CLOCK_MONOTONIC
, &now
);
932 dbpf
->purgeExpired(now
);
936 g_lua
.writeFunction("addBPFFilterDynBlocks", [](const map
<ComboAddress
,int>& m
, std::shared_ptr
<DynBPFFilter
> dynbpf
, boost::optional
<int> seconds
) {
938 struct timespec until
, now
;
939 clock_gettime(CLOCK_MONOTONIC
, &now
);
941 int actualSeconds
= seconds
? *seconds
: 10;
942 until
.tv_sec
+= actualSeconds
;
943 for(const auto& capair
: m
) {
944 dynbpf
->block(capair
.first
, until
);
948 #endif /* HAVE_EBPF */