]>
Commit | Line | Data |
---|---|---|
df111b53 | 1 | #include "dnsdist.hh" |
2 | #include <thread> | |
3 | #include "dolog.hh" | |
4 | #include "sodcrypto.hh" | |
5 | #include "base64.hh" | |
6 | #include <fstream> | |
7 | ||
8 | using std::thread; | |
9 | ||
10 | void 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 | } |