]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsdist-lua-bindings.cc
2cfb6f61e800cd58800e220f69271dbb6cdca220
[thirdparty/pdns.git] / pdns / dnsdist-lua-bindings.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 "config.h"
23 #include "dnsdist.hh"
24 #include "dnsdist-lua.hh"
25
26 #include "dolog.hh"
27
28 void setupLuaBindings(bool client)
29 {
30 g_lua.writeFunction("infolog", [](const string& arg) {
31 infolog("%s", arg);
32 });
33 g_lua.writeFunction("errlog", [](const string& arg) {
34 errlog("%s", arg);
35 });
36 g_lua.writeFunction("warnlog", [](const string& arg) {
37 warnlog("%s", arg);
38 });
39 g_lua.writeFunction("show", [](const string& arg) {
40 g_outputBuffer+=arg;
41 g_outputBuffer+="\n";
42 });
43
44 /* Exceptions */
45 g_lua.registerFunction<string(std::exception_ptr::*)()>("__tostring", [](const std::exception_ptr& eptr) {
46 try {
47 if (eptr) {
48 std::rethrow_exception(eptr);
49 }
50 } catch(const std::exception& e) {
51 return string(e.what());
52 } catch(const PDNSException& e) {
53 return e.reason;
54 } catch(...) {
55 return string("Unknown exception");
56 }
57 return string("No exception");
58 });
59 /* ServerPolicy */
60 g_lua.writeFunction("newServerPolicy", [](string name, policyfunc_t policy) { return ServerPolicy{name, policy, true};});
61 g_lua.registerMember("name", &ServerPolicy::name);
62 g_lua.registerMember("policy", &ServerPolicy::policy);
63 g_lua.registerMember("isLua", &ServerPolicy::isLua);
64 g_lua.registerFunction("toString", &ServerPolicy::toString);
65
66 g_lua.writeVariable("firstAvailable", ServerPolicy{"firstAvailable", firstAvailable, false});
67 g_lua.writeVariable("roundrobin", ServerPolicy{"roundrobin", roundrobin, false});
68 g_lua.writeVariable("wrandom", ServerPolicy{"wrandom", wrandom, false});
69 g_lua.writeVariable("whashed", ServerPolicy{"whashed", whashed, false});
70 g_lua.writeVariable("chashed", ServerPolicy{"chashed", chashed, false});
71 g_lua.writeVariable("leastOutstanding", ServerPolicy{"leastOutstanding", leastOutstanding, false});
72
73 /* ServerPool */
74 g_lua.registerFunction<void(std::shared_ptr<ServerPool>::*)(std::shared_ptr<DNSDistPacketCache>)>("setCache", [](std::shared_ptr<ServerPool> pool, std::shared_ptr<DNSDistPacketCache> cache) {
75 if (pool) {
76 pool->packetCache = cache;
77 }
78 });
79 g_lua.registerFunction("getCache", &ServerPool::getCache);
80 g_lua.registerFunction<void(std::shared_ptr<ServerPool>::*)()>("unsetCache", [](std::shared_ptr<ServerPool> pool) {
81 if (pool) {
82 pool->packetCache = nullptr;
83 }
84 });
85 g_lua.registerFunction("getECS", &ServerPool::getECS);
86 g_lua.registerFunction("setECS", &ServerPool::setECS);
87
88 /* DownstreamState */
89 g_lua.registerFunction<void(DownstreamState::*)(int)>("setQPS", [](DownstreamState& s, int lim) { s.qps = lim ? QPSLimiter(lim, lim) : QPSLimiter(); });
90 g_lua.registerFunction<void(std::shared_ptr<DownstreamState>::*)(string)>("addPool", [](std::shared_ptr<DownstreamState> s, string pool) {
91 auto localPools = g_pools.getCopy();
92 addServerToPool(localPools, pool, s);
93 g_pools.setState(localPools);
94 s->pools.insert(pool);
95 });
96 g_lua.registerFunction<void(std::shared_ptr<DownstreamState>::*)(string)>("rmPool", [](std::shared_ptr<DownstreamState> s, string pool) {
97 auto localPools = g_pools.getCopy();
98 removeServerFromPool(localPools, pool, s);
99 g_pools.setState(localPools);
100 s->pools.erase(pool);
101 });
102 g_lua.registerFunction<uint64_t(DownstreamState::*)()>("getOutstanding", [](const DownstreamState& s) { return s.outstanding.load(); });
103 g_lua.registerFunction("isUp", &DownstreamState::isUp);
104 g_lua.registerFunction("setDown", &DownstreamState::setDown);
105 g_lua.registerFunction("setUp", &DownstreamState::setUp);
106 g_lua.registerFunction<void(DownstreamState::*)(boost::optional<bool> newStatus)>("setAuto", [](DownstreamState& s, boost::optional<bool> newStatus) {
107 if (newStatus) {
108 s.upStatus = *newStatus;
109 }
110 s.setAuto();
111 });
112 g_lua.registerFunction("getName", &DownstreamState::getName);
113 g_lua.registerFunction("getNameWithAddr", &DownstreamState::getNameWithAddr);
114 g_lua.registerMember("upStatus", &DownstreamState::upStatus);
115 g_lua.registerMember<int (DownstreamState::*)>("weight",
116 [](const DownstreamState& s) -> int {return s.weight;},
117 [](DownstreamState& s, int newWeight) {s.setWeight(newWeight);}
118 );
119 g_lua.registerMember("order", &DownstreamState::order);
120 g_lua.registerMember("name", &DownstreamState::name);
121 g_lua.registerFunction<std::string(DownstreamState::*)()>("getID", [](const DownstreamState& s) { return boost::uuids::to_string(s.id); });
122
123 /* dnsheader */
124 g_lua.registerFunction<void(dnsheader::*)(bool)>("setRD", [](dnsheader& dh, bool v) {
125 dh.rd=v;
126 });
127
128 g_lua.registerFunction<bool(dnsheader::*)()>("getRD", [](dnsheader& dh) {
129 return (bool)dh.rd;
130 });
131
132 g_lua.registerFunction<void(dnsheader::*)(bool)>("setRA", [](dnsheader& dh, bool v) {
133 dh.ra=v;
134 });
135
136 g_lua.registerFunction<bool(dnsheader::*)()>("getRA", [](dnsheader& dh) {
137 return (bool)dh.ra;
138 });
139
140 g_lua.registerFunction<void(dnsheader::*)(bool)>("setAD", [](dnsheader& dh, bool v) {
141 dh.ad=v;
142 });
143
144 g_lua.registerFunction<bool(dnsheader::*)()>("getAD", [](dnsheader& dh) {
145 return (bool)dh.ad;
146 });
147
148 g_lua.registerFunction<void(dnsheader::*)(bool)>("setAA", [](dnsheader& dh, bool v) {
149 dh.aa=v;
150 });
151
152 g_lua.registerFunction<bool(dnsheader::*)()>("getAA", [](dnsheader& dh) {
153 return (bool)dh.aa;
154 });
155
156 g_lua.registerFunction<void(dnsheader::*)(bool)>("setCD", [](dnsheader& dh, bool v) {
157 dh.cd=v;
158 });
159
160 g_lua.registerFunction<bool(dnsheader::*)()>("getCD", [](dnsheader& dh) {
161 return (bool)dh.cd;
162 });
163
164 g_lua.registerFunction<void(dnsheader::*)(bool)>("setTC", [](dnsheader& dh, bool v) {
165 dh.tc=v;
166 if(v) dh.ra = dh.rd; // you'll always need this, otherwise TC=1 gets ignored
167 });
168
169 g_lua.registerFunction<void(dnsheader::*)(bool)>("setQR", [](dnsheader& dh, bool v) {
170 dh.qr=v;
171 });
172
173 /* ComboAddress */
174 g_lua.writeFunction("newCA", [](const std::string& name) { return ComboAddress(name); });
175 g_lua.registerFunction<string(ComboAddress::*)()>("tostring", [](const ComboAddress& ca) { return ca.toString(); });
176 g_lua.registerFunction<string(ComboAddress::*)()>("tostringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); });
177 g_lua.registerFunction<string(ComboAddress::*)()>("toString", [](const ComboAddress& ca) { return ca.toString(); });
178 g_lua.registerFunction<string(ComboAddress::*)()>("toStringWithPort", [](const ComboAddress& ca) { return ca.toStringWithPort(); });
179 g_lua.registerFunction<uint16_t(ComboAddress::*)()>("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); } );
180 g_lua.registerFunction<void(ComboAddress::*)(unsigned int)>("truncate", [](ComboAddress& ca, unsigned int bits) { ca.truncate(bits); });
181 g_lua.registerFunction<bool(ComboAddress::*)()>("isIPv4", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET; });
182 g_lua.registerFunction<bool(ComboAddress::*)()>("isIPv6", [](const ComboAddress& ca) { return ca.sin4.sin_family == AF_INET6; });
183 g_lua.registerFunction<bool(ComboAddress::*)()>("isMappedIPv4", [](const ComboAddress& ca) { return ca.isMappedIPv4(); });
184 g_lua.registerFunction<ComboAddress(ComboAddress::*)()>("mapToIPv4", [](const ComboAddress& ca) { return ca.mapToIPv4(); });
185 g_lua.registerFunction<bool(nmts_t::*)(const ComboAddress&)>("match", [](nmts_t& s, const ComboAddress& ca) { return s.match(ca); });
186
187 /* DNSName */
188 g_lua.registerFunction("isPartOf", &DNSName::isPartOf);
189 g_lua.registerFunction<bool(DNSName::*)()>("chopOff", [](DNSName&dn ) { return dn.chopOff(); });
190 g_lua.registerFunction<unsigned int(DNSName::*)()>("countLabels", [](const DNSName& name) { return name.countLabels(); });
191 g_lua.registerFunction<size_t(DNSName::*)()>("wirelength", [](const DNSName& name) { return name.wirelength(); });
192 g_lua.registerFunction<string(DNSName::*)()>("tostring", [](const DNSName&dn ) { return dn.toString(); });
193 g_lua.registerFunction<string(DNSName::*)()>("toString", [](const DNSName&dn ) { return dn.toString(); });
194 g_lua.writeFunction("newDNSName", [](const std::string& name) { return DNSName(name); });
195 g_lua.writeFunction("newSuffixMatchNode", []() { return SuffixMatchNode(); });
196 g_lua.writeFunction("newDNSNameSet", []() { return DNSNameSet(); });
197
198 /* DNSNameSet */
199 g_lua.registerFunction<string(DNSNameSet::*)()>("toString", [](const DNSNameSet&dns ) { return dns.toString(); });
200 g_lua.registerFunction<void(DNSNameSet::*)(DNSName&)>("add", [](DNSNameSet& dns, DNSName& dn) { dns.insert(dn); });
201 g_lua.registerFunction<bool(DNSNameSet::*)(DNSName&)>("check", [](DNSNameSet& dns, DNSName& dn) { return dns.find(dn) != dns.end(); });
202 g_lua.registerFunction("delete",(size_t (DNSNameSet::*)(const DNSName&)) &DNSNameSet::erase);
203 g_lua.registerFunction("size",(size_t (DNSNameSet::*)() const) &DNSNameSet::size);
204 g_lua.registerFunction("clear",(void (DNSNameSet::*)()) &DNSNameSet::clear);
205 g_lua.registerFunction("empty",(bool (DNSNameSet::*)()) &DNSNameSet::empty);
206
207 /* SuffixMatchNode */
208 g_lua.registerFunction<void (SuffixMatchNode::*)(const boost::variant<DNSName, string, vector<pair<int, DNSName>>, vector<pair<int, string>>> &name)>("add", [](SuffixMatchNode &smn, const boost::variant<DNSName, string, vector<pair<int, DNSName>>, vector<pair<int, string>>> &name) {
209 if (name.type() == typeid(DNSName)) {
210 auto n = boost::get<DNSName>(name);
211 smn.add(n);
212 return;
213 }
214 if (name.type() == typeid(string)) {
215 auto n = boost::get<string>(name);
216 smn.add(n);
217 return;
218 }
219 if (name.type() == typeid(vector<pair<int, DNSName>>)) {
220 auto names = boost::get<vector<pair<int, DNSName>>>(name);
221 for (auto const n : names) {
222 smn.add(n.second);
223 }
224 return;
225 }
226 if (name.type() == typeid(vector<pair<int, string>>)) {
227 auto names = boost::get<vector<pair<int, string>>>(name);
228 for (auto const n : names) {
229 smn.add(n.second);
230 }
231 return;
232 }
233 });
234 g_lua.registerFunction("check",(bool (SuffixMatchNode::*)(const DNSName&) const) &SuffixMatchNode::check);
235
236 /* NetmaskGroup */
237 g_lua.writeFunction("newNMG", []() { return NetmaskGroup(); });
238 g_lua.registerFunction<void(NetmaskGroup::*)(const std::string&mask)>("addMask", [](NetmaskGroup&nmg, const std::string& mask)
239 {
240 nmg.addMask(mask);
241 });
242 g_lua.registerFunction<void(NetmaskGroup::*)(const std::map<ComboAddress,int>& map)>("addMasks", [](NetmaskGroup&nmg, const std::map<ComboAddress,int>& map)
243 {
244 for (const auto& entry : map) {
245 nmg.addMask(Netmask(entry.first));
246 }
247 });
248
249 g_lua.registerFunction("match", (bool (NetmaskGroup::*)(const ComboAddress&) const)&NetmaskGroup::match);
250 g_lua.registerFunction("size", &NetmaskGroup::size);
251 g_lua.registerFunction("clear", &NetmaskGroup::clear);
252 g_lua.registerFunction<string(NetmaskGroup::*)()>("toString", [](const NetmaskGroup& nmg ) { return "NetmaskGroup " + nmg.toString(); });
253
254 /* QPSLimiter */
255 g_lua.writeFunction("newQPSLimiter", [](int rate, int burst) { return QPSLimiter(rate, burst); });
256 g_lua.registerFunction("check", &QPSLimiter::check);
257
258 /* ClientState */
259 g_lua.registerFunction<std::string(ClientState::*)()>("toString", [](const ClientState& fe) {
260 setLuaNoSideEffect();
261 return fe.local.toStringWithPort();
262 });
263 g_lua.registerMember("muted", &ClientState::muted);
264 #ifdef HAVE_EBPF
265 g_lua.registerFunction<void(ClientState::*)(std::shared_ptr<BPFFilter>)>("attachFilter", [](ClientState& frontend, std::shared_ptr<BPFFilter> bpf) {
266 if (bpf) {
267 frontend.attachFilter(bpf);
268 }
269 });
270 g_lua.registerFunction<void(ClientState::*)()>("detachFilter", [](ClientState& frontend) {
271 frontend.detachFilter();
272 });
273 #endif /* HAVE_EBPF */
274
275 /* BPF Filter */
276 #ifdef HAVE_EBPF
277 g_lua.writeFunction("newBPFFilter", [client](uint32_t maxV4, uint32_t maxV6, uint32_t maxQNames) {
278 if (client) {
279 return std::shared_ptr<BPFFilter>(nullptr);
280 }
281 return std::make_shared<BPFFilter>(maxV4, maxV6, maxQNames);
282 });
283
284 g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const ComboAddress& ca)>("block", [](std::shared_ptr<BPFFilter> bpf, const ComboAddress& ca) {
285 if (bpf) {
286 return bpf->block(ca);
287 }
288 });
289
290 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) {
291 if (bpf) {
292 return bpf->block(qname, qtype ? *qtype : 255);
293 }
294 });
295
296 g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)(const ComboAddress& ca)>("unblock", [](std::shared_ptr<BPFFilter> bpf, const ComboAddress& ca) {
297 if (bpf) {
298 return bpf->unblock(ca);
299 }
300 });
301
302 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) {
303 if (bpf) {
304 return bpf->unblock(qname, qtype ? *qtype : 255);
305 }
306 });
307
308 g_lua.registerFunction<std::string(std::shared_ptr<BPFFilter>::*)()>("getStats", [](const std::shared_ptr<BPFFilter> bpf) {
309 setLuaNoSideEffect();
310 std::string res;
311 if (bpf) {
312 std::vector<std::pair<ComboAddress, uint64_t> > stats = bpf->getAddrStats();
313 for (const auto& value : stats) {
314 if (value.first.sin4.sin_family == AF_INET) {
315 res += value.first.toString() + ": " + std::to_string(value.second) + "\n";
316 }
317 else if (value.first.sin4.sin_family == AF_INET6) {
318 res += "[" + value.first.toString() + "]: " + std::to_string(value.second) + "\n";
319 }
320 }
321 std::vector<std::tuple<DNSName, uint16_t, uint64_t> > qstats = bpf->getQNameStats();
322 for (const auto& value : qstats) {
323 res += std::get<0>(value).toString() + " " + std::to_string(std::get<1>(value)) + ": " + std::to_string(std::get<2>(value)) + "\n";
324 }
325 }
326 return res;
327 });
328
329 g_lua.registerFunction<void(std::shared_ptr<BPFFilter>::*)()>("attachToAllBinds", [](std::shared_ptr<BPFFilter> bpf) {
330 std::string res;
331 if (bpf) {
332 for (const auto& frontend : g_frontends) {
333 frontend->attachFilter(bpf);
334 }
335 }
336 });
337
338 g_lua.writeFunction("newDynBPFFilter", [client](std::shared_ptr<BPFFilter> bpf) {
339 if (client) {
340 return std::shared_ptr<DynBPFFilter>(nullptr);
341 }
342 return std::make_shared<DynBPFFilter>(bpf);
343 });
344
345 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) {
346 if (dbpf) {
347 struct timespec until;
348 clock_gettime(CLOCK_MONOTONIC, &until);
349 until.tv_sec += seconds ? *seconds : 10;
350 dbpf->block(addr, until);
351 }
352 });
353
354 g_lua.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)()>("purgeExpired", [](std::shared_ptr<DynBPFFilter> dbpf) {
355 if (dbpf) {
356 struct timespec now;
357 clock_gettime(CLOCK_MONOTONIC, &now);
358 dbpf->purgeExpired(now);
359 }
360 });
361
362 g_lua.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("excludeRange", [](std::shared_ptr<DynBPFFilter> dbpf, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) {
363 if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) {
364 for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) {
365 dbpf->excludeRange(Netmask(range.second));
366 }
367 }
368 else {
369 dbpf->excludeRange(Netmask(*boost::get<std::string>(&ranges)));
370 }
371 });
372
373 g_lua.registerFunction<void(std::shared_ptr<DynBPFFilter>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>>)>("includeRange", [](std::shared_ptr<DynBPFFilter> dbpf, boost::variant<std::string, std::vector<std::pair<int, std::string>>> ranges) {
374 if (ranges.type() == typeid(std::vector<std::pair<int, std::string>>)) {
375 for (const auto& range : *boost::get<std::vector<std::pair<int, std::string>>>(&ranges)) {
376 dbpf->includeRange(Netmask(range.second));
377 }
378 }
379 else {
380 dbpf->includeRange(Netmask(*boost::get<std::string>(&ranges)));
381 }
382 });
383 #endif /* HAVE_EBPF */
384
385 /* EDNSOptionView */
386 g_lua.registerFunction<size_t(EDNSOptionView::*)()>("count", [](const EDNSOptionView& option) {
387 return option.values.size();
388 });
389 g_lua.registerFunction<std::vector<string>(EDNSOptionView::*)()>("getValues", [] (const EDNSOptionView& option) {
390 std::vector<string> values;
391 for (const auto& value : option.values) {
392 values.push_back(std::string(value.content, value.size));
393 }
394 return values;
395 });
396
397 g_lua.writeFunction("newDOHResponseMapEntry", [](const std::string& regex, uint16_t status, const std::string& content, boost::optional<std::map<std::string, std::string>> customHeaders) {
398 boost::optional<std::vector<std::pair<std::string, std::string>>> headers{boost::none};
399 if (customHeaders) {
400 headers = std::vector<std::pair<std::string, std::string>>();
401 for (const auto& header : *customHeaders) {
402 headers->push_back({ boost::to_lower_copy(header.first), header.second });
403 }
404 }
405 return std::make_shared<DOHResponseMapEntry>(regex, status, content, headers);
406 });
407 }