]> git.ipfire.org Git - thirdparty/pdns.git/blob - modules/lua2backend/lua2api2.hh
40b309570b031665e0b6040f27b2915f45bca6ff
[thirdparty/pdns.git] / modules / lua2backend / lua2api2.hh
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 * MERCHANTAPILITY 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 #pragma once
23 #ifndef LUA2API_2_HH
24 #define LUA2API_2_HH 1
25
26 #include "boost/lexical_cast.hpp"
27 #include "boost/algorithm/string/join.hpp"
28 #include "pdns/arguments.hh"
29
30 class Lua2BackendAPIv2 : public DNSBackend, AuthLua4 {
31 private:
32 typedef std::function<void()> init_call_t;
33 typedef std::function<void()> deinit_call_t;
34
35 typedef std::vector<std::pair<string, string> > lookup_context_t;
36
37 typedef std::vector<std::pair<int, std::vector<std::pair<string, boost::variant<bool, int, DNSName, string, QType> > > > > lookup_result_t;
38 typedef std::function<lookup_result_t(const QType& qtype, const DNSName& qname, int domain_id, const lookup_context_t &ctx)> lookup_call_t;
39
40 typedef boost::variant<bool, lookup_result_t> list_result_t;
41 typedef std::function<list_result_t(const DNSName& qname, int domain_id)> list_call_t;
42
43 typedef vector<pair<string, boost::variant<bool, long, string, vector<string> > > > domaininfo_result_t;
44 typedef boost::variant<bool, domaininfo_result_t> get_domaininfo_result_t;
45 typedef vector<pair<DNSName, domaininfo_result_t> > get_all_domains_result_t;
46 typedef std::function<get_domaininfo_result_t(const DNSName& domain)> get_domaininfo_call_t;
47 typedef std::function<get_all_domains_result_t()> get_all_domains_call_t;
48
49 typedef vector<pair<int, string> > domain_metadata_result_t;
50 typedef boost::variant<bool, domain_metadata_result_t> get_domain_metadata_result_t;
51 typedef boost::variant<bool, vector<pair<string, domain_metadata_result_t> > > get_all_domain_metadata_result_t;
52 typedef std::function<get_domain_metadata_result_t(const DNSName& domain, const string& kind)> get_domain_metadata_call_t;
53 typedef std::function<get_all_domain_metadata_result_t(const DNSName& domain)> get_all_domain_metadata_call_t;
54
55 typedef vector<pair<string, boost::variant<bool, int, string> > > keydata_result_t;
56 typedef boost::variant<bool, vector<pair<int, keydata_result_t> > > get_domain_keys_result_t;
57 typedef std::function<get_domain_keys_result_t(const DNSName& domain)> get_domain_keys_call_t;
58
59 typedef std::vector<std::pair<string, boost::variant<string, DNSName> > > before_and_after_names_result_t;
60 typedef boost::variant<bool, before_and_after_names_result_t> get_before_and_after_names_absolute_result_t;
61 typedef std::function<get_before_and_after_names_absolute_result_t(int id, const DNSName& qname)> get_before_and_after_names_absolute_call_t;
62
63 typedef std::function<void(int, long)> set_notified_call_t;
64
65 typedef std::function<string(const string& cmd)> direct_backend_cmd_call_t;
66 public:
67 Lua2BackendAPIv2(const string& suffix) {
68 setArgPrefix("lua2"+suffix);
69 d_debug_log = mustDo("query-logging");
70 prepareContext();
71 loadFile(getArg("filename"));
72 }
73
74 ~Lua2BackendAPIv2();
75
76 #define logCall(func, var) { if (d_debug_log) { L<<Logger::Debug<<"["<<getPrefix()<<"] Calling "<<func<<"("<<var<<")"<< endl; } }
77 #define logResult(var) { if (d_debug_log) { L<<Logger::Debug<<"["<<getPrefix()<<"] Got result " << "'" << var << "'" << endl; } }
78
79 virtual void postPrepareContext() override {
80 AuthLua4::postPrepareContext();
81 }
82
83 virtual void postLoad() override {
84 f_lookup = d_lw->readVariable<boost::optional<lookup_call_t>>("dns_lookup").get_value_or(0);
85 f_list = d_lw->readVariable<boost::optional<list_call_t>>("dns_list").get_value_or(0);
86 f_get_all_domains = d_lw->readVariable<boost::optional<get_all_domains_call_t>>("dns_get_all_domains").get_value_or(0);
87 f_get_domaininfo = d_lw->readVariable<boost::optional<get_domaininfo_call_t>>("dns_get_domaininfo").get_value_or(0);
88 f_get_domain_metadata = d_lw->readVariable<boost::optional<get_domain_metadata_call_t>>("dns_get_domain_metadata").get_value_or(0);
89 f_get_all_domain_metadata = d_lw->readVariable<boost::optional<get_all_domain_metadata_call_t>>("dns_get_all_domain_metadata").get_value_or(0);
90 f_get_domain_keys = d_lw->readVariable<boost::optional<get_domain_keys_call_t>>("dns_get_domain_keys").get_value_or(0);
91 f_get_before_and_after_names_absolute = d_lw->readVariable<boost::optional<get_before_and_after_names_absolute_call_t>>("dns_get_before_and_after_names_absolute").get_value_or(0);
92 f_set_notified = d_lw->readVariable<boost::optional<set_notified_call_t>>("dns_set_notified").get_value_or(0);
93
94 auto init = d_lw->readVariable<boost::optional<init_call_t>>("dns_init").get_value_or(0);
95 if (init)
96 init();
97
98 f_deinit = d_lw->readVariable<boost::optional<deinit_call_t>>("dns_deinit").get_value_or(0);
99
100 if (f_lookup == nullptr)
101 throw PDNSException("dns_lookup missing");
102
103 /* see if dnssec support is wanted */
104 d_dnssec = d_lw->readVariable<boost::optional<bool>>("dns_dnssec").get_value_or(false);
105 if (d_dnssec) {
106 if (f_get_domain_metadata == nullptr)
107 throw PDNSException("dns_dnssec is true but dns_get_domain_metadata is missing");
108 if (f_get_before_and_after_names_absolute == nullptr)
109 throw PDNSException("dns_dnssec is true but dns_get_before_and_after_names_absolute is missing");
110 /* domain keys is not strictly speaking necessary for dnssec backend */
111 if (f_get_domain_keys == nullptr)
112 L<<Logger::Warning<<"dns_get_domain_keys missing - cannot do live signing"<<endl;
113 }
114 }
115
116 bool doesDNSSEC() override {
117 return d_dnssec;
118 }
119
120 void parseLookup(const lookup_result_t& result) {
121 for(const auto& row: result) {
122 DNSResourceRecord rec;
123 for(const auto& item: row.second) {
124 if (item.first == "type") {
125 if (item.second.which() == 1)
126 rec.qtype = QType(boost::get<int>(item.second));
127 else if (item.second.which() == 3)
128 rec.qtype = boost::get<string>(item.second);
129 else if (item.second.which() == 4)
130 rec.qtype = boost::get<QType>(item.second);
131 else
132 throw PDNSException("Unsupported value for type");
133 } else if (item.first == "name") {
134 if (item.second.which() == 3)
135 rec.qname = DNSName(boost::get<string>(item.second));
136 else if (item.second.which() == 2)
137 rec.qname = boost::get<DNSName>(item.second);
138 else
139 throw PDNSException("Unsupported value for name");
140 } else if (item.first == "domain_id")
141 rec.domain_id = boost::get<int>(item.second);
142 else if (item.first == "auth")
143 rec.auth = boost::get<bool>(item.second);
144 else if (item.first == "last_modified")
145 rec.last_modified = static_cast<time_t>(boost::get<int>(item.second));
146 else if (item.first == "ttl")
147 rec.ttl = boost::get<int>(item.second);
148 else if (item.first == "content")
149 rec.setContent(boost::get<string>(item.second));
150 else if (item.first == "scopeMask")
151 rec.scopeMask = boost::get<int>(item.second);
152 else
153 L<<Logger::Warning<<"Unsupported key '"<<item.first<<"' in lookup or list result"<<endl;
154
155 }
156 logResult(rec.qname<<" IN "<<rec.qtype.getName()<<" "<<rec.ttl<<" "<<rec.getZoneRepresentation());
157 d_result.push_back(rec);
158 }
159 if (d_result.empty() && d_debug_log)
160 L<<Logger::Debug<<"["<<getPrefix()<<"] Got empty result"<<endl;
161 }
162
163 bool list(const DNSName &target, int domain_id, bool include_disabled=false) override {
164 if (f_list == nullptr) {
165 L<<Logger::Error<<"["<<getPrefix()<<"] dns_list missing - cannot do AXFR"<<endl;
166 return false;
167 }
168
169 if (d_result.size() != 0)
170 throw PDNSException("list attempted while another was running");
171
172 logCall("list", "target="<<target<<",domain_id="<<domain_id);
173 list_result_t result = f_list(target, domain_id);
174
175 if (result.which() == 0)
176 return false;
177
178 parseLookup(boost::get<lookup_result_t>(result));
179
180 return true;
181 }
182
183 void lookup(const QType &qtype, const DNSName &qname, DNSPacket *p, int domain_id) override {
184 if (d_result.size() != 0)
185 throw PDNSException("lookup attempted while another was running");
186
187 lookup_context_t ctx;
188 if (p != NULL) {
189 ctx.emplace_back(lookup_context_t::value_type{"source_address", p->getRemote().toString()});
190 ctx.emplace_back(lookup_context_t::value_type{"real_source_address", p->getRealRemote().toString()});
191 }
192
193 logCall("lookup", "qtype="<<qtype.getName()<<",qname="<<qname<<",domain_id="<<domain_id);
194 lookup_result_t result = f_lookup(qtype, qname, domain_id, ctx);
195 parseLookup(result);
196 }
197
198 bool get(DNSResourceRecord &rr) override {
199 if (d_result.size() == 0)
200 return false;
201 rr = std::move(d_result.front());
202 d_result.pop_front();
203 return true;
204 }
205
206 string directBackendCmd(const string& querystr) override {
207 string::size_type pos = querystr.find_first_of(" \t");
208 string cmd = querystr;
209 string par = "";
210 if (pos != string::npos) {
211 cmd = querystr.substr(0, pos);
212 par = querystr.substr(pos+1);
213 }
214 direct_backend_cmd_call_t f = d_lw->readVariable<boost::optional<direct_backend_cmd_call_t>>(cmd).get_value_or(0);
215 if (f == nullptr) {
216 return cmd + "not found";
217 }
218 logCall(cmd, "parameter="<<par);
219 return f(par);
220 }
221
222 void setNotified(uint32_t id, uint32_t serial) override {
223 if (f_set_notified == NULL)
224 return;
225 logCall("dns_set_notified", "id="<<static_cast<int>(id)<<",serial="<<serial);
226 f_set_notified(static_cast<int>(id), serial);
227 }
228
229 void parseDomainInfo(const domaininfo_result_t& row, DomainInfo& di) {
230 for(const auto& item: row) {
231 if (item.first == "account")
232 di.account = boost::get<string>(item.second);
233 else if (item.first == "last_check")
234 di.last_check = static_cast<time_t>(boost::get<long>(item.second));
235 else if (item.first == "masters")
236 di.masters = boost::get<vector<string>>(item.second);
237 else if (item.first == "id")
238 di.id = static_cast<int>(boost::get<long>(item.second));
239 else if (item.first == "notified_serial")
240 di.notified_serial = static_cast<unsigned int>(boost::get<long>(item.second));
241 else if (item.first == "serial")
242 di.serial = static_cast<unsigned int>(boost::get<long>(item.second));
243 else if (item.first == "kind")
244 di.kind = DomainInfo::stringToKind(boost::get<string>(item.second));
245 else
246 L<<Logger::Warning<<"Unsupported key '"<<item.first<<"' in domaininfo result"<<endl;
247 }
248 di.backend = this;
249 logResult("zone="<<di.zone<<",serial="<<di.serial<<",kind="<<di.getKindString());
250 }
251
252 bool getDomainInfo(const DNSName& domain, DomainInfo& di) override {
253 if (f_get_domaininfo == nullptr) {
254 // use getAuth instead
255 SOAData sd;
256 if (!getAuth(domain, &sd))
257 return false;
258
259 di.zone = domain;
260 di.backend = this;
261 di.serial = sd.serial;
262 return true;
263 }
264
265 logCall("get_domaininfo","domain="<<domain);
266 get_domaininfo_result_t result = f_get_domaininfo(domain);
267
268 if (result.which() == 0)
269 return false;
270
271 di.zone = domain;
272 parseDomainInfo(boost::get<domaininfo_result_t>(result), di);
273
274 return true;
275 }
276
277 void getAllDomains(vector<DomainInfo> *domains, bool include_disabled=false) override {
278 if (f_get_all_domains == nullptr)
279 return;
280
281 logCall("get_all_domains", "");
282 for(const auto& row: f_get_all_domains()) {
283 DomainInfo di;
284 di.zone = row.first;
285 logResult(di.zone);
286 parseDomainInfo(row.second, di);
287 domains->push_back(di);
288 }
289 }
290
291 bool getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string> >& meta) override {
292 if (f_get_all_domain_metadata == nullptr)
293 return false;
294
295 logCall("get_all_domain_metadata","name="<<name);
296 get_all_domain_metadata_result_t result = f_get_all_domain_metadata(name);
297 if (result.which() == 0)
298 return false;
299
300 for(const auto& row: boost::get< vector<pair<string, domain_metadata_result_t> > >(result)) {
301 meta[row.first].clear();
302 for(const auto& item: row.second)
303 meta[row.first].push_back(item.second);
304 logResult("kind="<<row.first<<",value="<<boost::algorithm::join(meta[row.first], ", "));
305 }
306
307 return true;
308 }
309
310 bool getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta) override {
311 if (f_get_domain_metadata == nullptr)
312 return false;
313
314 logCall("get_domain_metadata","name="<<name<<",kind="<<kind);
315 get_domain_metadata_result_t result = f_get_domain_metadata(name, kind);
316 if (result.which() == 0)
317 return false;
318
319 meta.clear();
320 for(const auto& item: boost::get<domain_metadata_result_t>(result))
321 meta.push_back(item.second);
322
323 logResult("value="<<boost::algorithm::join(meta, ", "));
324 return true;
325 }
326
327 bool getDomainKeys(const DNSName& name, std::vector<DNSBackend::KeyData>& keys) override {
328 if (f_get_domain_keys == nullptr)
329 return false;
330
331 logCall("get_domain_keys","name="<<name);
332 get_domain_keys_result_t result = f_get_domain_keys(name);
333
334 if (result.which() == 0)
335 return false;
336
337 for(const auto& row: boost::get<vector<pair<int, keydata_result_t> > >(result)) {
338 DNSBackend::KeyData key;
339 for(const auto& item: row.second) {
340 if (item.first == "content")
341 key.content = boost::get<string>(item.second);
342 else if (item.first == "id")
343 key.id = static_cast<unsigned int>(boost::get<int>(item.second));
344 else if (item.first == "flags")
345 key.flags = static_cast<unsigned int>(boost::get<int>(item.second));
346 else if (item.first == "active")
347 key.active = boost::get<bool>(item.second);
348 else
349 L<<Logger::Warning<<"["<<getPrefix()<<"] Unsupported key '"<<item.first<<"' in keydata result"<<endl;
350 }
351 logResult("id="<<key.id<<",flags="<<key.flags<<",active="<<(key.active ? "true" : "false"));
352 keys.push_back(key);
353 }
354
355 return true;
356 }
357
358 bool getBeforeAndAfterNamesAbsolute(uint32_t id, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after) override {
359 if (f_get_before_and_after_names_absolute == nullptr)
360 return false;
361
362 logCall("get_before_and_after_names_absolute", "id=<<"<<id<<",qname="<<qname);
363 get_before_and_after_names_absolute_result_t result = f_get_before_and_after_names_absolute(id, qname);
364
365 if (result.which() == 0)
366 return false;
367
368 before_and_after_names_result_t row = boost::get<before_and_after_names_result_t>(result);
369 if (row.size() != 3) {
370 L<<Logger::Error<<"Invalid result from dns_get_before_and_after_names_absolute, expected array with 3 items, got "<<row.size()<<"item(s)"<<endl;
371 return false;
372 }
373 for(const auto& item: row) {
374 DNSName value;
375 if (item.second.which() == 0)
376 value = DNSName(boost::get<string>(item.second));
377 else
378 value = DNSName(boost::get<DNSName>(item.second));
379 if (item.first == "unhashed")
380 unhashed = value;
381 else if (item.first == "before")
382 before = value;
383 else if (item.first == "after")
384 after = value;
385 else {
386 L<<Logger::Error<<"Invalid result from dns_get_before_and_after_names_absolute, unexpected key "<<item.first<<endl;
387 return false;
388 }
389 }
390
391 logResult("unhashed="<<unhashed<<",before="<<before<<",after="<<after);
392 return true;
393 }
394
395 private:
396 std::list<DNSResourceRecord> d_result;
397 bool d_debug_log;
398 bool d_dnssec;
399
400 lookup_call_t f_lookup;
401 list_call_t f_list;
402
403 get_domaininfo_call_t f_get_domaininfo;
404 get_all_domains_call_t f_get_all_domains;
405
406 get_domain_metadata_call_t f_get_domain_metadata;
407 get_all_domain_metadata_call_t f_get_all_domain_metadata;
408
409 get_domain_keys_call_t f_get_domain_keys;
410
411 get_before_and_after_names_absolute_call_t f_get_before_and_after_names_absolute;
412
413 set_notified_call_t f_set_notified;
414
415 deinit_call_t f_deinit;
416 };
417
418 #endif