2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
24 #define LUA2API_2_HH 1
26 #include "boost/lexical_cast.hpp"
27 #include "boost/algorithm/string/join.hpp"
28 #include "pdns/arguments.hh"
30 class Lua2BackendAPIv2 : public DNSBackend, AuthLua4 {
32 typedef std::function<void()> init_call_t;
33 typedef std::function<void()> deinit_call_t;
35 typedef std::vector<std::pair<string, string> > lookup_context_t;
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;
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;
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;
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;
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;
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;
63 typedef std::function<void(int, long)> set_notified_call_t;
65 typedef std::function<string(const string& cmd)> direct_backend_cmd_call_t;
67 Lua2BackendAPIv2(const string& suffix) {
68 setArgPrefix("lua2"+suffix);
69 d_debug_log = mustDo("query-logging");
71 loadFile(getArg("filename"));
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; } }
79 virtual void postPrepareContext() override {
80 AuthLua4::postPrepareContext();
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);
94 auto init = d_lw->readVariable<boost::optional<init_call_t>>("dns_init").get_value_or(0);
98 f_deinit = d_lw->readVariable<boost::optional<deinit_call_t>>("dns_deinit").get_value_or(0);
100 if (f_lookup == nullptr)
101 throw PDNSException("dns_lookup missing");
103 /* see if dnssec support is wanted */
104 d_dnssec = d_lw->readVariable<boost::optional<bool>>("dns_dnssec").get_value_or(false);
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;
116 bool doesDNSSEC() override {
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);
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);
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);
153 L<<Logger::Warning<<"Unsupported key '"<<item.first<<"' in lookup or list result"<<endl;
156 logResult(rec.qname<<" IN "<<rec.qtype.getName()<<" "<<rec.ttl<<" "<<rec.getZoneRepresentation());
157 d_result.push_back(rec);
159 if (d_result.empty() && d_debug_log)
160 L<<Logger::Debug<<"["<<getPrefix()<<"] Got empty result"<<endl;
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;
169 if (d_result.size() != 0)
170 throw PDNSException("list attempted while another was running");
172 logCall("list", "target="<<target<<",domain_id="<<domain_id);
173 list_result_t result = f_list(target, domain_id);
175 if (result.which() == 0)
178 parseLookup(boost::get<lookup_result_t>(result));
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");
187 lookup_context_t ctx;
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()});
193 logCall("lookup", "qtype="<<qtype.getName()<<",qname="<<qname<<",domain_id="<<domain_id);
194 lookup_result_t result = f_lookup(qtype, qname, domain_id, ctx);
198 bool get(DNSResourceRecord &rr) override {
199 if (d_result.size() == 0)
201 rr = std::move(d_result.front());
202 d_result.pop_front();
206 string directBackendCmd(const string& querystr) override {
207 string::size_type pos = querystr.find_first_of(" \t");
208 string cmd = querystr;
210 if (pos != string::npos) {
211 cmd = querystr.substr(0, pos);
212 par = querystr.substr(pos+1);
214 direct_backend_cmd_call_t f = d_lw->readVariable<boost::optional<direct_backend_cmd_call_t>>(cmd).get_value_or(0);
216 return cmd + "not found";
218 logCall(cmd, "parameter="<<par);
222 void setNotified(uint32_t id, uint32_t serial) override {
223 if (f_set_notified == NULL)
225 logCall("dns_set_notified", "id="<<static_cast<int>(id)<<",serial="<<serial);
226 f_set_notified(static_cast<int>(id), serial);
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));
246 L<<Logger::Warning<<"Unsupported key '"<<item.first<<"' in domaininfo result"<<endl;
249 logResult("zone="<<di.zone<<",serial="<<di.serial<<",kind="<<di.getKindString());
252 bool getDomainInfo(const DNSName& domain, DomainInfo& di) override {
253 if (f_get_domaininfo == nullptr) {
254 // use getAuth instead
256 if (!getAuth(domain, &sd))
261 di.serial = sd.serial;
265 logCall("get_domaininfo","domain="<<domain);
266 get_domaininfo_result_t result = f_get_domaininfo(domain);
268 if (result.which() == 0)
272 parseDomainInfo(boost::get<domaininfo_result_t>(result), di);
277 void getAllDomains(vector<DomainInfo> *domains, bool include_disabled=false) override {
278 if (f_get_all_domains == nullptr)
281 logCall("get_all_domains", "");
282 for(const auto& row: f_get_all_domains()) {
286 parseDomainInfo(row.second, di);
287 domains->push_back(di);
291 bool getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string> >& meta) override {
292 if (f_get_all_domain_metadata == nullptr)
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)
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], ", "));
310 bool getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta) override {
311 if (f_get_domain_metadata == nullptr)
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)
320 for(const auto& item: boost::get<domain_metadata_result_t>(result))
321 meta.push_back(item.second);
323 logResult("value="<<boost::algorithm::join(meta, ", "));
327 bool getDomainKeys(const DNSName& name, std::vector<DNSBackend::KeyData>& keys) override {
328 if (f_get_domain_keys == nullptr)
331 logCall("get_domain_keys","name="<<name);
332 get_domain_keys_result_t result = f_get_domain_keys(name);
334 if (result.which() == 0)
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);
349 L<<Logger::Warning<<"["<<getPrefix()<<"] Unsupported key '"<<item.first<<"' in keydata result"<<endl;
351 logResult("id="<<key.id<<",flags="<<key.flags<<",active="<<(key.active ? "true" : "false"));
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)
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);
365 if (result.which() == 0)
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;
373 for(const auto& item: row) {
375 if (item.second.which() == 0)
376 value = DNSName(boost::get<string>(item.second));
378 value = DNSName(boost::get<DNSName>(item.second));
379 if (item.first == "unhashed")
381 else if (item.first == "before")
383 else if (item.first == "after")
386 L<<Logger::Error<<"Invalid result from dns_get_before_and_after_names_absolute, unexpected key "<<item.first<<endl;
391 logResult("unhashed="<<unhashed<<",before="<<before<<",after="<<after);
396 std::list<DNSResourceRecord> d_result;
400 lookup_call_t f_lookup;
403 get_domaininfo_call_t f_get_domaininfo;
404 get_all_domains_call_t f_get_all_domains;
406 get_domain_metadata_call_t f_get_domain_metadata;
407 get_all_domain_metadata_call_t f_get_all_domain_metadata;
409 get_domain_keys_call_t f_get_domain_keys;
411 get_before_and_after_names_absolute_call_t f_get_before_and_after_names_absolute;
413 set_notified_call_t f_set_notified;
415 deinit_call_t f_deinit;