]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsdist-lua2.cc
Standardize license text in all PDNS files
[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
36 boost::tribool g_noLuaSideEffect;
37
38 /* this is a best effort way to prevent logging calls with no side-effects in the output of delta()
39 Functions can declare setLuaNoSideEffect() and if nothing else does declare a side effect, or nothing
40 has done so before on this invocation, this call won't be part of delta() output */
41 void setLuaNoSideEffect()
42 {
43 if(g_noLuaSideEffect==false) // there has been a side effect already
44 return;
45 g_noLuaSideEffect=true;
46 }
47
48 void setLuaSideEffect()
49 {
50 g_noLuaSideEffect=false;
51 }
52
53 bool getLuaNoSideEffect()
54 {
55 return g_noLuaSideEffect==true;
56 }
57
58 void resetLuaSideEffect()
59 {
60 g_noLuaSideEffect = boost::logic::indeterminate;
61 }
62
63 map<ComboAddress,int> filterScore(const map<ComboAddress, unsigned int,ComboAddress::addressOnlyLessThan >& counts,
64 double delta, int rate)
65 {
66 std::multimap<unsigned int,ComboAddress> score;
67 for(const auto& e : counts)
68 score.insert({e.second, e.first});
69
70 map<ComboAddress,int> ret;
71
72 double lim = delta*rate;
73 for(auto s = score.crbegin(); s != score.crend() && s->first > lim; ++s) {
74 ret[s->second]=s->first;
75 }
76 return ret;
77 }
78
79
80 typedef std::function<void(const StatNode&, const StatNode::Stat&, const StatNode::Stat&)> statvisitor_t;
81
82 void statNodeRespRing(statvisitor_t visitor)
83 {
84 std::lock_guard<std::mutex> lock(g_rings.respMutex);
85
86 StatNode root;
87 for(const auto& c : g_rings.respRing) {
88 root.submit(c.name, c.dh.rcode, c.requestor);
89 }
90 StatNode::Stat node;
91
92 root.visit([&visitor](const StatNode* node, const StatNode::Stat& self, const StatNode::Stat& children) {
93 visitor(*node, self, children);}, node);
94
95 }
96
97 vector<pair<unsigned int, std::unordered_map<string,string> > > getRespRing(boost::optional<int> rcode)
98 {
99 typedef std::unordered_map<string,string> entry_t;
100 vector<pair<unsigned int, entry_t > > ret;
101 std::lock_guard<std::mutex> lock(g_rings.respMutex);
102
103 entry_t e;
104 unsigned int count=1;
105 for(const auto& c : g_rings.respRing) {
106 if(rcode && (rcode.get() != c.dh.rcode))
107 continue;
108 e["qname"]=c.name.toString();
109 e["rcode"]=std::to_string(c.dh.rcode);
110 ret.push_back(std::make_pair(count,e));
111 count++;
112 }
113 return ret;
114 }
115
116 typedef map<ComboAddress, unsigned int,ComboAddress::addressOnlyLessThan > counts_t;
117 map<ComboAddress,int> exceedRespGen(int rate, int seconds, std::function<void(counts_t&, const Rings::Response&)> T)
118 {
119 counts_t counts;
120 struct timespec cutoff, mintime, now;
121 gettime(&now);
122 cutoff = mintime = now;
123 cutoff.tv_sec -= seconds;
124
125 std::lock_guard<std::mutex> lock(g_rings.respMutex);
126 for(const auto& c : g_rings.respRing) {
127 if(seconds && c.when < cutoff)
128 continue;
129 if(now < c.when)
130 continue;
131
132 T(counts, c);
133 if(c.when < mintime)
134 mintime = c.when;
135 }
136 double delta = seconds ? seconds : DiffTime(now, mintime);
137 return filterScore(counts, delta, rate);
138 }
139
140 map<ComboAddress,int> exceedQueryGen(int rate, int seconds, std::function<void(counts_t&, const Rings::Query&)> T)
141 {
142 counts_t counts;
143 struct timespec cutoff, mintime, now;
144 gettime(&now);
145 cutoff = mintime = now;
146 cutoff.tv_sec -= seconds;
147
148 ReadLock rl(&g_rings.queryLock);
149 for(const auto& c : g_rings.queryRing) {
150 if(seconds && c.when < cutoff)
151 continue;
152 if(now < c.when)
153 continue;
154 T(counts, c);
155 if(c.when < mintime)
156 mintime = c.when;
157 }
158 double delta = seconds ? seconds : DiffTime(now, mintime);
159 return filterScore(counts, delta, rate);
160 }
161
162
163 map<ComboAddress,int> exceedRCode(int rate, int seconds, int rcode)
164 {
165 return exceedRespGen(rate, seconds, [rcode](counts_t& counts, const Rings::Response& r)
166 {
167 if(r.dh.rcode == rcode)
168 counts[r.requestor]++;
169 });
170 }
171
172 map<ComboAddress,int> exceedRespByterate(int rate, int seconds)
173 {
174 return exceedRespGen(rate, seconds, [](counts_t& counts, const Rings::Response& r)
175 {
176 counts[r.requestor]+=r.size;
177 });
178 }
179
180
181
182 void moreLua(bool client)
183 {
184 typedef NetmaskTree<DynBlock> nmts_t;
185 g_lua.writeFunction("newCA", [](const std::string& name) { return ComboAddress(name); });
186
187
188 g_lua.writeFunction("newNMG", []() { return NetmaskGroup(); });
189 g_lua.registerFunction<void(NetmaskGroup::*)(const std::string&mask)>("addMask", [](NetmaskGroup&nmg, const std::string& mask)
190 {
191 nmg.addMask(mask);
192 });
193
194 g_lua.registerFunction("match", (bool (NetmaskGroup::*)(const ComboAddress&) const)&NetmaskGroup::match);
195 g_lua.registerFunction("size", &NetmaskGroup::size);
196 g_lua.registerFunction("clear", &NetmaskGroup::clear);
197
198
199 g_lua.writeFunction("showDynBlocks", []() {
200 setLuaNoSideEffect();
201 auto slow = g_dynblockNMG.getCopy();
202 struct timespec now;
203 gettime(&now);
204 boost::format fmt("%-24s %8d %8d %s\n");
205 g_outputBuffer = (fmt % "What" % "Seconds" % "Blocks" % "Reason").str();
206 for(const auto& e: slow) {
207 if(now < e->second.until)
208 g_outputBuffer+= (fmt % e->first.toString() % (e->second.until.tv_sec - now.tv_sec) % e->second.blocks % e->second.reason).str();
209 }
210 auto slow2 = g_dynblockSMT.getCopy();
211 slow2.visit([&now, &fmt](const SuffixMatchTree<DynBlock>& node) {
212 if(now <node.d_value.until) {
213 string dom("empty");
214 if(!node.d_value.domain.empty())
215 dom = node.d_value.domain.toString();
216 g_outputBuffer+= (fmt % dom % (node.d_value.until.tv_sec - now.tv_sec) % node.d_value.blocks % node.d_value.reason).str();
217 }
218 });
219
220 });
221
222 g_lua.writeFunction("clearDynBlocks", []() {
223 setLuaSideEffect();
224 nmts_t nmg;
225 g_dynblockNMG.setState(nmg);
226 SuffixMatchTree<DynBlock> smt;
227 g_dynblockSMT.setState(smt);
228 });
229
230 g_lua.writeFunction("addDynBlocks",
231 [](const map<ComboAddress,int>& m, const std::string& msg, boost::optional<int> seconds) {
232 setLuaSideEffect();
233 auto slow = g_dynblockNMG.getCopy();
234 struct timespec until, now;
235 gettime(&now);
236 until=now;
237 int actualSeconds = seconds ? *seconds : 10;
238 until.tv_sec += actualSeconds;
239 for(const auto& capair : m) {
240 unsigned int count = 0;
241 auto got = slow.lookup(Netmask(capair.first));
242 bool expired=false;
243 if(got) {
244 if(until < got->second.until) // had a longer policy
245 continue;
246 if(now < got->second.until) // only inherit count on fresh query we are extending
247 count=got->second.blocks;
248 else
249 expired=true;
250 }
251 DynBlock db{msg,until};
252 db.blocks=count;
253 if(!got || expired)
254 warnlog("Inserting dynamic block for %s for %d seconds: %s", capair.first.toString(), actualSeconds, msg);
255 slow.insert(Netmask(capair.first)).second=db;
256 }
257 g_dynblockNMG.setState(slow);
258 });
259
260 g_lua.writeFunction("addDynBlockSMT",
261 [](const vector<pair<unsigned int, string> >&names, const std::string& msg, boost::optional<int> seconds) {
262 setLuaSideEffect();
263 auto slow = g_dynblockSMT.getCopy();
264 struct timespec until, now;
265 gettime(&now);
266 until=now;
267 int actualSeconds = seconds ? *seconds : 10;
268 until.tv_sec += actualSeconds;
269
270 for(const auto& capair : names) {
271 unsigned int count = 0;
272 DNSName domain(capair.second);
273 auto got = slow.lookup(domain);
274 bool expired=false;
275 if(got) {
276 if(until < got->until) // had a longer policy
277 continue;
278 if(now < got->until) // only inherit count on fresh query we are extending
279 count=got->blocks;
280 else
281 expired=true;
282 }
283
284 DynBlock db{msg,until,domain};
285 db.blocks=count;
286 if(!got || expired)
287 warnlog("Inserting dynamic block for %s for %d seconds: %s", domain, actualSeconds, msg);
288 slow.add(domain, db);
289 }
290 g_dynblockSMT.setState(slow);
291 });
292
293
294
295 g_lua.registerFunction<bool(nmts_t::*)(const ComboAddress&)>("match",
296 [](nmts_t& s, const ComboAddress& ca) { return s.match(ca); });
297
298 g_lua.writeFunction("exceedServFails", [](unsigned int rate, int seconds) {
299 setLuaNoSideEffect();
300 return exceedRCode(rate, seconds, RCode::ServFail);
301 });
302 g_lua.writeFunction("exceedNXDOMAINs", [](unsigned int rate, int seconds) {
303 setLuaNoSideEffect();
304 return exceedRCode(rate, seconds, RCode::NXDomain);
305 });
306
307
308
309 g_lua.writeFunction("exceedRespByterate", [](unsigned int rate, int seconds) {
310 setLuaNoSideEffect();
311 return exceedRespByterate(rate, seconds);
312 });
313
314 g_lua.writeFunction("exceedQTypeRate", [](uint16_t type, unsigned int rate, int seconds) {
315 setLuaNoSideEffect();
316 return exceedQueryGen(rate, seconds, [type](counts_t& counts, const Rings::Query& q) {
317 if(q.qtype==type)
318 counts[q.requestor]++;
319 });
320 });
321
322 g_lua.writeFunction("exceedQRate", [](unsigned int rate, int seconds) {
323 setLuaNoSideEffect();
324 return exceedQueryGen(rate, seconds, [](counts_t& counts, const Rings::Query& q) {
325 counts[q.requestor]++;
326 });
327 });
328
329 g_lua.writeFunction("getRespRing", getRespRing);
330
331 g_lua.registerFunction<StatNode, unsigned int()>("numChildren",
332 [](StatNode& sn) -> unsigned int {
333
334 return sn.children.size();
335 } );
336
337 g_lua.registerMember("fullname", &StatNode::fullname);
338 g_lua.registerMember("servfails", &StatNode::Stat::servfails);
339 g_lua.registerMember("nxdomains", &StatNode::Stat::nxdomains);
340 g_lua.registerMember("queries", &StatNode::Stat::queries);
341
342 g_lua.writeFunction("statNodeRespRing", statNodeRespRing);
343
344 g_lua.writeFunction("getTopBandwidth", [](unsigned int top) {
345 setLuaNoSideEffect();
346 return g_rings.getTopBandwidth(top);
347 });
348 g_lua.executeCode(R"(function topBandwidth(top) top = top or 10; for k,v in ipairs(getTopBandwidth(top)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
349
350 g_lua.writeFunction("delta", []() {
351 setLuaNoSideEffect();
352 // we hold the lua lock already!
353 for(const auto& d : g_confDelta) {
354 struct tm tm;
355 localtime_r(&d.first.tv_sec, &tm);
356 char date[80];
357 strftime(date, sizeof(date)-1, "-- %a %b %d %Y %H:%M:%S %Z\n", &tm);
358 g_outputBuffer += date;
359 g_outputBuffer += d.second + "\n";
360 }
361 });
362
363 g_lua.writeFunction("grepq", [](boost::variant<string, vector<pair<int,string> > > inp, boost::optional<unsigned int> limit) {
364 setLuaNoSideEffect();
365 boost::optional<Netmask> nm;
366 boost::optional<DNSName> dn;
367 int msec=-1;
368
369 vector<string> vec;
370 auto str=boost::get<string>(&inp);
371 if(str)
372 vec.push_back(*str);
373 else {
374 auto v = boost::get<vector<pair<int, string> > >(inp);
375 for(const auto& a: v)
376 vec.push_back(a.second);
377 }
378
379 for(const auto& s : vec) {
380 try
381 {
382 nm = Netmask(s);
383 }
384 catch(...) {
385 if(boost::ends_with(s,"ms") && sscanf(s.c_str(), "%ums", &msec)) {
386 ;
387 }
388 else {
389 try { dn=DNSName(s); }
390 catch(...)
391 {
392 g_outputBuffer = "Could not parse '"+s+"' as domain name or netmask";
393 return;
394 }
395 }
396 }
397 }
398
399 decltype(g_rings.queryRing) qr;
400 decltype(g_rings.respRing) rr;
401 {
402 ReadLock rl(&g_rings.queryLock);
403 qr=g_rings.queryRing;
404 }
405 sort(qr.begin(), qr.end(), [](const decltype(qr)::value_type& a, const decltype(qr)::value_type& b) {
406 return b.when < a.when;
407 });
408 {
409 std::lock_guard<std::mutex> lock(g_rings.respMutex);
410 rr=g_rings.respRing;
411 }
412
413 sort(rr.begin(), rr.end(), [](const decltype(rr)::value_type& a, const decltype(rr)::value_type& b) {
414 return b.when < a.when;
415 });
416
417 unsigned int num=0;
418 struct timespec now;
419 gettime(&now);
420
421 std::multimap<struct timespec, string> out;
422
423 boost::format fmt("%-7.1f %-47s %-12s %-5d %-25s %-5s %-6.1f %-2s %-2s %-2s %s\n");
424 g_outputBuffer+= (fmt % "Time" % "Client" % "Server" % "ID" % "Name" % "Type" % "Lat." % "TC" % "RD" % "AA" % "Rcode").str();
425
426 if(msec==-1) {
427 for(const auto& c : qr) {
428 bool nmmatch=true, dnmatch=true;
429 if(nm)
430 nmmatch = nm->match(c.requestor);
431 if(dn)
432 dnmatch = c.name.isPartOf(*dn);
433 if(nmmatch && dnmatch) {
434 QType qt(c.qtype);
435 out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % "" % htons(c.dh.id) % c.name.toString() % qt.getName() % "" % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % "Question").str() )) ;
436
437 if(limit && *limit==++num)
438 break;
439 }
440 }
441 }
442 num=0;
443
444
445 string extra;
446 for(const auto& c : rr) {
447 bool nmmatch=true, dnmatch=true, msecmatch=true;
448 if(nm)
449 nmmatch = nm->match(c.requestor);
450 if(dn)
451 dnmatch = c.name.isPartOf(*dn);
452 if(msec != -1)
453 msecmatch=(c.usec/1000 > (unsigned int)msec);
454
455 if(nmmatch && dnmatch && msecmatch) {
456 QType qt(c.qtype);
457 if(!c.dh.rcode)
458 extra=". " +std::to_string(htons(c.dh.ancount))+ " answers";
459 else
460 extra.clear();
461 if(c.usec != std::numeric_limits<decltype(c.usec)>::max())
462 out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % c.ds.toStringWithPort() % htons(c.dh.id) % c.name.toString() % qt.getName() % (c.usec/1000.0) % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str() )) ;
463 else
464 out.insert(make_pair(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % c.ds.toStringWithPort() % htons(c.dh.id) % c.name.toString() % qt.getName() % "T.O" % (c.dh.tc ? "TC" : "") % (c.dh.rd? "RD" : "") % (c.dh.aa? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str() )) ;
465
466 if(limit && *limit==++num)
467 break;
468 }
469 }
470
471 for(const auto& p : out) {
472 g_outputBuffer+=p.second;
473 }
474 });
475
476 g_lua.writeFunction("addDNSCryptBind", [](const std::string& addr, const std::string& providerName, const std::string& certFile, const std::string keyFile, boost::optional<bool> reusePort, boost::optional<int> tcpFastOpenQueueSize) {
477 if (g_configurationDone) {
478 g_outputBuffer="addDNSCryptBind cannot be used at runtime!\n";
479 return;
480 }
481 #ifdef HAVE_DNSCRYPT
482 try {
483 DnsCryptContext ctx(providerName, certFile, keyFile);
484 g_dnsCryptLocals.push_back(std::make_tuple(ComboAddress(addr, 443), ctx, reusePort ? *reusePort : false, tcpFastOpenQueueSize ? *tcpFastOpenQueueSize : 0));
485 }
486 catch(std::exception& e) {
487 errlog(e.what());
488 g_outputBuffer="Error: "+string(e.what())+"\n";
489 }
490 #else
491 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
492 #endif
493 });
494
495 g_lua.writeFunction("showDNSCryptBinds", []() {
496 setLuaNoSideEffect();
497 #ifdef HAVE_DNSCRYPT
498 ostringstream ret;
499 boost::format fmt("%1$-3d %2% %|25t|%3$-20.20s %|26t|%4$-8d %|35t|%5$-21.21s %|56t|%6$-9d %|66t|%7$-21.21s" );
500 ret << (fmt % "#" % "Address" % "Provider Name" % "Serial" % "Validity" % "P. Serial" % "P. Validity") << endl;
501 size_t idx = 0;
502
503 for (const auto& local : g_dnsCryptLocals) {
504 const DnsCryptContext& ctx = std::get<1>(local);
505 bool const hasOldCert = ctx.hadOldCertificate();
506 const DnsCryptCert& cert = ctx.getCurrentCertificate();
507 const DnsCryptCert& oldCert = ctx.getOldCertificate();
508
509 ret<< (fmt % idx % std::get<0>(local).toStringWithPort() % ctx.getProviderName() % cert.signedData.serial % DnsCryptContext::certificateDateToStr(cert.signedData.tsEnd) % (hasOldCert ? oldCert.signedData.serial : 0) % (hasOldCert ? DnsCryptContext::certificateDateToStr(oldCert.signedData.tsEnd) : "-")) << endl;
510 idx++;
511 }
512
513 g_outputBuffer=ret.str();
514 #else
515 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
516 #endif
517 });
518
519 g_lua.writeFunction("generateDNSCryptProviderKeys", [](const std::string& publicKeyFile, const std::string privateKeyFile) {
520 setLuaNoSideEffect();
521 #ifdef HAVE_DNSCRYPT
522 unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
523 unsigned char privateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
524 sodium_mlock(privateKey, sizeof(privateKey));
525
526 try {
527 DnsCryptContext::generateProviderKeys(publicKey, privateKey);
528
529 ofstream pubKStream(publicKeyFile);
530 pubKStream.write((char*) publicKey, sizeof(publicKey));
531 pubKStream.close();
532
533 ofstream privKStream(privateKeyFile);
534 privKStream.write((char*) privateKey, sizeof(privateKey));
535 privKStream.close();
536
537 g_outputBuffer="Provider fingerprint is: " + DnsCryptContext::getProviderFingerprint(publicKey) + "\n";
538 }
539 catch(std::exception& e) {
540 errlog(e.what());
541 g_outputBuffer="Error: "+string(e.what())+"\n";
542 }
543
544 sodium_memzero(privateKey, sizeof(privateKey));
545 sodium_munlock(privateKey, sizeof(privateKey));
546 #else
547 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
548 #endif
549 });
550
551 g_lua.writeFunction("printDNSCryptProviderFingerprint", [](const std::string& publicKeyFile) {
552 setLuaNoSideEffect();
553 #ifdef HAVE_DNSCRYPT
554 unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
555
556 try {
557 ifstream file(publicKeyFile);
558 file.read((char *) &publicKey, sizeof(publicKey));
559
560 if (file.fail())
561 throw std::runtime_error("Invalid dnscrypt provider public key file " + publicKeyFile);
562
563 file.close();
564 g_outputBuffer="Provider fingerprint is: " + DnsCryptContext::getProviderFingerprint(publicKey) + "\n";
565 }
566 catch(std::exception& e) {
567 errlog(e.what());
568 g_outputBuffer="Error: "+string(e.what())+"\n";
569 }
570 #else
571 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
572 #endif
573 });
574
575 g_lua.writeFunction("generateDNSCryptCertificate", [](const std::string& providerPrivateKeyFile, const std::string& certificateFile, const std::string privateKeyFile, uint32_t serial, time_t begin, time_t end) {
576 setLuaNoSideEffect();
577 #ifdef HAVE_DNSCRYPT
578 unsigned char providerPrivateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
579 sodium_mlock(providerPrivateKey, sizeof(providerPrivateKey));
580 sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey));
581
582 try {
583 DnsCryptPrivateKey privateKey;
584 DnsCryptCert cert;
585 ifstream providerKStream(providerPrivateKeyFile);
586 providerKStream.read((char*) providerPrivateKey, sizeof(providerPrivateKey));
587 if (providerKStream.fail()) {
588 providerKStream.close();
589 throw std::runtime_error("Invalid DNSCrypt provider key file " + providerPrivateKeyFile);
590 }
591
592 DnsCryptContext::generateCertificate(serial, begin, end, providerPrivateKey, privateKey, cert);
593
594 privateKey.saveToFile(privateKeyFile);
595 DnsCryptContext::saveCertFromFile(cert, certificateFile);
596 }
597 catch(std::exception& e) {
598 errlog(e.what());
599 g_outputBuffer="Error: "+string(e.what())+"\n";
600 }
601
602 sodium_memzero(providerPrivateKey, sizeof(providerPrivateKey));
603 sodium_munlock(providerPrivateKey, sizeof(providerPrivateKey));
604 #else
605 g_outputBuffer="Error: DNSCrypt support is not enabled.\n";
606 #endif
607 });
608
609 g_lua.writeFunction("showPools", []() {
610 setLuaNoSideEffect();
611 try {
612 ostringstream ret;
613 boost::format fmt("%1$-20.20s %|25t|%2$20s %|50t|%3%" );
614 // 1 3 4
615 ret << (fmt % "Name" % "Cache" % "Servers" ) << endl;
616
617 const auto localPools = g_pools.getCopy();
618 for (const auto& entry : localPools) {
619 const string& name = entry.first;
620 const std::shared_ptr<ServerPool> pool = entry.second;
621 string cache = pool->packetCache != nullptr ? pool->packetCache->toString() : "";
622 string servers;
623
624 for (const auto& server: pool->servers) {
625 if (!servers.empty()) {
626 servers += ", ";
627 }
628 if (!server.second->name.empty()) {
629 servers += server.second->name;
630 servers += " ";
631 }
632 servers += server.second->remote.toStringWithPort();
633 }
634
635 ret << (fmt % name % cache % servers) << endl;
636 }
637 g_outputBuffer=ret.str();
638 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
639 });
640
641 g_lua.registerFunction<void(std::shared_ptr<ServerPool>::*)(std::shared_ptr<DNSDistPacketCache>)>("setCache", [](std::shared_ptr<ServerPool> pool, std::shared_ptr<DNSDistPacketCache> cache) {
642 if (pool) {
643 pool->packetCache = cache;
644 }
645 });
646 g_lua.registerFunction("getCache", &ServerPool::getCache);
647 g_lua.registerFunction<void(std::shared_ptr<ServerPool>::*)()>("unsetCache", [](std::shared_ptr<ServerPool> pool) {
648 if (pool) {
649 pool->packetCache = nullptr;
650 }
651 });
652
653 g_lua.writeFunction("newPacketCache", [client](size_t maxEntries, boost::optional<uint32_t> maxTTL, boost::optional<uint32_t> minTTL, boost::optional<uint32_t> servFailTTL, boost::optional<uint32_t> staleTTL) {
654 return std::make_shared<DNSDistPacketCache>(maxEntries, maxTTL ? *maxTTL : 86400, minTTL ? *minTTL : 0, servFailTTL ? *servFailTTL : 60, staleTTL ? *staleTTL : 60);
655 });
656 g_lua.registerFunction("toString", &DNSDistPacketCache::toString);
657 g_lua.registerFunction("isFull", &DNSDistPacketCache::isFull);
658 g_lua.registerFunction("purgeExpired", &DNSDistPacketCache::purgeExpired);
659 g_lua.registerFunction("expunge", &DNSDistPacketCache::expunge);
660 g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)(const DNSName& dname, boost::optional<uint16_t> qtype)>("expungeByName", [](std::shared_ptr<DNSDistPacketCache> cache, const DNSName& dname, boost::optional<uint16_t> qtype) {
661 if (cache) {
662 cache->expungeByName(dname, qtype ? *qtype : QType::ANY);
663 }
664 });
665 g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)()>("printStats", [](const std::shared_ptr<DNSDistPacketCache> cache) {
666 if (cache) {
667 g_outputBuffer="Entries: " + std::to_string(cache->getEntriesCount()) + "/" + std::to_string(cache->getMaxEntries()) + "\n";
668 g_outputBuffer+="Hits: " + std::to_string(cache->getHits()) + "\n";
669 g_outputBuffer+="Misses: " + std::to_string(cache->getMisses()) + "\n";
670 g_outputBuffer+="Deferred inserts: " + std::to_string(cache->getDeferredInserts()) + "\n";
671 g_outputBuffer+="Deferred lookups: " + std::to_string(cache->getDeferredLookups()) + "\n";
672 g_outputBuffer+="Lookup Collisions: " + std::to_string(cache->getLookupCollisions()) + "\n";
673 g_outputBuffer+="Insert Collisions: " + std::to_string(cache->getInsertCollisions()) + "\n";
674 g_outputBuffer+="TTL Too Shorts: " + std::to_string(cache->getTTLTooShorts()) + "\n";
675 }
676 });
677
678 g_lua.writeFunction("getPool", [client](const string& poolName) {
679 if (client) {
680 return std::make_shared<ServerPool>();
681 }
682 auto localPools = g_pools.getCopy();
683 std::shared_ptr<ServerPool> pool = createPoolIfNotExists(localPools, poolName);
684 g_pools.setState(localPools);
685 return pool;
686 });
687
688 g_lua.writeFunction("setVerboseHealthChecks", [](bool verbose) { g_verboseHealthChecks=verbose; });
689 g_lua.writeFunction("setStaleCacheEntriesTTL", [](uint32_t ttl) { g_staleCacheEntriesTTL = ttl; });
690
691 g_lua.writeFunction("RemoteLogAction", [](std::shared_ptr<RemoteLogger> logger) {
692 #ifdef HAVE_PROTOBUF
693 return std::shared_ptr<DNSAction>(new RemoteLogAction(logger));
694 #else
695 throw std::runtime_error("Protobuf support is required to use RemoteLogAction");
696 #endif
697 });
698 g_lua.writeFunction("RemoteLogResponseAction", [](std::shared_ptr<RemoteLogger> logger) {
699 #ifdef HAVE_PROTOBUF
700 return std::shared_ptr<DNSResponseAction>(new RemoteLogResponseAction(logger));
701 #else
702 throw std::runtime_error("Protobuf support is required to use RemoteLogResponseAction");
703 #endif
704 });
705 g_lua.writeFunction("newRemoteLogger", [client](const std::string& remote, boost::optional<uint16_t> timeout, boost::optional<uint64_t> maxQueuedEntries, boost::optional<uint8_t> reconnectWaitTime) {
706 return std::make_shared<RemoteLogger>(ComboAddress(remote), timeout ? *timeout : 2, maxQueuedEntries ? *maxQueuedEntries : 100, reconnectWaitTime ? *reconnectWaitTime : 1);
707 });
708
709 g_lua.writeFunction("TeeAction", [](const std::string& remote, boost::optional<bool> addECS) {
710 return std::shared_ptr<DNSAction>(new TeeAction(ComboAddress(remote, 53), addECS ? *addECS : false));
711 });
712
713 g_lua.registerFunction<void(DNSAction::*)()>("printStats", [](const DNSAction& ta) {
714 setLuaNoSideEffect();
715 auto stats = ta.getStats();
716 for(const auto& s : stats) {
717 g_outputBuffer+=s.first+"\t";
718 if((uint64_t)s.second == s.second)
719 g_outputBuffer += std::to_string((uint64_t)s.second)+"\n";
720 else
721 g_outputBuffer += std::to_string(s.second)+"\n";
722 }
723 });
724
725 g_lua.writeFunction("getAction", [](unsigned int num) {
726 setLuaNoSideEffect();
727 boost::optional<std::shared_ptr<DNSAction>> ret;
728 auto rulactions = g_rulactions.getCopy();
729 if(num < rulactions.size())
730 ret=rulactions[num].second;
731 return ret;
732 });
733
734 g_lua.registerFunction("getStats", &DNSAction::getStats);
735
736 g_lua.writeFunction("showResponseRules", []() {
737 setLuaNoSideEffect();
738 boost::format fmt("%-3d %9d %-50s %s\n");
739 g_outputBuffer += (fmt % "#" % "Matches" % "Rule" % "Action").str();
740 int num=0;
741 for(const auto& lim : g_resprulactions.getCopy()) {
742 string name = lim.first->toString();
743 g_outputBuffer += (fmt % num % lim.first->d_matches % name % lim.second->toString()).str();
744 ++num;
745 }
746 });
747
748 g_lua.writeFunction("rmResponseRule", [](unsigned int num) {
749 setLuaSideEffect();
750 auto rules = g_resprulactions.getCopy();
751 if(num >= rules.size()) {
752 g_outputBuffer = "Error: attempt to delete non-existing rule\n";
753 return;
754 }
755 rules.erase(rules.begin()+num);
756 g_resprulactions.setState(rules);
757 });
758
759 g_lua.writeFunction("topResponseRule", []() {
760 setLuaSideEffect();
761 auto rules = g_resprulactions.getCopy();
762 if(rules.empty())
763 return;
764 auto subject = *rules.rbegin();
765 rules.erase(std::prev(rules.end()));
766 rules.insert(rules.begin(), subject);
767 g_resprulactions.setState(rules);
768 });
769
770 g_lua.writeFunction("mvResponseRule", [](unsigned int from, unsigned int to) {
771 setLuaSideEffect();
772 auto rules = g_resprulactions.getCopy();
773 if(from >= rules.size() || to > rules.size()) {
774 g_outputBuffer = "Error: attempt to move rules from/to invalid index\n";
775 return;
776 }
777 auto subject = rules[from];
778 rules.erase(rules.begin()+from);
779 if(to == rules.size())
780 rules.push_back(subject);
781 else {
782 if(from < to)
783 --to;
784 rules.insert(rules.begin()+to, subject);
785 }
786 g_resprulactions.setState(rules);
787 });
788
789 g_lua.writeFunction("showBinds", []() {
790 setLuaNoSideEffect();
791 try {
792 ostringstream ret;
793 boost::format fmt("%1$-3d %2$-20.20s %|25t|%3$-8.8s %|35t|%4%" );
794 // 1 2 3 4
795 ret << (fmt % "#" % "Address" % "Protocol" % "Queries" ) << endl;
796
797 size_t counter = 0;
798 for (const auto& front : g_frontends) {
799 ret << (fmt % counter % front->local.toStringWithPort() % (front->udpFD != -1 ? "UDP" : "TCP") % front->queries) << endl;
800 counter++;
801 }
802 g_outputBuffer=ret.str();
803 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
804 });
805
806 g_lua.writeFunction("getBind", [](size_t num) {
807 setLuaNoSideEffect();
808 ClientState* ret = nullptr;
809 if(num < g_frontends.size()) {
810 ret=g_frontends[num];
811 }
812 return ret;
813 });
814
815 g_lua.registerFunction<std::string(ClientState::*)()>("toString", [](const ClientState& fe) {
816 setLuaNoSideEffect();
817 return fe.local.toStringWithPort();
818 });
819
820 g_lua.writeFunction("help", [](boost::optional<std::string> command) {
821 setLuaNoSideEffect();
822 g_outputBuffer = "";
823 for (const auto& keyword : g_consoleKeywords) {
824 if (!command) {
825 g_outputBuffer += keyword.toString() + "\n";
826 }
827 else if (keyword.name == command) {
828 g_outputBuffer = keyword.toString() + "\n";
829 return;
830 }
831 }
832 if (command) {
833 g_outputBuffer = "Nothing found for " + *command + "\n";
834 }
835 });
836
837 g_lua.writeFunction("showVersion", []() {
838 setLuaNoSideEffect();
839 g_outputBuffer = "dnsdist " + std::string(VERSION) + "\n";
840 });
841
842 #ifdef HAVE_EBPF
843 g_lua.writeFunction("newBPFFilter", [](uint32_t maxV4, uint32_t maxV6, uint32_t maxQNames) {
844 return std::make_shared<BPFFilter>(maxV4, maxV6, maxQNames);
845 });
846
847 g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const ComboAddress& ca)>("block", [](std::shared_ptr<BPFFilter> bpf, const ComboAddress& ca) {
848 if (bpf) {
849 return bpf->block(ca);
850 }
851 });
852
853 g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const DNSName& qname, boost::optional<uint16_t> qtype)>("blockQName", [](std::shared_ptr<BPFFilter> bpf, const DNSName& qname, boost::optional<uint16_t> qtype) {
854 if (bpf) {
855 return bpf->block(qname, qtype ? *qtype : 255);
856 }
857 });
858
859 g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const ComboAddress& ca)>("unblock", [](std::shared_ptr<BPFFilter> bpf, const ComboAddress& ca) {
860 if (bpf) {
861 return bpf->unblock(ca);
862 }
863 });
864
865 g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const DNSName& qname, boost::optional<uint16_t> qtype)>("unblockQName", [](std::shared_ptr<BPFFilter> bpf, const DNSName& qname, boost::optional<uint16_t> qtype) {
866 if (bpf) {
867 return bpf->unblock(qname, qtype ? *qtype : 255);
868 }
869 });
870
871 g_lua.registerFunction<std::string(std::shared_ptr<BPFFilter>::*)()>("getStats", [](const std::shared_ptr<BPFFilter> bpf) {
872 setLuaNoSideEffect();
873 std::string res;
874 if (bpf) {
875 std::vector<std::pair<ComboAddress, uint64_t> > stats = bpf->getAddrStats();
876 for (const auto& value : stats) {
877 if (value.first.sin4.sin_family == AF_INET) {
878 res += value.first.toString() + ": " + std::to_string(value.second) + "\n";
879 }
880 else if (value.first.sin4.sin_family == AF_INET6) {
881 res += "[" + value.first.toString() + "]: " + std::to_string(value.second) + "\n";
882 }
883 }
884 std::vector<std::tuple<DNSName, uint16_t, uint64_t> > qstats = bpf->getQNameStats();
885 for (const auto& value : qstats) {
886 res += std::get<0>(value).toString() + " " + std::to_string(std::get<1>(value)) + ": " + std::to_string(std::get<2>(value)) + "\n";
887 }
888 }
889 return res;
890 });
891
892 g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)()>("attachToAllBinds", [](std::shared_ptr<BPFFilter> bpf) {
893 std::string res;
894 if (bpf) {
895 for (const auto& front : g_frontends) {
896 bpf->addSocket(front->udpFD != -1 ? front->udpFD : front->tcpFD);
897 }
898 }
899 });
900
901 g_lua.registerFunction<void(ClientState::*)(std::shared_ptr<BPFFilter>)>("attachFilter", [](ClientState& frontend, std::shared_ptr<BPFFilter> bpf) {
902 if (bpf) {
903 bpf->addSocket(frontend.udpFD != -1 ? frontend.udpFD : frontend.tcpFD);
904 }
905 });
906
907 g_lua.writeFunction("setDefaultBPFFilter", [](std::shared_ptr<BPFFilter> bpf) {
908 if (g_configurationDone) {
909 g_outputBuffer="setDefaultBPFFilter() cannot be used at runtime!\n";
910 return;
911 }
912 g_defaultBPFFilter = bpf;
913 });
914
915 g_lua.writeFunction("newDynBPFFilter", [](std::shared_ptr<BPFFilter> bpf) {
916 return std::make_shared<DynBPFFilter>(bpf);
917 });
918
919 g_lua.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)(const ComboAddress& addr, boost::optional<int> seconds)>("block", [](std::shared_ptr<DynBPFFilter> dbpf, const ComboAddress& addr, boost::optional<int> seconds) {
920 if (dbpf) {
921 struct timespec until;
922 clock_gettime(CLOCK_MONOTONIC, &until);
923 until.tv_sec += seconds ? *seconds : 10;
924 dbpf->block(addr, until);
925 }
926 });
927
928 g_lua.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)()>("purgeExpired", [](std::shared_ptr<DynBPFFilter> dbpf) {
929 if (dbpf) {
930 struct timespec now;
931 clock_gettime(CLOCK_MONOTONIC, &now);
932 dbpf->purgeExpired(now);
933 }
934 });
935
936 g_lua.writeFunction("addBPFFilterDynBlocks", [](const map<ComboAddress,int>& m, std::shared_ptr<DynBPFFilter> dynbpf, boost::optional<int> seconds) {
937 setLuaSideEffect();
938 struct timespec until, now;
939 clock_gettime(CLOCK_MONOTONIC, &now);
940 until=now;
941 int actualSeconds = seconds ? *seconds : 10;
942 until.tv_sec += actualSeconds;
943 for(const auto& capair : m) {
944 dynbpf->block(capair.first, until);
945 }
946 });
947
948 #endif /* HAVE_EBPF */
949 }