]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsdist-lua.cc
split up dnsdist.cc, it was getting large
[thirdparty/pdns.git] / pdns / dnsdist-lua.cc
CommitLineData
df111b53 1#include "dnsdist.hh"
2#include <thread>
3#include "dolog.hh"
4#include "sodcrypto.hh"
5#include "base64.hh"
6#include <fstream>
7
8using std::thread;
9
10void setupLua(bool client)
11{
12 g_lua.writeFunction("newServer",
13 [](boost::variant<string,std::unordered_map<std::string, std::string>> pvars, boost::optional<int> qps)
14 {
15 if(auto address = boost::get<string>(&pvars)) {
16 auto ret=std::make_shared<DownstreamState>(ComboAddress(*address, 53));
17 ret->tid = move(thread(responderThread, ret));
18 if(qps) {
19 ret->qps=QPSLimiter(*qps, *qps);
20 }
21 g_dstates.push_back(ret);
22 return ret;
23 }
24 auto vars=boost::get<std::unordered_map<std::string, std::string>>(pvars);
25 auto ret=std::make_shared<DownstreamState>(ComboAddress(vars["address"], 53));
26
27 ret->tid = move(thread(responderThread, ret));
28
29 if(vars.count("qps")) {
30 ret->qps=QPSLimiter(boost::lexical_cast<int>(vars["qps"]),boost::lexical_cast<int>(vars["qps"]));
31 }
32
33 if(vars.count("pool")) {
34 ret->pools.insert(vars["pool"]);
35 }
36
37 if(vars.count("order")) {
38 ret->order=boost::lexical_cast<int>(vars["order"]);
39 }
40
41 if(vars.count("weight")) {
42 ret->weight=boost::lexical_cast<int>(vars["weight"]);
43 }
44
45
46 g_dstates.push_back(ret);
47 std::stable_sort(g_dstates.begin(), g_dstates.end(), [](const decltype(ret)& a, const decltype(ret)& b) {
48 return a->order < b->order;
49 });
50 return ret;
51 } );
52
53
54
55
56 g_lua.writeFunction("rmServer",
57 [](boost::variant<std::shared_ptr<DownstreamState>, int> var)
58 {
59 if(auto* rem = boost::get<shared_ptr<DownstreamState>>(&var))
60 g_dstates.erase(remove(g_dstates.begin(), g_dstates.end(), *rem), g_dstates.end());
61 else
62 g_dstates.erase(g_dstates.begin() + boost::get<int>(var));
63 } );
64
65
66 g_lua.writeFunction("setServerPolicy", [](ServerPolicy policy) {
67 g_policy=policy;
68 });
69
70 g_lua.writeFunction("setServerPolicyLua", [](string name, policy_t policy) {
71 g_policy=ServerPolicy{name, policy};
72 });
73
74 g_lua.writeFunction("showServerPolicy", []() {
75 g_outputBuffer=g_policy.name+"\n";
76 });
77
78
79 g_lua.registerMember("name", &ServerPolicy::name);
80 g_lua.registerMember("policy", &ServerPolicy::policy);
81 g_lua.writeFunction("newServerPolicy", [](string name, policy_t policy) { return ServerPolicy{name, policy};});
82 g_lua.writeVariable("firstAvailable", ServerPolicy{"firstAvailable", firstAvailable});
83 g_lua.writeVariable("roundrobin", ServerPolicy{"roundrobin", roundrobin});
84 g_lua.writeVariable("wrandom", ServerPolicy{"wrandom", wrandom});
85 g_lua.writeVariable("leastOutstanding", ServerPolicy{"leastOutstanding", leastOutstanding});
86 g_lua.writeFunction("addACL", [](const std::string& domain) {
87 g_ACL.addMask(domain);
88 });
89 g_lua.writeFunction("setACL", [](const vector<pair<int, string>>& parts) {
90 NetmaskGroup nmg;
91 for(const auto& p : parts) {
92 nmg.addMask(p.second);
93 }
94 g_ACL=nmg;
95 });
96 g_lua.writeFunction("showACL", []() {
97 vector<string> vec;
98 g_ACL.toStringVector(&vec);
99 string ret;
100 for(const auto& s : vec)
101 ret+=s+"\n";
102 return ret;
103 });
104 g_lua.writeFunction("shutdown", []() { _exit(0);} );
105
106
107 g_lua.writeFunction("addDomainBlock", [](const std::string& domain) { g_suffixMatchNodeFilter.add(DNSName(domain)); });
108 g_lua.writeFunction("showServers", []() {
109 try {
110 ostringstream ret;
111
112 boost::format fmt("%1$-3d %2% %|30t|%3$5s %|36t|%4$7.1f %|41t|%5$7d %|44t|%6$3d %|53t|%7$2d %|55t|%8$10d %|61t|%9$7d %|76t|%10$5.1f %|84t|%11$5.1f %12%" );
113 // 1 2 3 4 5 6 7 8 9 10 11
114 ret << (fmt % "#" % "Address" % "State" % "Qps" % "Qlim" % "Ord" % "Wt" % "Queries" % "Drops" % "Drate" % "Lat" % "Pools") << endl;
115
116 uint64_t totQPS{0}, totQueries{0}, totDrops{0};
117 int counter=0;
118 for(auto& s : g_dstates) {
119 string status;
120 if(s->availability == DownstreamState::Availability::Up)
121 status = "UP";
122 else if(s->availability == DownstreamState::Availability::Down)
123 status = "DOWN";
124 else
125 status = (s->upStatus ? "up" : "down");
126
127 string pools;
128 for(auto& p : s->pools) {
129 if(!pools.empty())
130 pools+=" ";
131 pools+=p;
132 }
133
134 ret << (fmt % counter % s->remote.toStringWithPort() %
135 status %
136 s->queryLoad % s->qps.getRate() % s->order % s->weight % s->queries.load() % s->reuseds.load() % (s->dropRate) % (s->latencyUsec/1000.0) % pools) << endl;
137
138 totQPS += s->queryLoad;
139 totQueries += s->queries.load();
140 totDrops += s->reuseds.load();
141 ++counter;
142 }
143 ret<< (fmt % "All" % "" % ""
144 %
145 (double)totQPS % "" % "" % "" % totQueries % totDrops % "" % "" % "" ) << endl;
146
147 g_outputBuffer=ret.str();
148 }catch(std::exception& e) { g_outputBuffer=e.what(); throw; }
149 });
150
151 g_lua.writeFunction("addPoolRule", [](boost::variant<string,vector<pair<int, string>> > var, string pool) {
152 SuffixMatchNode smn;
153 NetmaskGroup nmg;
154
155 auto add=[&](string src) {
156 try {
157 smn.add(DNSName(src));
158 } catch(...) {
159 nmg.addMask(src);
160 }
161 };
162 if(auto src = boost::get<string>(&var))
163 add(*src);
164 else {
165 for(auto& a : boost::get<vector<pair<int, string>>>(var)) {
166 add(a.second);
167 }
168 }
169 if(nmg.empty())
170 g_poolrules.push_back({smn, pool});
171 else
172 g_poolrules.push_back({nmg, pool});
173
174 });
175
176 g_lua.writeFunction("showPoolRules", []() {
177 boost::format fmt("%-3d %-50s %s\n");
178 g_outputBuffer += (fmt % "#" % "Object" % "Pool").str();
179 int num=0;
180 for(const auto& lim : g_poolrules) {
181 string name;
182 if(auto nmg=boost::get<NetmaskGroup>(&lim.first)) {
183 name=nmg->toString();
184 }
185 else if(auto smn=boost::get<SuffixMatchNode>(&lim.first)) {
186 name=smn->toString();
187 }
188 g_outputBuffer += (fmt % num % name % lim.second).str();
189 ++num;
190 }
191 });
192
193
194 g_lua.writeFunction("addQPSLimit", [](boost::variant<string,vector<pair<int, string>> > var, int lim) {
195 SuffixMatchNode smn;
196 NetmaskGroup nmg;
197
198 auto add=[&](string src) {
199 try {
200 smn.add(DNSName(src));
201 } catch(...) {
202 nmg.addMask(src);
203 }
204 };
205 if(auto src = boost::get<string>(&var))
206 add(*src);
207 else {
208 for(auto& a : boost::get<vector<pair<int, string>>>(var)) {
209 add(a.second);
210 }
211 }
212 if(nmg.empty())
213 g_limiters.push_back({smn, QPSLimiter(lim, lim)});
214 else
215 g_limiters.push_back({nmg, QPSLimiter(lim, lim)});
216 });
217
218 g_lua.writeFunction("rmQPSLimit", [](int i) {
219 g_limiters.erase(g_limiters.begin() + i);
220 });
221
222 g_lua.writeFunction("showQPSLimits", []() {
223 boost::format fmt("%-3d %-50s %7d %8d %8d\n");
224 g_outputBuffer += (fmt % "#" % "Object" % "Lim" % "Passed" % "Blocked").str();
225 int num=0;
226 for(const auto& lim : g_limiters) {
227 string name;
228 if(auto nmg=boost::get<NetmaskGroup>(&lim.first)) {
229 name=nmg->toString();
230 }
231 else if(auto smn=boost::get<SuffixMatchNode>(&lim.first)) {
232 name=smn->toString();
233 }
234 g_outputBuffer += (fmt % num % name % lim.second.getRate() % lim.second.getPassed() % lim.second.getBlocked()).str();
235 ++num;
236 }
237 });
238
239
240 g_lua.writeFunction("getServers", []() {
241 vector<pair<int, std::shared_ptr<DownstreamState> > > ret;
242 int count=1;
243 for(auto& s : g_dstates) {
244 ret.push_back(make_pair(count++, s));
245 }
246 return ret;
247 });
248
249 g_lua.writeFunction("getServer", [](int i) { return g_dstates[i]; });
250
251 g_lua.registerFunction<bool(DownstreamState::*)()>("checkQPS", [](DownstreamState& s) { return s.qps.check(); });
252 g_lua.registerFunction<void(DownstreamState::*)(int)>("setQPS", [](DownstreamState& s, int lim) { s.qps = lim ? QPSLimiter(lim, lim) : QPSLimiter(); });
253 g_lua.registerFunction<void(DownstreamState::*)(string)>("addPool", [](DownstreamState& s, string pool) { s.pools.insert(pool);});
254 g_lua.registerFunction<void(DownstreamState::*)(string)>("rmPool", [](DownstreamState& s, string pool) { s.pools.erase(pool);});
255
256 g_lua.registerFunction<void(DownstreamState::*)()>("getOutstanding", [](const DownstreamState& s) { g_outputBuffer=std::to_string(s.outstanding.load()); });
257
258
259 g_lua.registerFunction("isUp", &DownstreamState::isUp);
260 g_lua.registerFunction("setDown", &DownstreamState::setDown);
261 g_lua.registerFunction("setUp", &DownstreamState::setUp);
262 g_lua.registerFunction("setAuto", &DownstreamState::setAuto);
263 g_lua.registerMember("upstatus", &DownstreamState::upStatus);
264 g_lua.registerMember("weight", &DownstreamState::weight);
265 g_lua.registerMember("order", &DownstreamState::order);
266
267 g_lua.writeFunction("show", [](const string& arg) {
268 g_outputBuffer+=arg;
269 g_outputBuffer+="\n";
270 });
271
272 g_lua.registerFunction<void(dnsheader::*)(bool)>("setRD", [](dnsheader& dh, bool v) {
273 dh.rd=v;
274 });
275
276 g_lua.registerFunction<bool(dnsheader::*)()>("getRD", [](dnsheader& dh) {
277 return (bool)dh.rd;
278 });
279
280
281 g_lua.registerFunction<void(dnsheader::*)(bool)>("setTC", [](dnsheader& dh, bool v) {
282 dh.tc=v;
283 });
284
285 g_lua.registerFunction<void(dnsheader::*)(bool)>("setQR", [](dnsheader& dh, bool v) {
286 dh.qr=v;
287 });
288
289 std::ifstream ifs(g_vm["config"].as<string>());
290 if(!ifs)
291 warnlog("Unable to read configuration from '%s'", g_vm["config"].as<string>());
292 else
293 infolog("Read configuration from '%s'", g_vm["config"].as<string>());
294
295 g_lua.registerFunction("tostring", &ComboAddress::toString);
296
297 g_lua.registerFunction("isPartOf", &DNSName::isPartOf);
298 g_lua.registerFunction("tostring", &DNSName::toString);
299 g_lua.writeFunction("newDNSName", [](const std::string& name) { return DNSName(name); });
300 g_lua.writeFunction("newSuffixNode", []() { return SuffixMatchNode(); });
301
302 g_lua.registerFunction("add",(void (SuffixMatchNode::*)(const DNSName&)) &SuffixMatchNode::add);
303 g_lua.registerFunction("check",(bool (SuffixMatchNode::*)(const DNSName&) const) &SuffixMatchNode::check);
304
305 g_lua.writeFunction("controlSocket", [client](const std::string& str) {
306 ComboAddress local(str, 5199);
307
308 if(client) {
309 g_serverControl = local;
310 return;
311 }
312
313 try {
314 int sock = socket(local.sin4.sin_family, SOCK_STREAM, 0);
315 SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
316 SBind(sock, local);
317 SListen(sock, 5);
318 thread t(controlThread, sock, local);
319 t.detach();
320 }
321 catch(std::exception& e) {
322 errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what());
323 }
324 });
325
326 g_lua.writeFunction("getTopQueries", [](unsigned int top, boost::optional<int> labels) {
327 map<DNSName, int> counts;
328 unsigned int total=0;
329 if(!labels) {
330 for(const auto& a : g_rings.queryRing) {
331 counts[a]++;
332 total++;
333 }
334 }
335 else {
336 unsigned int lab = *labels;
337 for(auto a : g_rings.queryRing) {
338 a.trimToLabels(lab);
339 counts[a]++;
340 total++;
341 }
342
343 }
344 cout<<"Looked at "<<total<<" queries, "<<counts.size()<<" different ones"<<endl;
345 vector<pair<int, DNSName>> rcounts;
346 for(const auto& c : counts)
347 rcounts.push_back(make_pair(c.second, c.first));
348
349 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
350 const decltype(rcounts)::value_type& b) {
351 return b.first < a.first;
352 });
353
354 std::unordered_map<int, vector<boost::variant<string,double>>> ret;
355 unsigned int count=1, rest=0;
356 for(const auto& rc : rcounts) {
357 if(count==top+1)
358 rest+=rc.first;
359 else
360 ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
361 }
362 ret.insert({count, {"Rest", rest, 100.0*rest/total}});
363 return ret;
364
365 });
366
367 g_lua.executeCode(R"(function topQueries(top, labels) for k,v in ipairs(getTopQueries(top,labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2], v[3])) end end)");
368
369 g_lua.writeFunction("getTopResponses", [](unsigned int top, unsigned int kind, boost::optional<int> labels) {
370 map<DNSName, int> counts;
371 unsigned int total=0;
372 {
373 std::lock_guard<std::mutex> lock(g_rings.respMutex);
374 if(!labels) {
375 for(const auto& a : g_rings.respRing) {
376 if(a.rcode!=kind)
377 continue;
378 counts[a.name]++;
379 total++;
380 }
381 }
382 else {
383 unsigned int lab = *labels;
384 for(auto a : g_rings.respRing) {
385 if(a.rcode!=kind)
386 continue;
387
388 a.name.trimToLabels(lab);
389 counts[a.name]++;
390 total++;
391 }
392
393 }
394 }
395 // cout<<"Looked at "<<total<<" responses, "<<counts.size()<<" different ones"<<endl;
396 vector<pair<int, DNSName>> rcounts;
397 for(const auto& c : counts)
398 rcounts.push_back(make_pair(c.second, c.first));
399
400 sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
401 const decltype(rcounts)::value_type& b) {
402 return b.first < a.first;
403 });
404
405 std::unordered_map<int, vector<boost::variant<string,double>>> ret;
406 unsigned int count=1, rest=0;
407 for(const auto& rc : rcounts) {
408 if(count==top+1)
409 rest+=rc.first;
410 else
411 ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
412 }
413 ret.insert({count, {"Rest", rest, 100.0*rest/total}});
414 return ret;
415
416 });
417
418 g_lua.executeCode(R"(function topResponses(top, kind, labels) for k,v in ipairs(getTopResponses(top, kind, labels)) do show(string.format("%4d %-40s %4d %4.1f%%",k,v[1],v[2], v[3])) end end)");
419
420
421 g_lua.writeFunction("showResponseLatency", []() {
422
423 map<double, unsigned int> histo;
424 double bin=100;
425 for(int i=0; i < 15; ++i) {
426 histo[bin];
427 bin*=2;
428 }
429
430 double totlat=0;
431 int size=0;
432 {
433 std::lock_guard<std::mutex> lock(g_rings.respMutex);
434 for(const auto& r : g_rings.respRing) {
435 ++size;
436 auto iter = histo.lower_bound(r.usec);
437 if(iter != histo.end())
438 iter->second++;
439 else
440 histo.rbegin()++;
441 totlat+=r.usec;
442 }
443 }
444
445 g_outputBuffer = (boost::format("Average response latency: %.02f msec\n") % (0.001*totlat/size)).str();
446 double highest=0;
447
448 for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
449 highest=std::max(highest, iter->second*1.0);
450 }
451 boost::format fmt("%7.2f\t%s\n");
452 g_outputBuffer += (fmt % "msec" % "").str();
453
454 for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
455 int stars = (70.0 * iter->second/highest);
456 char c='*';
457 if(!stars && iter->second) {
458 stars=1; // you get 1 . to show something is there..
459 if(70.0*iter->second/highest > 0.5)
460 c=':';
461 else
462 c='.';
463 }
464 g_outputBuffer += (fmt % (iter->first/1000.0) % string(stars, c)).str();
465 }
466 });
467
468 g_lua.writeFunction("newQPSLimiter", [](int rate, int burst) { return QPSLimiter(rate, burst); });
469 g_lua.registerFunction("check", &QPSLimiter::check);
470
471
472 g_lua.writeFunction("makeKey", []() {
473 g_outputBuffer="setKey("+newKey()+")\n";
474 });
475
476 g_lua.writeFunction("setKey", [](const std::string& key) {
477 if(B64Decode(key, g_key))
478 throw std::runtime_error("Unable to decode "+key+" as Base64");
479 });
480
481
482 g_lua.writeFunction("testCrypto", [](string testmsg)
483 {
484 try {
485 SodiumNonce sn, sn2;
486 sn.init();
487 sn2=sn;
488 string encrypted = sodEncryptSym(testmsg, g_key, sn);
489 string decrypted = sodDecryptSym(encrypted, g_key, sn2);
490
491 if(testmsg == decrypted)
492 g_outputBuffer="Everything is ok!\n";
493 else
494 g_outputBuffer="Crypto failed..\n";
495
496 }
497 catch(...) {
498 g_outputBuffer="Crypto failed..\n";
499 }});
500
501
502
503 g_lua.executeCode(ifs);
504}