]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-lua2.cc
Merge pull request #5523 from rubenk/fix-typos-in-logmessage
[thirdparty/pdns.git] / pdns / dnsdist-lua2.cc
CommitLineData
12471842
PL
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 */
ea0aa517 22#include "dnsdist.hh"
886e2cf2 23#include "dnsdist-cache.hh"
ea0aa517 24#include "dnsrulactions.hh"
25#include <thread>
26#include "dolog.hh"
27#include "sodcrypto.hh"
28#include "base64.hh"
0e41337b 29#include "lock.hh"
85c7ca75 30#include "gettime.hh"
ea0aa517 31#include <map>
32#include <fstream>
f758857a 33#include <boost/logic/tribool.hpp>
71c94675 34#include "statnode.hh"
69389510
RG
35#include <sys/types.h>
36#include <dirent.h>
37#include <sys/stat.h>
38#include <unistd.h>
f758857a 39
cf48b0ce
RG
40#include "dnsdist-lua.hh"
41
f758857a 42boost::tribool g_noLuaSideEffect;
69389510 43static bool g_included{false};
f758857a 44
45/* this is a best effort way to prevent logging calls with no side-effects in the output of delta()
46 Functions can declare setLuaNoSideEffect() and if nothing else does declare a side effect, or nothing
47 has done so before on this invocation, this call won't be part of delta() output */
48void setLuaNoSideEffect()
49{
50 if(g_noLuaSideEffect==false) // there has been a side effect already
51 return;
52 g_noLuaSideEffect=true;
53}
54
55void setLuaSideEffect()
56{
57 g_noLuaSideEffect=false;
58}
59
60bool getLuaNoSideEffect()
61{
62 return g_noLuaSideEffect==true;
63}
64
65void resetLuaSideEffect()
66{
67 g_noLuaSideEffect = boost::logic::indeterminate;
68}
ea0aa517 69
ea0aa517 70map<ComboAddress,int> filterScore(const map<ComboAddress, unsigned int,ComboAddress::addressOnlyLessThan >& counts,
479a5404 71 double delta, int rate)
ea0aa517 72{
73 std::multimap<unsigned int,ComboAddress> score;
74 for(const auto& e : counts)
75 score.insert({e.second, e.first});
76
77 map<ComboAddress,int> ret;
78
ea0aa517 79 double lim = delta*rate;
ea0aa517 80 for(auto s = score.crbegin(); s != score.crend() && s->first > lim; ++s) {
81 ret[s->second]=s->first;
82 }
83 return ret;
84}
85
86
71c94675 87typedef std::function<void(const StatNode&, const StatNode::Stat&, const StatNode::Stat&)> statvisitor_t;
88
26b86deb 89static void statNodeRespRing(statvisitor_t visitor, unsigned int seconds)
71c94675 90{
26b86deb
RG
91 struct timespec cutoff, now;
92 gettime(&now);
93 if (seconds) {
94 cutoff = now;
95 cutoff.tv_sec -= seconds;
96 }
97
71c94675 98 std::lock_guard<std::mutex> lock(g_rings.respMutex);
99
100 StatNode root;
101 for(const auto& c : g_rings.respRing) {
26b86deb
RG
102 if (now < c.when)
103 continue;
104
105 if (seconds && c.when < cutoff)
106 continue;
107
71c94675 108 root.submit(c.name, c.dh.rcode, c.requestor);
109 }
110 StatNode::Stat node;
803d4b5d 111
af619119
RG
112 root.visit([&visitor](const StatNode* node_, const StatNode::Stat& self, const StatNode::Stat& children) {
113 visitor(*node_, self, children);}, node);
71c94675 114
115}
116
117vector<pair<unsigned int, std::unordered_map<string,string> > > getRespRing(boost::optional<int> rcode)
118{
119 typedef std::unordered_map<string,string> entry_t;
120 vector<pair<unsigned int, entry_t > > ret;
121 std::lock_guard<std::mutex> lock(g_rings.respMutex);
122
123 entry_t e;
124 unsigned int count=1;
125 for(const auto& c : g_rings.respRing) {
126 if(rcode && (rcode.get() != c.dh.rcode))
127 continue;
128 e["qname"]=c.name.toString();
129 e["rcode"]=std::to_string(c.dh.rcode);
130 ret.push_back(std::make_pair(count,e));
131 count++;
132 }
133 return ret;
134}
135
ea0aa517 136typedef map<ComboAddress, unsigned int,ComboAddress::addressOnlyLessThan > counts_t;
137map<ComboAddress,int> exceedRespGen(int rate, int seconds, std::function<void(counts_t&, const Rings::Response&)> T)
138{
139 counts_t counts;
479a5404 140 struct timespec cutoff, mintime, now;
85c7ca75 141 gettime(&now);
479a5404 142 cutoff = mintime = now;
ea0aa517 143 cutoff.tv_sec -= seconds;
5d9f9650
RG
144
145 std::lock_guard<std::mutex> lock(g_rings.respMutex);
ea0aa517 146 for(const auto& c : g_rings.respRing) {
147 if(seconds && c.when < cutoff)
148 continue;
479a5404 149 if(now < c.when)
150 continue;
ea0aa517 151
152 T(counts, c);
153 if(c.when < mintime)
154 mintime = c.when;
155 }
479a5404 156 double delta = seconds ? seconds : DiffTime(now, mintime);
157 return filterScore(counts, delta, rate);
ea0aa517 158}
159
0ba5eecf 160map<ComboAddress,int> exceedQueryGen(int rate, int seconds, std::function<void(counts_t&, const Rings::Query&)> T)
161{
162 counts_t counts;
479a5404 163 struct timespec cutoff, mintime, now;
85c7ca75 164 gettime(&now);
479a5404 165 cutoff = mintime = now;
0ba5eecf 166 cutoff.tv_sec -= seconds;
479a5404 167
0e41337b 168 ReadLock rl(&g_rings.queryLock);
0ba5eecf 169 for(const auto& c : g_rings.queryRing) {
170 if(seconds && c.when < cutoff)
171 continue;
479a5404 172 if(now < c.when)
173 continue;
0ba5eecf 174 T(counts, c);
175 if(c.when < mintime)
176 mintime = c.when;
177 }
479a5404 178 double delta = seconds ? seconds : DiffTime(now, mintime);
179 return filterScore(counts, delta, rate);
0ba5eecf 180}
181
ea0aa517 182
183map<ComboAddress,int> exceedRCode(int rate, int seconds, int rcode)
184{
185 return exceedRespGen(rate, seconds, [rcode](counts_t& counts, const Rings::Response& r)
186 {
3fcaeeac 187 if(r.dh.rcode == rcode)
ea0aa517 188 counts[r.requestor]++;
189 });
190}
191
192map<ComboAddress,int> exceedRespByterate(int rate, int seconds)
193{
194 return exceedRespGen(rate, seconds, [](counts_t& counts, const Rings::Response& r)
195 {
196 counts[r.requestor]+=r.size;
197 });
198}
199
79500db5
RG
200#ifdef HAVE_DNSCRYPT
201static bool generateDNSCryptCertificate(const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end, DnsCryptCert& certOut, DnsCryptPrivateKey& keyOut)
202{
203 bool success = false;
204 unsigned char providerPrivateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
205 sodium_mlock(providerPrivateKey, sizeof(providerPrivateKey));
206 sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey));
207
208 try {
209 ifstream providerKStream(providerPrivateKeyFile);
210 providerKStream.read((char*) providerPrivateKey, sizeof(providerPrivateKey));
211 if (providerKStream.fail()) {
212 providerKStream.close();
213 throw std::runtime_error("Invalid DNSCrypt provider key file " + providerPrivateKeyFile);
214 }
215
216 DnsCryptContext::generateCertificate(serial, begin, end, providerPrivateKey, keyOut, certOut);
217 success = true;
218 }
219 catch(const std::exception& e) {
220 errlog(e.what());
221 }
ea0aa517 222
79500db5
RG
223 sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey));
224 sodium_munlock(providerPrivateKey, sizeof(providerPrivateKey));
225 return success;
226}
227#endif /* HAVE_DNSCRYPT */
71c94675 228
886e2cf2 229void moreLua(bool client)
ea0aa517 230{
78ffa782 231 typedef NetmaskTree<DynBlock> nmts_t;
ea0aa517 232 g_lua.writeFunction("newCA", [](const std::string& name) { return ComboAddress(name); });
68e82cf9 233
68e82cf9 234 g_lua.writeFunction("newNMG", []() { return NetmaskGroup(); });
235 g_lua.registerFunction<void(NetmaskGroup::*)(const std::string&mask)>("addMask", [](NetmaskGroup&nmg, const std::string& mask)
7a25c01b
RG
236 {
237 nmg.addMask(mask);
238 });
1b096265 239 g_lua.registerFunction<void(NetmaskGroup::*)(const std::map<ComboAddress,int>& map)>("addMasks", [](NetmaskGroup&nmg, const std::map<ComboAddress,int>& map)
7a25c01b
RG
240 {
241 for (const auto& entry : map) {
242 nmg.addMask(Netmask(entry.first));
243 }
244 });
68e82cf9 245
246 g_lua.registerFunction("match", (bool (NetmaskGroup::*)(const ComboAddress&) const)&NetmaskGroup::match);
247 g_lua.registerFunction("size", &NetmaskGroup::size);
248 g_lua.registerFunction("clear", &NetmaskGroup::clear);
249
bd1c631b 250
6da5d988 251 g_lua.writeFunction("showDynBlocks", []() {
f758857a 252 setLuaNoSideEffect();
6da5d988 253 auto slow = g_dynblockNMG.getCopy();
78ffa782 254 struct timespec now;
85c7ca75 255 gettime(&now);
8d774826 256 boost::format fmt("%-24s %8d %8d %s\n");
71c94675 257 g_outputBuffer = (fmt % "What" % "Seconds" % "Blocks" % "Reason").str();
6da5d988 258 for(const auto& e: slow) {
78ffa782 259 if(now < e->second.until)
8d774826 260 g_outputBuffer+= (fmt % e->first.toString() % (e->second.until.tv_sec - now.tv_sec) % e->second.blocks % e->second.reason).str();
6da5d988 261 }
71c94675 262 auto slow2 = g_dynblockSMT.getCopy();
263 slow2.visit([&now, &fmt](const SuffixMatchTree<DynBlock>& node) {
264 if(now <node.d_value.until) {
265 string dom("empty");
266 if(!node.d_value.domain.empty())
267 dom = node.d_value.domain.toString();
268 g_outputBuffer+= (fmt % dom % (node.d_value.until.tv_sec - now.tv_sec) % node.d_value.blocks % node.d_value.reason).str();
269 }
270 });
271
6da5d988 272 });
273
78ffa782 274 g_lua.writeFunction("clearDynBlocks", []() {
f758857a 275 setLuaSideEffect();
78ffa782 276 nmts_t nmg;
277 g_dynblockNMG.setState(nmg);
71c94675 278 SuffixMatchTree<DynBlock> smt;
279 g_dynblockSMT.setState(smt);
78ffa782 280 });
281
282 g_lua.writeFunction("addDynBlocks",
7b925432 283 [](const map<ComboAddress,int>& m, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
f758857a 284 setLuaSideEffect();
78ffa782 285 auto slow = g_dynblockNMG.getCopy();
94b44bfa 286 struct timespec until, now;
85c7ca75 287 gettime(&now);
94b44bfa 288 until=now;
32c99b3d 289 int actualSeconds = seconds ? *seconds : 10;
290 until.tv_sec += actualSeconds;
94b44bfa 291 for(const auto& capair : m) {
8d06661a 292 unsigned int count = 0;
bac6e8fb 293 auto got = slow.lookup(Netmask(capair.first));
e126e7dd 294 bool expired=false;
bac6e8fb 295 if(got) {
94b44bfa 296 if(until < got->second.until) // had a longer policy
297 continue;
e126e7dd 298 if(now < got->second.until) // only inherit count on fresh query we are extending
94b44bfa 299 count=got->second.blocks;
e126e7dd 300 else
301 expired=true;
94b44bfa 302 }
7b925432 303 DynBlock db{msg,until,DNSName(),(action ? *action : DNSAction::Action::None)};
94b44bfa 304 db.blocks=count;
e126e7dd 305 if(!got || expired)
bac6e8fb 306 warnlog("Inserting dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg);
94b44bfa 307 slow.insert(Netmask(capair.first)).second=db;
308 }
78ffa782 309 g_dynblockNMG.setState(slow);
310 });
311
71c94675 312 g_lua.writeFunction("addDynBlockSMT",
7b925432 313 [](const vector<pair<unsigned int, string> >&names, const std::string& msg, boost::optional<int> seconds, boost::optional<DNSAction::Action> action) {
71c94675 314 setLuaSideEffect();
315 auto slow = g_dynblockSMT.getCopy();
316 struct timespec until, now;
317 gettime(&now);
318 until=now;
319 int actualSeconds = seconds ? *seconds : 10;
320 until.tv_sec += actualSeconds;
321
322 for(const auto& capair : names) {
323 unsigned int count = 0;
324 DNSName domain(capair.second);
325 auto got = slow.lookup(domain);
326 bool expired=false;
327 if(got) {
328 if(until < got->until) // had a longer policy
329 continue;
330 if(now < got->until) // only inherit count on fresh query we are extending
331 count=got->blocks;
332 else
333 expired=true;
334 }
335
7b925432 336 DynBlock db{msg,until,domain,(action ? *action : DNSAction::Action::None)};
71c94675 337 db.blocks=count;
338 if(!got || expired)
339 warnlog("Inserting dynamic block for %s for %d seconds: %s", domain, actualSeconds, msg);
340 slow.add(domain, db);
341 }
342 g_dynblockSMT.setState(slow);
343 });
344
dd46e5e3
RG
345 g_lua.writeFunction("setDynBlocksAction", [](DNSAction::Action action) {
346 if (!g_configurationDone) {
c4f5aeff 347 if (action == DNSAction::Action::Drop || action == DNSAction::Action::Refused || action == DNSAction::Action::Truncate) {
dd46e5e3
RG
348 g_dynBlockAction = action;
349 }
350 else {
c4f5aeff
RG
351 errlog("Dynamic blocks action can only be Drop, Refused or Truncate!");
352 g_outputBuffer="Dynamic blocks action can only be Drop, Refused or Truncate!\n";
dd46e5e3
RG
353 }
354 } else {
355 g_outputBuffer="Dynamic blocks action cannot be altered at runtime!\n";
356 }
357 });
78ffa782 358
6da5d988 359 g_lua.registerFunction<bool(nmts_t::*)(const ComboAddress&)>("match",
360 [](nmts_t& s, const ComboAddress& ca) { return s.match(ca); });
ea0aa517 361
f8d85d5b 362 g_lua.writeFunction("exceedServFails", [](unsigned int rate, int seconds) {
f758857a 363 setLuaNoSideEffect();
ea0aa517 364 return exceedRCode(rate, seconds, RCode::ServFail);
365 });
366 g_lua.writeFunction("exceedNXDOMAINs", [](unsigned int rate, int seconds) {
f758857a 367 setLuaNoSideEffect();
ea0aa517 368 return exceedRCode(rate, seconds, RCode::NXDomain);
369 });
370
0ba5eecf 371
372
ea0aa517 373 g_lua.writeFunction("exceedRespByterate", [](unsigned int rate, int seconds) {
f758857a 374 setLuaNoSideEffect();
ea0aa517 375 return exceedRespByterate(rate, seconds);
376 });
377
0ba5eecf 378 g_lua.writeFunction("exceedQTypeRate", [](uint16_t type, unsigned int rate, int seconds) {
f758857a 379 setLuaNoSideEffect();
0ba5eecf 380 return exceedQueryGen(rate, seconds, [type](counts_t& counts, const Rings::Query& q) {
381 if(q.qtype==type)
382 counts[q.requestor]++;
383 });
bac6e8fb 384 });
0ba5eecf 385
bac6e8fb 386 g_lua.writeFunction("exceedQRate", [](unsigned int rate, int seconds) {
387 setLuaNoSideEffect();
388 return exceedQueryGen(rate, seconds, [](counts_t& counts, const Rings::Query& q) {
389 counts[q.requestor]++;
390 });
0ba5eecf 391 });
392
71c94675 393 g_lua.writeFunction("getRespRing", getRespRing);
394
395 g_lua.registerFunction<StatNode, unsigned int()>("numChildren",
396 [](StatNode& sn) -> unsigned int {
397
398 return sn.children.size();
399 } );
400
401 g_lua.registerMember("fullname", &StatNode::fullname);
431c4359 402 g_lua.registerMember("labelsCount", &StatNode::labelsCount);
71c94675 403 g_lua.registerMember("servfails", &StatNode::Stat::servfails);
404 g_lua.registerMember("nxdomains", &StatNode::Stat::nxdomains);
405 g_lua.registerMember("queries", &StatNode::Stat::queries);
406
26b86deb
RG
407 g_lua.writeFunction("statNodeRespRing", [](statvisitor_t visitor, boost::optional<unsigned int> seconds) {
408 statNodeRespRing(visitor, seconds ? *seconds : 0);
409 });
bac6e8fb 410
7fc00937 411 g_lua.writeFunction("getTopBandwidth", [](unsigned int top) {
f758857a 412 setLuaNoSideEffect();
7fc00937 413 return g_rings.getTopBandwidth(top);
ddafcf1e 414 });
7fc00937 415 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)");
77f8ae1a 416
f758857a 417 g_lua.writeFunction("delta", []() {
418 setLuaNoSideEffect();
419 // we hold the lua lock already!
420 for(const auto& d : g_confDelta) {
421 struct tm tm;
422 localtime_r(&d.first.tv_sec, &tm);
423 char date[80];
171fca9a 424 strftime(date, sizeof(date)-1, "-- %a %b %d %Y %H:%M:%S %Z\n", &tm);
f758857a 425 g_outputBuffer += date;
426 g_outputBuffer += d.second + "\n";
427 }
428 });
429
2a05b4a9 430 g_lua.writeFunction("grepq", [](boost::variant<string, vector<pair<int,string> > > inp, boost::optional<unsigned int> limit) {
f0ff4ec2 431 setLuaNoSideEffect();
77f8ae1a 432 boost::optional<Netmask> nm;
433 boost::optional<DNSName> dn;
2a05b4a9 434 int msec=-1;
435
436 vector<string> vec;
437 auto str=boost::get<string>(&inp);
438 if(str)
439 vec.push_back(*str);
440 else {
441 auto v = boost::get<vector<pair<int, string> > >(inp);
442 for(const auto& a: v)
443 vec.push_back(a.second);
77f8ae1a 444 }
2a05b4a9 445
446 for(const auto& s : vec) {
447 try
448 {
449 nm = Netmask(s);
450 }
451 catch(...) {
452 if(boost::ends_with(s,"ms") && sscanf(s.c_str(), "%ums", &msec)) {
453 ;
454 }
455 else {
456 try { dn=DNSName(s); }
457 catch(...)
458 {
459 g_outputBuffer = "Could not parse '"+s+"' as domain name or netmask";
460 return;
461 }
462 }
2d11d1b2 463 }
77f8ae1a 464 }
465
466 decltype(g_rings.queryRing) qr;
467 decltype(g_rings.respRing) rr;
468 {
469 ReadLock rl(&g_rings.queryLock);
470 qr=g_rings.queryRing;
471 }
b049c29b 472 sort(qr.begin(), qr.end(), [](const decltype(qr)::value_type& a, const decltype(qr)::value_type& b) {
473 return b.when < a.when;
474 });
77f8ae1a 475 {
476 std::lock_guard<std::mutex> lock(g_rings.respMutex);
477 rr=g_rings.respRing;
478 }
b049c29b 479
480 sort(rr.begin(), rr.end(), [](const decltype(rr)::value_type& a, const decltype(rr)::value_type& b) {
481 return b.when < a.when;
482 });
77f8ae1a 483
484 unsigned int num=0;
485 struct timespec now;
85c7ca75 486 gettime(&now);
b049c29b 487
77f8ae1a 488 std::multimap<struct timespec, string> out;
3fcaeeac 489
2a05b4a9 490 boost::format fmt("%-7.1f %-47s %-12s %-5d %-25s %-5s %-6.1f %-2s %-2s %-2s %s\n");
2d11d1b2 491 g_outputBuffer+= (fmt % "Time" % "Client" % "Server" % "ID" % "Name" % "Type" % "Lat." % "TC" % "RD" % "AA" % "Rcode").str();
3fcaeeac 492
2a05b4a9 493 if(msec==-1) {
494 for(const auto& c : qr) {
495 bool nmmatch=true, dnmatch=true;
496 if(nm)
497 nmmatch = nm->match(c.requestor);
498 if(dn)
499 dnmatch = c.name.isPartOf(*dn);
500 if(nmmatch && dnmatch) {
501 QType qt(c.qtype);
502 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() )) ;
503
504 if(limit && *limit==++num)
505 break;
506 }
77f8ae1a 507 }
508 }
509 num=0;
510
3fcaeeac 511
d68acc7a 512 string extra;
77f8ae1a 513 for(const auto& c : rr) {
2a05b4a9 514 bool nmmatch=true, dnmatch=true, msecmatch=true;
515 if(nm)
516 nmmatch = nm->match(c.requestor);
517 if(dn)
518 dnmatch = c.name.isPartOf(*dn);
519 if(msec != -1)
520 msecmatch=(c.usec/1000 > (unsigned int)msec);
521
522 if(nmmatch && dnmatch && msecmatch) {
77f8ae1a 523 QType qt(c.qtype);
d68acc7a 524 if(!c.dh.rcode)
525 extra=". " +std::to_string(htons(c.dh.ancount))+ " answers";
526 else
527 extra.clear();
2d11d1b2 528 if(c.usec != std::numeric_limits<decltype(c.usec)>::max())
529 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() )) ;
530 else
531 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() )) ;
77f8ae1a 532
533 if(limit && *limit==++num)
534 break;
535 }
536 }
537
538 for(const auto& p : out) {
539 g_outputBuffer+=p.second;
77f8ae1a 540 }
541 });
11e1e08b 542
efd35aa8 543 g_lua.writeFunction("addDNSCryptBind", [](const std::string& addr, const std::string& providerName, const std::string& certFile, const std::string keyFile, boost::optional<localbind_t> vars) {
11e1e08b
RG
544 if (g_configurationDone) {
545 g_outputBuffer="addDNSCryptBind cannot be used at runtime!\n";
546 return;
547 }
548#ifdef HAVE_DNSCRYPT
efd35aa8
RG
549 bool doTCP = true;
550 bool reusePort = false;
551 int tcpFastOpenQueueSize = 0;
552 std::string interface;
553
554 parseLocalBindVars(vars, doTCP, reusePort, tcpFastOpenQueueSize, interface);
555
11e1e08b
RG
556 try {
557 DnsCryptContext ctx(providerName, certFile, keyFile);
efd35aa8 558 g_dnsCryptLocals.push_back(std::make_tuple(ComboAddress(addr, 443), ctx, reusePort, tcpFastOpenQueueSize, interface));
11e1e08b
RG
559 }
560 catch(std::exception& e) {
561 errlog(e.what());
562 g_outputBuffer="Error: "+string(e.what())+"\n";
563 }
564#else
565 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
566#endif
567 });
568
569 g_lua.writeFunction("showDNSCryptBinds", []() {
570 setLuaNoSideEffect();
571#ifdef HAVE_DNSCRYPT
572 ostringstream ret;
573 boost::format fmt("%1$-3d %2% %|25t|%3$-20.20s %|26t|%4$-8d %|35t|%5$-21.21s %|56t|%6$-9d %|66t|%7$-21.21s" );
574 ret << (fmt % "#" % "Address" % "Provider Name" % "Serial" % "Validity" % "P. Serial" % "P. Validity") << endl;
575 size_t idx = 0;
576
577 for (const auto& local : g_dnsCryptLocals) {
4c34246d 578 const DnsCryptContext& ctx = std::get<1>(local);
79500db5 579 bool const hasOldCert = ctx.hasOldCertificate();
11e1e08b
RG
580 const DnsCryptCert& cert = ctx.getCurrentCertificate();
581 const DnsCryptCert& oldCert = ctx.getOldCertificate();
582
4c34246d 583 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;
11e1e08b
RG
584 idx++;
585 }
586
587 g_outputBuffer=ret.str();
588#else
589 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
590#endif
591 });
592
79500db5
RG
593 g_lua.writeFunction("getDNSCryptBind", [client](size_t idx) {
594 setLuaNoSideEffect();
595#ifdef HAVE_DNSCRYPT
596 DnsCryptContext* ret = nullptr;
597 if (idx < g_dnsCryptLocals.size()) {
598 ret = &(std::get<1>(g_dnsCryptLocals.at(idx)));
599 }
600 return ret;
601#else
602 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
603#endif
604 });
605
606#ifdef HAVE_DNSCRYPT
607 /* DnsCryptContext bindings */
608 g_lua.registerFunction<std::string(DnsCryptContext::*)()>("getProviderName", [](const DnsCryptContext& ctx) { return ctx.getProviderName(); });
609 g_lua.registerFunction<DnsCryptCert(DnsCryptContext::*)()>("getCurrentCertificate", [](const DnsCryptContext& ctx) { return ctx.getCurrentCertificate(); });
610 g_lua.registerFunction<DnsCryptCert(DnsCryptContext::*)()>("getOldCertificate", [](const DnsCryptContext& ctx) { return ctx.getOldCertificate(); });
611 g_lua.registerFunction("hasOldCertificate", &DnsCryptContext::hasOldCertificate);
612 g_lua.registerFunction("loadNewCertificate", &DnsCryptContext::loadNewCertificate);
613 g_lua.registerFunction<void(DnsCryptContext::*)(const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end)>("generateAndLoadInMemoryCertificate", [](DnsCryptContext& ctx, const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end) {
614 DnsCryptPrivateKey privateKey;
615 DnsCryptCert cert;
616
617 try {
618 if (generateDNSCryptCertificate(providerPrivateKeyFile, serial, begin, end, cert, privateKey)) {
619 ctx.setNewCertificate(cert, privateKey);
620 }
621 }
622 catch(const std::exception& e) {
623 errlog(e.what());
624 g_outputBuffer="Error: "+string(e.what())+"\n";
625 }
626 });
627
628 /* DnsCryptCert */
629 g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getMagic", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.magic), sizeof(cert.magic)); });
630 g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getEsVersion", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.esVersion), sizeof(cert.esVersion)); });
631 g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getProtocolMinorVersion", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.protocolMinorVersion), sizeof(cert.protocolMinorVersion)); });
632 g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getSignature", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.signature), sizeof(cert.signature)); });
633 g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getResolverPublicKey", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.signedData.resolverPK), sizeof(cert.signedData.resolverPK)); });
634 g_lua.registerFunction<std::string(DnsCryptCert::*)()>("getClientMagic", [](const DnsCryptCert& cert) { return std::string(reinterpret_cast<const char*>(cert.signedData.clientMagic), sizeof(cert.signedData.clientMagic)); });
635 g_lua.registerFunction<uint32_t(DnsCryptCert::*)()>("getSerial", [](const DnsCryptCert& cert) { return cert.signedData.serial; });
13a325f5
RG
636 g_lua.registerFunction<uint32_t(DnsCryptCert::*)()>("getTSStart", [](const DnsCryptCert& cert) { return ntohl(cert.signedData.tsStart); });
637 g_lua.registerFunction<uint32_t(DnsCryptCert::*)()>("getTSEnd", [](const DnsCryptCert& cert) { return ntohl(cert.signedData.tsEnd); });
79500db5
RG
638#endif
639
11e1e08b
RG
640 g_lua.writeFunction("generateDNSCryptProviderKeys", [](const std::string& publicKeyFile, const std::string privateKeyFile) {
641 setLuaNoSideEffect();
642#ifdef HAVE_DNSCRYPT
643 unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
644 unsigned char privateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
645 sodium_mlock(privateKey, sizeof(privateKey));
646
647 try {
648 DnsCryptContext::generateProviderKeys(publicKey, privateKey);
649
650 ofstream pubKStream(publicKeyFile);
651 pubKStream.write((char*) publicKey, sizeof(publicKey));
652 pubKStream.close();
653
654 ofstream privKStream(privateKeyFile);
655 privKStream.write((char*) privateKey, sizeof(privateKey));
656 privKStream.close();
657
658 g_outputBuffer="Provider fingerprint is: " + DnsCryptContext::getProviderFingerprint(publicKey) + "\n";
659 }
660 catch(std::exception& e) {
661 errlog(e.what());
662 g_outputBuffer="Error: "+string(e.what())+"\n";
663 }
664
665 sodium_memzero(privateKey, sizeof(privateKey));
666 sodium_munlock(privateKey, sizeof(privateKey));
667#else
668 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
669#endif
670 });
671
b8db58a2
RG
672 g_lua.writeFunction("printDNSCryptProviderFingerprint", [](const std::string& publicKeyFile) {
673 setLuaNoSideEffect();
674#ifdef HAVE_DNSCRYPT
675 unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
676
677 try {
678 ifstream file(publicKeyFile);
679 file.read((char *) &publicKey, sizeof(publicKey));
680
681 if (file.fail())
682 throw std::runtime_error("Invalid dnscrypt provider public key file " + publicKeyFile);
683
684 file.close();
685 g_outputBuffer="Provider fingerprint is: " + DnsCryptContext::getProviderFingerprint(publicKey) + "\n";
686 }
687 catch(std::exception& e) {
688 errlog(e.what());
689 g_outputBuffer="Error: "+string(e.what())+"\n";
690 }
691#else
692 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
693#endif
694 });
695
11e1e08b
RG
696 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) {
697 setLuaNoSideEffect();
698#ifdef HAVE_DNSCRYPT
79500db5
RG
699 DnsCryptPrivateKey privateKey;
700 DnsCryptCert cert;
11e1e08b
RG
701
702 try {
79500db5
RG
703 if (generateDNSCryptCertificate(providerPrivateKeyFile, serial, begin, end, cert, privateKey)) {
704 privateKey.saveToFile(privateKeyFile);
705 DnsCryptContext::saveCertFromFile(cert, certificateFile);
11e1e08b 706 }
11e1e08b 707 }
79500db5 708 catch(const std::exception& e) {
11e1e08b
RG
709 errlog(e.what());
710 g_outputBuffer="Error: "+string(e.what())+"\n";
711 }
11e1e08b
RG
712#else
713 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
714#endif
715 });
716
886e2cf2
RG
717 g_lua.writeFunction("showPools", []() {
718 setLuaNoSideEffect();
719 try {
720 ostringstream ret;
938184df
RG
721 boost::format fmt("%1$-20.20s %|25t|%2$20s %|25t|%3$20s %|50t|%4%" );
722 // 1 2 3 4
723 ret << (fmt % "Name" % "Cache" % "ServerPolicy" % "Servers" ) << endl;
886e2cf2
RG
724
725 const auto localPools = g_pools.getCopy();
726 for (const auto& entry : localPools) {
727 const string& name = entry.first;
728 const std::shared_ptr<ServerPool> pool = entry.second;
729 string cache = pool->packetCache != nullptr ? pool->packetCache->toString() : "";
938184df 730 string policy = g_policy.getLocal()->name;
b9f8a6c8 731 if (pool->policy != nullptr) {
938184df
RG
732 policy = pool->policy->name;
733 }
886e2cf2
RG
734 string servers;
735
736 for (const auto& server: pool->servers) {
737 if (!servers.empty()) {
738 servers += ", ";
739 }
740 if (!server.second->name.empty()) {
741 servers += server.second->name;
742 servers += " ";
743 }
744 servers += server.second->remote.toStringWithPort();
745 }
746
938184df 747 ret << (fmt % name % cache % policy % servers) << endl;
886e2cf2
RG
748 }
749 g_outputBuffer=ret.str();
750 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
751 });
752
753 g_lua.registerFunction<void(std::shared_ptr<ServerPool>::*)(std::shared_ptr<DNSDistPacketCache>)>("setCache", [](std::shared_ptr<ServerPool> pool, std::shared_ptr<DNSDistPacketCache> cache) {
2e439533
RG
754 if (pool) {
755 pool->packetCache = cache;
756 }
886e2cf2
RG
757 });
758 g_lua.registerFunction("getCache", &ServerPool::getCache);
f87c4aff 759 g_lua.registerFunction<void(std::shared_ptr<ServerPool>::*)()>("unsetCache", [](std::shared_ptr<ServerPool> pool) {
2e439533
RG
760 if (pool) {
761 pool->packetCache = nullptr;
762 }
f87c4aff 763 });
886e2cf2 764
2b67180c
RG
765 g_lua.writeFunction("newPacketCache", [client](size_t maxEntries, boost::optional<uint32_t> maxTTL, boost::optional<uint32_t> minTTL, boost::optional<uint32_t> tempFailTTL, boost::optional<uint32_t> staleTTL, boost::optional<bool> dontAge) {
766 return std::make_shared<DNSDistPacketCache>(maxEntries, maxTTL ? *maxTTL : 86400, minTTL ? *minTTL : 0, tempFailTTL ? *tempFailTTL : 60, staleTTL ? *staleTTL : 60, dontAge ? *dontAge : false);
886e2cf2
RG
767 });
768 g_lua.registerFunction("toString", &DNSDistPacketCache::toString);
769 g_lua.registerFunction("isFull", &DNSDistPacketCache::isFull);
4275aaba
RG
770 g_lua.registerFunction("purgeExpired", &DNSDistPacketCache::purgeExpired);
771 g_lua.registerFunction("expunge", &DNSDistPacketCache::expunge);
490dc586
RG
772 g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)(const DNSName& dname, boost::optional<uint16_t> qtype, boost::optional<bool> suffixMatch)>("expungeByName", [](
773 std::shared_ptr<DNSDistPacketCache> cache,
774 const DNSName& dname,
775 boost::optional<uint16_t> qtype,
776 boost::optional<bool> suffixMatch) {
2e439533 777 if (cache) {
490dc586 778 cache->expungeByName(dname, qtype ? *qtype : QType::ANY, suffixMatch ? *suffixMatch : false);
2e439533 779 }
886e2cf2
RG
780 });
781 g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)()>("printStats", [](const std::shared_ptr<DNSDistPacketCache> cache) {
2e439533 782 if (cache) {
9e9be156
RG
783 g_outputBuffer="Entries: " + std::to_string(cache->getEntriesCount()) + "/" + std::to_string(cache->getMaxEntries()) + "\n";
784 g_outputBuffer+="Hits: " + std::to_string(cache->getHits()) + "\n";
2e439533
RG
785 g_outputBuffer+="Misses: " + std::to_string(cache->getMisses()) + "\n";
786 g_outputBuffer+="Deferred inserts: " + std::to_string(cache->getDeferredInserts()) + "\n";
787 g_outputBuffer+="Deferred lookups: " + std::to_string(cache->getDeferredLookups()) + "\n";
788 g_outputBuffer+="Lookup Collisions: " + std::to_string(cache->getLookupCollisions()) + "\n";
789 g_outputBuffer+="Insert Collisions: " + std::to_string(cache->getInsertCollisions()) + "\n";
cc8cefe1 790 g_outputBuffer+="TTL Too Shorts: " + std::to_string(cache->getTTLTooShorts()) + "\n";
2e439533 791 }
886e2cf2
RG
792 });
793
794 g_lua.writeFunction("getPool", [client](const string& poolName) {
795 if (client) {
796 return std::make_shared<ServerPool>();
797 }
798 auto localPools = g_pools.getCopy();
799 std::shared_ptr<ServerPool> pool = createPoolIfNotExists(localPools, poolName);
800 g_pools.setState(localPools);
801 return pool;
802 });
9e87dcb8
RG
803
804 g_lua.writeFunction("setVerboseHealthChecks", [](bool verbose) { g_verboseHealthChecks=verbose; });
1ea747c0 805 g_lua.writeFunction("setStaleCacheEntriesTTL", [](uint32_t ttl) { g_staleCacheEntriesTTL = ttl; });
d8c19b98 806
788c3243
RG
807 g_lua.writeFunction("DropResponseAction", []() {
808 return std::shared_ptr<DNSResponseAction>(new DropResponseAction);
809 });
810
811 g_lua.writeFunction("AllowResponseAction", []() {
812 return std::shared_ptr<DNSResponseAction>(new AllowResponseAction);
813 });
814
815 g_lua.writeFunction("DelayResponseAction", [](int msec) {
816 return std::shared_ptr<DNSResponseAction>(new DelayResponseAction(msec));
817 });
818
a94673ea 819 g_lua.writeFunction("RemoteLogAction", [](std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSQuestion&, DNSDistProtoBufMessage*)> > alterFunc) {
5965ea6a 820#ifdef HAVE_PROTOBUF
a94673ea 821 return std::shared_ptr<DNSAction>(new RemoteLogAction(logger, alterFunc));
5965ea6a
RG
822#else
823 throw std::runtime_error("Protobuf support is required to use RemoteLogAction");
824#endif
d8c19b98 825 });
165c9030 826 g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr<RemoteLogger> logger, boost::optional<std::function<void(const DNSResponse&, DNSDistProtoBufMessage*)> > alterFunc, boost::optional<bool> includeCNAME) {
5965ea6a 827#ifdef HAVE_PROTOBUF
165c9030 828 return std::shared_ptr<DNSResponseAction>(new RemoteLogResponseAction(logger, alterFunc, includeCNAME ? *includeCNAME : false));
5965ea6a
RG
829#else
830 throw std::runtime_error("Protobuf support is required to use RemoteLogResponseAction");
831#endif
d8c19b98 832 });
a94673ea
RG
833
834 g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const Netmask&)>("setEDNSSubnet", [](DNSDistProtoBufMessage& message, const Netmask& subnet) { message.setEDNSSubnet(subnet); });
835 g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const DNSName&, uint16_t, uint16_t)>("setQuestion", [](DNSDistProtoBufMessage& message, const DNSName& qname, uint16_t qtype, uint16_t qclass) { message.setQuestion(qname, qtype, qclass); });
836 g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(size_t)>("setBytes", [](DNSDistProtoBufMessage& message, size_t bytes) { message.setBytes(bytes); });
837 g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(time_t, uint32_t)>("setTime", [](DNSDistProtoBufMessage& message, time_t sec, uint32_t usec) { message.setTime(sec, usec); });
838 g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(time_t, uint32_t)>("setQueryTime", [](DNSDistProtoBufMessage& message, time_t sec, uint32_t usec) { message.setQueryTime(sec, usec); });
839 g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(uint8_t)>("setResponseCode", [](DNSDistProtoBufMessage& message, uint8_t rcode) { message.setResponseCode(rcode); });
840 g_lua.registerFunction<std::string(DNSDistProtoBufMessage::*)()>("toDebugString", [](const DNSDistProtoBufMessage& message) { return message.toDebugString(); });
841
842 g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const ComboAddress&)>("setRequestor", [](DNSDistProtoBufMessage& message, const ComboAddress& addr) {
843 message.setRequestor(addr);
844 });
845 g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&)>("setRequestorFromString", [](DNSDistProtoBufMessage& message, const std::string& str) {
846 message.setRequestor(str);
847 });
848 g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const ComboAddress&)>("setResponder", [](DNSDistProtoBufMessage& message, const ComboAddress& addr) {
849 message.setResponder(addr);
850 });
851 g_lua.registerFunction<void(DNSDistProtoBufMessage::*)(const std::string&)>("setResponderFromString", [](DNSDistProtoBufMessage& message, const std::string& str) {
852 message.setResponder(str);
853 });
854
ec469dd7
RG
855 g_lua.writeFunction("newRemoteLogger", [client](const std::string& remote, boost::optional<uint16_t> timeout, boost::optional<uint64_t> maxQueuedEntries, boost::optional<uint8_t> reconnectWaitTime) {
856 return std::make_shared<RemoteLogger>(ComboAddress(remote), timeout ? *timeout : 2, maxQueuedEntries ? *maxQueuedEntries : 100, reconnectWaitTime ? *reconnectWaitTime : 1);
d8c19b98
RG
857 });
858
384c2cb2
RG
859 g_lua.writeFunction("TeeAction", [](const std::string& remote, boost::optional<bool> addECS) {
860 return std::shared_ptr<DNSAction>(new TeeAction(ComboAddress(remote, 53), addECS ? *addECS : false));
cf6874ba 861 });
862
ff0902ec
RG
863 g_lua.writeFunction("ECSPrefixLengthAction", [](uint16_t v4PrefixLength, uint16_t v6PrefixLength) {
864 return std::shared_ptr<DNSAction>(new ECSPrefixLengthAction(v4PrefixLength, v6PrefixLength));
865 });
866
867 g_lua.writeFunction("ECSOverrideAction", [](bool ecsOverride) {
868 return std::shared_ptr<DNSAction>(new ECSOverrideAction(ecsOverride));
869 });
870
871 g_lua.writeFunction("DisableECSAction", []() {
872 return std::shared_ptr<DNSAction>(new DisableECSAction());
873 });
874
cf6874ba 875 g_lua.registerFunction<void(DNSAction::*)()>("printStats", [](const DNSAction& ta) {
3d8a6b1d 876 setLuaNoSideEffect();
cf6874ba 877 auto stats = ta.getStats();
878 for(const auto& s : stats) {
3d8a6b1d 879 g_outputBuffer+=s.first+"\t";
880 if((uint64_t)s.second == s.second)
881 g_outputBuffer += std::to_string((uint64_t)s.second)+"\n";
882 else
883 g_outputBuffer += std::to_string(s.second)+"\n";
cf6874ba 884 }
885 });
3d8a6b1d 886
887 g_lua.writeFunction("getAction", [](unsigned int num) {
888 setLuaNoSideEffect();
17f52224 889 boost::optional<std::shared_ptr<DNSAction>> ret;
3d8a6b1d 890 auto rulactions = g_rulactions.getCopy();
17f52224 891 if(num < rulactions.size())
892 ret=rulactions[num].second;
893 return ret;
3d8a6b1d 894 });
895
896 g_lua.registerFunction("getStats", &DNSAction::getStats);
897
f186603d
RG
898 g_lua.writeFunction("addResponseAction", [](luadnsrule_t var, boost::variant<std::shared_ptr<DNSAction>, std::shared_ptr<DNSResponseAction> > era) {
899 if (era.type() == typeid(std::shared_ptr<DNSAction>)) {
900 throw std::runtime_error("addResponseAction() can only be called with response-related actions, not query-related ones. Are you looking for addAction()?");
901 }
902
903 auto ea = *boost::get<std::shared_ptr<DNSResponseAction>>(&era);
904
905 setLuaSideEffect();
906 auto rule=makeRule(var);
907 g_resprulactions.modify([rule, ea](decltype(g_resprulactions)::value_type& rulactions){
908 rulactions.push_back({rule, ea});
909 });
910 });
cf48b0ce 911
d4d5c5c3
RG
912 g_lua.writeFunction("showResponseRules", []() {
913 setLuaNoSideEffect();
914 boost::format fmt("%-3d %9d %-50s %s\n");
915 g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
916 int num=0;
917 for(const auto& lim : g_resprulactions.getCopy()) {
918 string name = lim.first->toString();
919 g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
920 ++num;
921 }
922 });
923
924 g_lua.writeFunction("rmResponseRule", [](unsigned int num) {
925 setLuaSideEffect();
926 auto rules = g_resprulactions.getCopy();
927 if(num >= rules.size()) {
928 g_outputBuffer = "Error: attempt to delete non-existing rule\n";
929 return;
930 }
931 rules.erase(rules.begin()+num);
932 g_resprulactions.setState(rules);
933 });
934
935 g_lua.writeFunction("topResponseRule", []() {
936 setLuaSideEffect();
937 auto rules = g_resprulactions.getCopy();
938 if(rules.empty())
939 return;
940 auto subject = *rules.rbegin();
941 rules.erase(std::prev(rules.end()));
942 rules.insert(rules.begin(), subject);
943 g_resprulactions.setState(rules);
944 });
945
946 g_lua.writeFunction("mvResponseRule", [](unsigned int from, unsigned int to) {
947 setLuaSideEffect();
948 auto rules = g_resprulactions.getCopy();
949 if(from >= rules.size() || to > rules.size()) {
950 g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
951 return;
952 }
953 auto subject = rules[from];
954 rules.erase(rules.begin()+from);
955 if(to == rules.size())
956 rules.push_back(subject);
957 else {
958 if(from < to)
959 --to;
960 rules.insert(rules.begin()+to, subject);
961 }
962 g_resprulactions.setState(rules);
963 });
964
cf48b0ce
RG
965 g_lua.writeFunction("addCacheHitResponseAction", [](luadnsrule_t var, std::shared_ptr<DNSResponseAction> ea) {
966 setLuaSideEffect();
967 auto rule=makeRule(var);
968 g_cachehitresprulactions.modify([rule, ea](decltype(g_cachehitresprulactions)::value_type& rulactions){
969 rulactions.push_back({rule, ea});
970 });
971 });
972
973 g_lua.writeFunction("showCacheHitResponseRules", []() {
974 setLuaNoSideEffect();
975 boost::format fmt("%-3d %9d %-50s %s\n");
976 g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
977 int num=0;
978 for(const auto& lim : g_cachehitresprulactions.getCopy()) {
979 string name = lim.first->toString();
980 g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
981 ++num;
982 }
983 });
984
985 g_lua.writeFunction("rmCacheHitResponseRule", [](unsigned int num) {
986 setLuaSideEffect();
987 auto rules = g_cachehitresprulactions.getCopy();
988 if(num >= rules.size()) {
989 g_outputBuffer = "Error: attempt to delete non-existing rule\n";
990 return;
991 }
992 rules.erase(rules.begin()+num);
993 g_cachehitresprulactions.setState(rules);
994 });
995
996 g_lua.writeFunction("topCacheHitResponseRule", []() {
997 setLuaSideEffect();
998 auto rules = g_cachehitresprulactions.getCopy();
999 if(rules.empty())
1000 return;
1001 auto subject = *rules.rbegin();
1002 rules.erase(std::prev(rules.end()));
1003 rules.insert(rules.begin(), subject);
1004 g_cachehitresprulactions.setState(rules);
1005 });
1006
1007 g_lua.writeFunction("mvCacheHitResponseRule", [](unsigned int from, unsigned int to) {
1008 setLuaSideEffect();
1009 auto rules = g_cachehitresprulactions.getCopy();
1010 if(from >= rules.size() || to > rules.size()) {
1011 g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
1012 return;
1013 }
1014 auto subject = rules[from];
1015 rules.erase(rules.begin()+from);
1016 if(to == rules.size())
1017 rules.push_back(subject);
1018 else {
1019 if(from < to)
1020 --to;
1021 rules.insert(rules.begin()+to, subject);
1022 }
1023 g_cachehitresprulactions.setState(rules);
1024 });
1025
87b515ed
RG
1026 g_lua.writeFunction("showBinds", []() {
1027 setLuaNoSideEffect();
1028 try {
1029 ostringstream ret;
1030 boost::format fmt("%1$-3d %2$-20.20s %|25t|%3$-8.8s %|35t|%4%" );
1031 // 1 2 3 4
1032 ret << (fmt % "#" % "Address" % "Protocol" % "Queries" ) << endl;
1033
1034 size_t counter = 0;
1035 for (const auto& front : g_frontends) {
1036 ret << (fmt % counter % front->local.toStringWithPort() % (front->udpFD != -1 ? "UDP" : "TCP") % front->queries) << endl;
1037 counter++;
1038 }
1039 g_outputBuffer=ret.str();
1040 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
1041 });
1042
1043 g_lua.writeFunction("getBind", [](size_t num) {
1044 setLuaNoSideEffect();
1045 ClientState* ret = nullptr;
1046 if(num < g_frontends.size()) {
1047 ret=g_frontends[num];
1048 }
1049 return ret;
1050 });
1051
1052 g_lua.registerFunction<std::string(ClientState::*)()>("toString", [](const ClientState& fe) {
1053 setLuaNoSideEffect();
1054 return fe.local.toStringWithPort();
1055 });
1056
b5b93e0b
RG
1057 g_lua.registerMember("muted", &ClientState::muted);
1058
ca4252e0
RG
1059 g_lua.writeFunction("help", [](boost::optional<std::string> command) {
1060 setLuaNoSideEffect();
1061 g_outputBuffer = "";
1062 for (const auto& keyword : g_consoleKeywords) {
1063 if (!command) {
1064 g_outputBuffer += keyword.toString() + "\n";
1065 }
1066 else if (keyword.name == command) {
1067 g_outputBuffer = keyword.toString() + "\n";
1068 return;
1069 }
1070 }
1071 if (command) {
1072 g_outputBuffer = "Nothing found for " + *command + "\n";
1073 }
1074 });
1075
1076 g_lua.writeFunction("showVersion", []() {
1077 setLuaNoSideEffect();
1078 g_outputBuffer = "dnsdist " + std::string(VERSION) + "\n";
1079 });
1080
87b515ed 1081#ifdef HAVE_EBPF
478c786e
RG
1082 g_lua.writeFunction("newBPFFilter", [client](uint32_t maxV4, uint32_t maxV6, uint32_t maxQNames) {
1083 if (client) {
1084 return std::shared_ptr<BPFFilter>(nullptr);
1085 }
87b515ed
RG
1086 return std::make_shared<BPFFilter>(maxV4, maxV6, maxQNames);
1087 });
1088
1089 g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const ComboAddress& ca)>("block", [](std::shared_ptr<BPFFilter> bpf, const ComboAddress& ca) {
1090 if (bpf) {
1091 return bpf->block(ca);
1092 }
1093 });
1094
1095 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) {
1096 if (bpf) {
1097 return bpf->block(qname, qtype ? *qtype : 255);
1098 }
1099 });
1100
1101 g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const ComboAddress& ca)>("unblock", [](std::shared_ptr<BPFFilter> bpf, const ComboAddress& ca) {
1102 if (bpf) {
1103 return bpf->unblock(ca);
1104 }
1105 });
1106
1107 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) {
1108 if (bpf) {
1109 return bpf->unblock(qname, qtype ? *qtype : 255);
1110 }
1111 });
1112
1113 g_lua.registerFunction<std::string(std::shared_ptr<BPFFilter>::*)()>("getStats", [](const std::shared_ptr<BPFFilter> bpf) {
1114 setLuaNoSideEffect();
1115 std::string res;
1116 if (bpf) {
1117 std::vector<std::pair<ComboAddress, uint64_t> > stats = bpf->getAddrStats();
1118 for (const auto& value : stats) {
1119 if (value.first.sin4.sin_family == AF_INET) {
1120 res += value.first.toString() + ": " + std::to_string(value.second) + "\n";
1121 }
1122 else if (value.first.sin4.sin_family == AF_INET6) {
1123 res += "[" + value.first.toString() + "]: " + std::to_string(value.second) + "\n";
1124 }
1125 }
1126 std::vector<std::tuple<DNSName, uint16_t, uint64_t> > qstats = bpf->getQNameStats();
1127 for (const auto& value : qstats) {
1128 res += std::get<0>(value).toString() + " " + std::to_string(std::get<1>(value)) + ": " + std::to_string(std::get<2>(value)) + "\n";
1129 }
1130 }
1131 return res;
1132 });
1133
1134 g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)()>("attachToAllBinds", [](std::shared_ptr<BPFFilter> bpf) {
1135 std::string res;
1136 if (bpf) {
8429ad04
RG
1137 for (const auto& frontend : g_frontends) {
1138 frontend->attachFilter(bpf);
87b515ed
RG
1139 }
1140 }
1141 });
1142
8429ad04
RG
1143 g_lua.registerFunction<void(ClientState::*)()>("detachFilter", [](ClientState& frontend) {
1144 frontend.detachFilter();
1145 });
1146
87b515ed
RG
1147 g_lua.registerFunction<void(ClientState::*)(std::shared_ptr<BPFFilter>)>("attachFilter", [](ClientState& frontend, std::shared_ptr<BPFFilter> bpf) {
1148 if (bpf) {
8429ad04 1149 frontend.attachFilter(bpf);
87b515ed
RG
1150 }
1151 });
1152
1153 g_lua.writeFunction("setDefaultBPFFilter", [](std::shared_ptr<BPFFilter> bpf) {
1154 if (g_configurationDone) {
1155 g_outputBuffer="setDefaultBPFFilter() cannot be used at runtime!\n";
1156 return;
1157 }
1158 g_defaultBPFFilter = bpf;
1159 });
1160
478c786e
RG
1161 g_lua.writeFunction("newDynBPFFilter", [client](std::shared_ptr<BPFFilter> bpf) {
1162 if (client) {
1163 return std::shared_ptr<DynBPFFilter>(nullptr);
1164 }
87b515ed
RG
1165 return std::make_shared<DynBPFFilter>(bpf);
1166 });
1167
8429ad04
RG
1168 g_lua.writeFunction("registerDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
1169 if (dbpf) {
1170 g_dynBPFFilters.push_back(dbpf);
1171 }
1172 });
1173
1174 g_lua.writeFunction("unregisterDynBPFFilter", [](std::shared_ptr<DynBPFFilter> dbpf) {
1175 if (dbpf) {
5459f1d8 1176 for (auto it = g_dynBPFFilters.begin(); it != g_dynBPFFilters.end(); it++) {
8429ad04
RG
1177 if (*it == dbpf) {
1178 g_dynBPFFilters.erase(it);
1179 break;
1180 }
1181 }
1182 }
1183 });
1184
87b515ed
RG
1185 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) {
1186 if (dbpf) {
1187 struct timespec until;
1188 clock_gettime(CLOCK_MONOTONIC, &until);
1189 until.tv_sec += seconds ? *seconds : 10;
1190 dbpf->block(addr, until);
1191 }
1192 });
1193
1194 g_lua.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)()>("purgeExpired", [](std::shared_ptr<DynBPFFilter> dbpf) {
1195 if (dbpf) {
1196 struct timespec now;
1197 clock_gettime(CLOCK_MONOTONIC, &now);
1198 dbpf->purgeExpired(now);
1199 }
1200 });
1201
1202 g_lua.writeFunction("addBPFFilterDynBlocks", [](const map<ComboAddress,int>& m, std::shared_ptr<DynBPFFilter> dynbpf, boost::optional<int> seconds) {
1203 setLuaSideEffect();
1204 struct timespec until, now;
1205 clock_gettime(CLOCK_MONOTONIC, &now);
1206 until=now;
1207 int actualSeconds = seconds ? *seconds : 10;
1208 until.tv_sec += actualSeconds;
1209 for(const auto& capair : m) {
1210 dynbpf->block(capair.first, until);
1211 }
1212 });
1213
1214#endif /* HAVE_EBPF */
86baa8df
RG
1215
1216 g_lua.writeFunction<std::unordered_map<string,uint64_t>()>("getStatisticsCounters", []() {
1217 setLuaNoSideEffect();
1218 std::unordered_map<string,uint64_t> res;
1219 for(const auto& entry : g_stats.entries) {
1220 if(const auto& val = boost::get<DNSDistStats::stat_t*>(&entry.second))
1221 res[entry.first] = (*val)->load();
1222 }
1223 return res;
1224 });
69389510
RG
1225
1226 g_lua.writeFunction("includeDirectory", [](const std::string& dirname) {
1227 if (g_configurationDone) {
1228 errlog("includeDirectory() cannot be used at runtime!");
1229 g_outputBuffer="includeDirectory() cannot be used at runtime!\n";
1230 return;
1231 }
1232
1233 if (g_included) {
1234 errlog("includeDirectory() cannot be used recursively!");
1235 g_outputBuffer="includeDirectory() cannot be used recursively!\n";
1236 return;
1237 }
1238
1239 g_included = true;
1240 struct stat st;
1241
1242 if (stat(dirname.c_str(), &st)) {
1243 errlog("The included directory %s does not exist!", dirname.c_str());
1244 g_outputBuffer="The included directory " + dirname + " does not exist!";
1245 return;
1246 }
1247
1248 if (!S_ISDIR(st.st_mode)) {
1249 errlog("The included directory %s is not a directory!", dirname.c_str());
1250 g_outputBuffer="The included directory " + dirname + " is not a directory!";
1251 return;
1252 }
1253
1254 DIR *dirp;
1255 struct dirent *ent;
00bf2fb9 1256 std::list<std::string> files;
69389510
RG
1257 if (!(dirp = opendir(dirname.c_str()))) {
1258 errlog("Error opening the included directory %s!", dirname.c_str());
1259 g_outputBuffer="Error opening the included directory " + dirname + "!";
1260 return;
1261 }
1262
1263 while((ent = readdir(dirp)) != NULL) {
1264 if (ent->d_name[0] == '.') {
1265 continue;
1266 }
1267
1268 if (boost::ends_with(ent->d_name, ".conf")) {
1269 std::ostringstream namebuf;
1270 namebuf << dirname.c_str() << "/" << ent->d_name;
1271
1272 if (stat(namebuf.str().c_str(), &st) || !S_ISREG(st.st_mode)) {
1273 continue;
1274 }
1275
00bf2fb9 1276 files.push_back(namebuf.str());
69389510
RG
1277 }
1278 }
00bf2fb9 1279
69389510 1280 closedir(dirp);
00bf2fb9
RG
1281 files.sort();
1282
1283 for (auto file = files.begin(); file != files.end(); ++file) {
1284 std::ifstream ifs(*file);
1285 if (!ifs) {
1286 warnlog("Unable to read configuration from '%s'", *file);
1287 } else {
1288 vinfolog("Read configuration from '%s'", *file);
1289 }
1290
1291 g_lua.executeCode(ifs);
1292 }
69389510
RG
1293
1294 g_included = false;
1295 });
56d68fad
RG
1296
1297 g_lua.writeFunction("setAPIWritable", [](bool writable, boost::optional<std::string> apiConfigDir) {
1298 setLuaSideEffect();
1299 g_apiReadWrite = writable;
1300 if (apiConfigDir) {
1301 if (!(*apiConfigDir).empty()) {
1302 g_apiConfigDirectory = *apiConfigDir;
1303 }
1304 else {
1305 errlog("The API configuration directory value cannot be empty!");
1306 g_outputBuffer="The API configuration directory value cannot be empty!";
1307 }
1308 }
1309 });
26a3cdb7
RG
1310
1311 g_lua.writeFunction("setServFailWhenNoServer", [](bool servfail) {
1312 setLuaSideEffect();
1313 g_servFailOnNoPolicy = servfail;
1314 });
e4c24bb3
RG
1315
1316 g_lua.writeFunction("setRingBuffersSize", [](size_t capacity) {
1317 setLuaSideEffect();
1318 if (g_configurationDone) {
1319 errlog("setRingBuffersSize() cannot be used at runtime!");
1320 g_outputBuffer="setRingBuffersSize() cannot be used at runtime!\n";
1321 return;
1322 }
1323 g_rings.setCapacity(capacity);
7caebbb2
RG
1324 });
1325
1326 g_lua.writeFunction("RDRule", []() {
1327 return std::shared_ptr<DNSRule>(new RDRule());
1328 });
1329
2b58b625 1330 g_lua.writeFunction("TimedIPSetRule", []() {
1331 return std::shared_ptr<TimedIPSetRule>(new TimedIPSetRule());
1332 });
1333
1334 g_lua.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)()>("clear", [](std::shared_ptr<TimedIPSetRule> tisr) {
1335 tisr->clear();
1336 });
1337
01b8149e 1338 g_lua.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)()>("cleanup", [](std::shared_ptr<TimedIPSetRule> tisr) {
1339 tisr->cleanup();
1340 });
1341
2b58b625 1342 g_lua.registerFunction<void(std::shared_ptr<TimedIPSetRule>::*)(const ComboAddress& ca, int t)>("add", [](std::shared_ptr<TimedIPSetRule> tisr, const ComboAddress& ca, int t) {
1343 tisr->add(ca, time(0)+t);
1344 });
1345
1346 g_lua.registerFunction<std::shared_ptr<DNSRule>(std::shared_ptr<TimedIPSetRule>::*)()>("slice", [](std::shared_ptr<TimedIPSetRule> tisr) {
1347 return std::dynamic_pointer_cast<DNSRule>(tisr);
1348 });
1349
36e763fa
RG
1350 g_lua.writeFunction("setWHashedPertubation", [](uint32_t pertub) {
1351 setLuaSideEffect();
1352 g_hashperturb = pertub;
1353 });
1354
edbda1ad
RG
1355 g_lua.writeFunction("setTCPUseSinglePipe", [](bool flag) {
1356 if (g_configurationDone) {
1357 g_outputBuffer="setTCPUseSinglePipe() cannot be used at runtime!\n";
1358 return;
1359 }
1360 setLuaSideEffect();
1361 g_useTCPSinglePipe = flag;
1362 });
1363
9f4eb5cc
RG
1364 g_lua.writeFunction("snmpAgent", [](bool enableTraps, boost::optional<std::string> masterSocket) {
1365#ifdef HAVE_NET_SNMP
1366 if (g_configurationDone) {
1367 errlog("snmpAgent() cannot be used at runtime!");
1368 g_outputBuffer="snmpAgent() cannot be used at runtime!\n";
1369 return;
1370 }
1371
1372 if (g_snmpEnabled) {
1373 errlog("snmpAgent() cannot be used twice!");
1374 g_outputBuffer="snmpAgent() cannot be used twice!\n";
1375 return;
1376 }
1377
1378 g_snmpEnabled = true;
1379 g_snmpTrapsEnabled = enableTraps;
1380 g_snmpAgent = new DNSDistSNMPAgent("dnsdist", masterSocket ? *masterSocket : std::string());
1381#else
1382 errlog("NET SNMP support is required to use snmpAgent()");
1383 g_outputBuffer="NET SNMP support is required to use snmpAgent()\n";
1384#endif /* HAVE_NET_SNMP */
1385 });
1386
1387 g_lua.writeFunction("SNMPTrapAction", [](boost::optional<std::string> reason) {
1388#ifdef HAVE_NET_SNMP
1389 return std::shared_ptr<DNSAction>(new SNMPTrapAction(reason ? *reason : ""));
1390#else
1391 throw std::runtime_error("NET SNMP support is required to use SNMPTrapAction()");
1392#endif /* HAVE_NET_SNMP */
1393 });
1394
1395 g_lua.writeFunction("SNMPTrapResponseAction", [](boost::optional<std::string> reason) {
1396#ifdef HAVE_NET_SNMP
1397 return std::shared_ptr<DNSResponseAction>(new SNMPTrapResponseAction(reason ? *reason : ""));
1398#else
1399 throw std::runtime_error("NET SNMP support is required to use SNMPTrapResponseAction()");
1400#endif /* HAVE_NET_SNMP */
1401 });
1402
1403 g_lua.writeFunction("sendCustomTrap", [](const std::string& str) {
1404#ifdef HAVE_NET_SNMP
1405 if (g_snmpAgent && g_snmpTrapsEnabled) {
1406 g_snmpAgent->sendCustomTrap(str);
1407 }
1408#endif /* HAVE_NET_SNMP */
1409 });
742c079a
RG
1410
1411 g_lua.writeFunction("setPoolServerPolicy", [](ServerPolicy policy, string pool) {
1412 setLuaSideEffect();
1413 auto localPools = g_pools.getCopy();
1414 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(policy));
b9f8a6c8 1415 g_pools.setState(localPools);
742c079a
RG
1416 });
1417
1418 g_lua.writeFunction("setPoolServerPolicyLua", [](string name, policyfunc_t policy, string pool) {
1419 setLuaSideEffect();
1420 auto localPools = g_pools.getCopy();
1421 setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(ServerPolicy{name, policy}));
b9f8a6c8 1422 g_pools.setState(localPools);
742c079a
RG
1423 });
1424
1425 g_lua.writeFunction("showPoolServerPolicy", [](string pool) {
1426 setLuaSideEffect();
1427 auto localPools = g_pools.getCopy();
1428 auto poolObj = getPool(localPools, pool);
b9f8a6c8 1429 if (poolObj->policy == nullptr) {
742c079a
RG
1430 g_outputBuffer=g_policy.getLocal()->name+"\n";
1431 } else {
1432 g_outputBuffer=poolObj->policy->name+"\n";
1433 }
1434 });
840ed663
RG
1435
1436 g_lua.writeFunction("setTCPDownstreamCleanupInterval", [](uint16_t interval) {
1437 setLuaSideEffect();
1438 g_downstreamTCPCleanupInterval = interval;
1439 });
ea0aa517 1440}