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