This is API version 1 of lua2 backend.
It provides improved interface for Lua script to act as backends.
Configuration
- `lua2-filename` - path to script
- `lua2-query-logging` - log lua queries and results
- `lua2-api' - API version (default 2)
for a in $modules; do
AC_MSG_CHECKING([whether we can build module "${a}"])
+ AS_IF([test "x$a" = "xlua2"], [
+ AS_IF([test "x$with_lua" != "xyes"],
+ AC_MSG_ERROR([Cannot build lua2 module without lua]),[])
+ ])
if [[ -d "$srcdir/modules/${a}backend" ]]; then
AC_MSG_RESULT([yes])
moduledirs="$moduledirs ${a}backend"
modules/gsqlite3backend/Makefile
modules/ldapbackend/Makefile
modules/luabackend/Makefile
+ modules/lua2backend/Makefile
modules/mydnsbackend/Makefile
modules/opendbxbackend/Makefile
modules/oraclebackend/Makefile
+------------------------------------------------+--------+--------+-------+--------------+-------------+---------------------------------+--------------+
| :doc:`LDAP <ldap>` | Yes | No | No | No | No | No | ``ldap`` |
+------------------------------------------------+--------+--------+-------+--------------+-------------+---------------------------------+--------------+
+| :doc:`Lua2 <lua2>` | Yes | Yes | No | No | Yes | Yes | ``lua2`` |
++------------------------------------------------+--------+--------+-------+--------------+-------------+---------------------------------+--------------+
| :doc:`MyDNS <mydns>` | Yes | No | No | No | No | No | ``mydns`` |
+------------------------------------------------+--------+--------+-------+--------------+-------------+---------------------------------+--------------+
| :doc:`OpenDBX <opendbx>` | Yes | Yes | Yes | Yes | No | No | ``opendbx`` |
geoip
ldap
lua
+ lua2
mydns
opendbx
oracle
--- /dev/null
+Lua Backend
+===========
+
+* Native: Yes
+* Master: Yes
+* Slave: No
+* Superslave: No
+* Autoserial: No
+* DNSSEC: Yes
+* Disabled data: No
+* Comments: No
+* Module name: lua2
+* Launch name: ``lua2``
+
+This is a rewrite of existing Lua backend.
+This backend is stub between your Lua script and PowerDNS authoritative server.
+The backend uses AuthLua4 base class, and you can use same functions and types as in any other Lua script.
+
+.. warning::
+ Some of the function calls and configuration settings have been changed from original ``Luabackend``, please review this document carefully.
+
+.. warning::
+ All places which use DNS names now use DNSName class which cannot be compared directly to a string.
+ To compare them against a string use either ``tostring(dnsname)`` or ``newDN(string)``.
+
+.. warning::
+ There is no API version 1.
+ Use Luabackend if you need version 1.
+
+API description (v2)
+^^^^^^^^^^^^^^^^^^^^
+
+``bool dns_dnssec``
+~~~~~~~~~~~~~~~~~~~
+If your script supports DNSSEC, set this to true.
+
+``dns_lookup(qtype, qname, domain_id, ctx)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Perform lookup of given resource record name and type.
+
+INPUT:
+ - QType qtype - Type of queried resource record
+ - DNSName qname - Name of queried resource record
+ - int domain_id - ID of associated domain
+ - table ctx - Query context table, contains ``source_address`` and ``real_source_address``.
+
+OUTPUT:
+ Expects a array which has tables with following keys:
+ - DNSName name - resource record name (can also be string)
+ - string type - type of resource record (can also be QType or valid integer)
+ - string content - resource record content
+ - int ttl - time to live for this resource record (default: configured value)
+ - int domain_id - ID of associated domain (default: -1)
+ - bool auth - Whether data is authoritative or not (default: true)
+ - int last_modified - UNIX timestamp of last modification
+ - int scope_mask - How many bytes of source IP netmask was used for this result
+
+NOTES:
+ Defaults are used for omitted keys.
+ Return empty array if you have no results.
+ The requested record type is unlikely to match what was asked from PowerDNS.
+ This function is **required**.
+
+
+``dns_list(target, domain_id)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+List all resource records for target.
+
+INPUT:
+ - DNSName target - Zone name to list
+ - int domain_id - Associated domain ID
+
+OUTPUT:
+ Same as ``lookup`` function. Return false if not found or wanted.
+
+NOTES:
+ This function is **optional**.
+
+``dns_get_domaininfo(domain)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Get domain information for given domain.
+
+INPUT:
+ - DNSName domain - Domain to get info for
+
+OUTPUT:
+ Return false if not supported or found, otherwise expects a table with keys:
+ - string account - Associated account of this domain (default: <empty>)
+ - string kind - Domain kind (NATIVE,MASTER,SLAVE) (default: NATIVE)
+ - int id - Associated domain ID (default: -1)
+ - int last_check - UNIX timestamp of last check from master (default: 0)
+ - table of strings masters - Master servers for this domain (default: <empty>)
+ - long notified_serial - Notified serial to slaves (default: 0)
+ - long serial - Current domain serial
+
+NOTES:
+ This function is **optional**.
+ Defaults are used for omitted keys.
+ ``last_check`` is for automatic serial.
+ ``masters``, ``account``, ``notified_serial`` are for master/slave interaction only.
+ If this function is missing, it will revert into looking up SOA record for the given domain,
+ and uses that, if found.
+
+``dns_get_all_domains()``
+~~~~~~~~~~~~~~~~~~~~~~~~~
+Get domain information for all domains.
+
+OUTPUT:
+ Return false if not supported or found, otherwise return a table of string, domaininfo. See ``dns_get_domaininfo```.
+
+NOTES:
+ This function is **optional**, except if you need master functionality.
+
+``dns_get_domain_metadata(domain, kind)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Get metadata value(s) for given domain and metadata kind.
+
+INPUT:
+ - DNSName domain - Domain to get metadata for
+ - string kind - What kind of metadata to return
+
+OUTPUT:
+ - array of strings. Or false if not supported or found.
+
+NOTES:
+ This function is **required** if ``dns_dnssec`` is true.
+
+``dns_get_all_domain_metadata(domain)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Get all metadata for domain.
+
+INPUT:
+ - DNSName domain - Domain to get metadata for
+
+OUTPUT:
+ Table with metadata keys containing array of strings. Or false if not supported or found.
+
+NOTES:
+ This function is **optional**.
+
+``dns_get_domain_keys(domain)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Get DNSSEC key(s) for the given domain. Content must be valid key record in format that PowerDNS understands.
+
+INPUT:
+ - DNSName domain - Domain to get key(s) for
+
+OUTPUT:
+ Return false if not found or supported, otherwise expects array of tables with keys:
+ - int id - Key ID
+ - int flags - Key flags
+ - bool active - Is key active
+ - string content - Key itself
+
+NOTES:
+ This function is **optional**. However, not implementing this means you cannot do live signing.
+
+``dns_get_before_and_after_names_absolute(id, qname)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Calculate NSEC before/after value for the given qname for domain with id.
+
+INPUT:
+ - int id - Associated domain id
+ - DNSName qname - DNS name to calculate
+
+OUTPUT:
+ Table with keys:
+ - unhashed - DNSName of the unhashed relative to domain
+ - before - (hashed) name of previous record relative to domain
+ - after - (hashed) name of next record relative to domain
+
+NOTES:
+ Strings are promoted to DNSNames (you can also return DNSNames directly)
+ This function is **required** if ``dns_dnssec`` is true.
+ Hashing is required with NSEC3/5.
+ ``before`` and ``after`` should wrap, so that after record of last record is apex record.
+ You can use ``DNSName#canonCompare`` to sort records in correct order.
+
+``dns_set_notified(id, serial)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Called after NOTIFY so that backend can store the notified serial.
+
+INPUT:
+ - int id - Associated domain id
+ - long serial - Notified serial
+
+NOTES:
+ This function is **optional**. However, not implementing this can cause problems with master functionality.
gsqlite3backend \
ldapbackend \
luabackend \
+ lua2backend \
mydnsbackend \
opendbxbackend \
oraclebackend \
--- /dev/null
+AM_CPPFLAGS += $(LUA_CFLAGS) \
+ -I$(top_srcdir)/ext/luawrapper/include
+
+EXTRA_DIST = OBJECTFILES OBJECTLIBS
+
+pkglib_LTLIBRARIES = liblua2backend.la
+
+liblua2backend_la_SOURCES = \
+ lua2backend.cc lua2backend.hh \
+ lua2api2.hh lua2api2.cc
+
+liblua2backend_la_LDFLAGS = -module -avoid-version
+liblua2backend_la_LIBADD = $(LUA_LIBS)
--- /dev/null
+lua2backend.lo lua2api2.lo
--- /dev/null
+$(LUA_LIBS)
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTAPILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "lua2backend.hh"
+
+Lua2BackendAPIv2::~Lua2BackendAPIv2() {
+ if (f_deinit)
+ f_deinit();
+}
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTAPILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+#ifndef LUA2API_2_HH
+#define LUA2API_2_HH 1
+
+#include "boost/lexical_cast.hpp"
+#include "boost/algorithm/string/join.hpp"
+#include "pdns/arguments.hh"
+
+class Lua2BackendAPIv2 : public DNSBackend, AuthLua4 {
+private:
+ typedef std::function<void()> init_call_t;
+ typedef std::function<void()> deinit_call_t;
+
+ typedef std::vector<std::pair<string, string> > lookup_context_t;
+
+ typedef std::vector<std::pair<int, std::vector<std::pair<string, boost::variant<bool, int, DNSName, string, QType> > > > > lookup_result_t;
+ typedef std::function<lookup_result_t(const QType& qtype, const DNSName& qname, int domain_id, const lookup_context_t &ctx)> lookup_call_t;
+
+ typedef boost::variant<bool, lookup_result_t> list_result_t;
+ typedef std::function<list_result_t(const DNSName& qname, int domain_id)> list_call_t;
+
+ typedef vector<pair<string, boost::variant<bool, long, string, vector<string> > > > domaininfo_result_t;
+ typedef boost::variant<bool, domaininfo_result_t> get_domaininfo_result_t;
+ typedef vector<pair<DNSName, domaininfo_result_t> > get_all_domains_result_t;
+ typedef std::function<get_domaininfo_result_t(const DNSName& domain)> get_domaininfo_call_t;
+ typedef std::function<get_all_domains_result_t()> get_all_domains_call_t;
+
+ typedef vector<pair<int, string> > domain_metadata_result_t;
+ typedef boost::variant<bool, domain_metadata_result_t> get_domain_metadata_result_t;
+ typedef boost::variant<bool, vector<pair<string, domain_metadata_result_t> > > get_all_domain_metadata_result_t;
+ typedef std::function<get_domain_metadata_result_t(const DNSName& domain, const string& kind)> get_domain_metadata_call_t;
+ typedef std::function<get_all_domain_metadata_result_t(const DNSName& domain)> get_all_domain_metadata_call_t;
+
+ typedef vector<pair<string, boost::variant<bool, int, string> > > keydata_result_t;
+ typedef boost::variant<bool, vector<pair<int, keydata_result_t> > > get_domain_keys_result_t;
+ typedef std::function<get_domain_keys_result_t(const DNSName& domain)> get_domain_keys_call_t;
+
+ typedef std::vector<std::pair<string, boost::variant<string, DNSName> > > before_and_after_names_result_t;
+ typedef boost::variant<bool, before_and_after_names_result_t> get_before_and_after_names_absolute_result_t;
+ typedef std::function<get_before_and_after_names_absolute_result_t(int id, const DNSName& qname)> get_before_and_after_names_absolute_call_t;
+
+ typedef std::function<void(int, long)> set_notified_call_t;
+
+ typedef std::function<string(const string& cmd)> direct_backend_cmd_call_t;
+public:
+ Lua2BackendAPIv2(const string& suffix) {
+ setArgPrefix("lua2"+suffix);
+ d_debug_log = mustDo("query-logging");
+ prepareContext();
+ loadFile(getArg("filename"));
+ }
+
+ ~Lua2BackendAPIv2();
+
+ #define logCall(func, var) { if (d_debug_log) { L<<Logger::Debug<<"["<<getPrefix()<<"] Calling "<<func<<"("<<var<<")"<< endl; } }
+ #define logResult(var) { if (d_debug_log) { L<<Logger::Debug<<"["<<getPrefix()<<"] Got result " << "'" << var << "'" << endl; } }
+
+ virtual void postPrepareContext() override {
+ AuthLua4::postPrepareContext();
+ }
+
+ virtual void postLoad() override {
+ f_lookup = d_lw->readVariable<boost::optional<lookup_call_t>>("dns_lookup").get_value_or(0);
+ f_list = d_lw->readVariable<boost::optional<list_call_t>>("dns_list").get_value_or(0);
+ f_get_all_domains = d_lw->readVariable<boost::optional<get_all_domains_call_t>>("dns_get_all_domains").get_value_or(0);
+ f_get_domaininfo = d_lw->readVariable<boost::optional<get_domaininfo_call_t>>("dns_get_domaininfo").get_value_or(0);
+ f_get_domain_metadata = d_lw->readVariable<boost::optional<get_domain_metadata_call_t>>("dns_get_domain_metadata").get_value_or(0);
+ 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);
+ f_get_domain_keys = d_lw->readVariable<boost::optional<get_domain_keys_call_t>>("dns_get_domain_keys").get_value_or(0);
+ 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);
+ f_set_notified = d_lw->readVariable<boost::optional<set_notified_call_t>>("dns_set_notified").get_value_or(0);
+
+ auto init = d_lw->readVariable<boost::optional<init_call_t>>("dns_init").get_value_or(0);
+ if (init)
+ init();
+
+ f_deinit = d_lw->readVariable<boost::optional<deinit_call_t>>("dns_deinit").get_value_or(0);
+
+ if (f_lookup == nullptr)
+ throw PDNSException("dns_lookup missing");
+
+ /* see if dnssec support is wanted */
+ d_dnssec = d_lw->readVariable<boost::optional<bool>>("dns_dnssec").get_value_or(false);
+ if (d_dnssec) {
+ if (f_get_domain_metadata == nullptr)
+ throw PDNSException("dns_dnssec is true but dns_get_domain_metadata is missing");
+ if (f_get_before_and_after_names_absolute == nullptr)
+ throw PDNSException("dns_dnssec is true but dns_get_before_and_after_names_absolute is missing");
+ /* domain keys is not strictly speaking necessary for dnssec backend */
+ if (f_get_domain_keys == nullptr)
+ L<<Logger::Warning<<"dns_get_domain_keys missing - cannot do live signing"<<endl;
+ }
+ }
+
+ bool doesDNSSEC() override {
+ return d_dnssec;
+ }
+
+ void parseLookup(const lookup_result_t& result) {
+ for(const auto& row: result) {
+ DNSResourceRecord rec;
+ for(const auto& item: row.second) {
+ if (item.first == "type") {
+ if (item.second.which() == 1)
+ rec.qtype = QType(boost::get<int>(item.second));
+ else if (item.second.which() == 3)
+ rec.qtype = boost::get<string>(item.second);
+ else if (item.second.which() == 4)
+ rec.qtype = boost::get<QType>(item.second);
+ else
+ throw PDNSException("Unsupported value for type");
+ } else if (item.first == "name") {
+ if (item.second.which() == 3)
+ rec.qname = DNSName(boost::get<string>(item.second));
+ else if (item.second.which() == 2)
+ rec.qname = boost::get<DNSName>(item.second);
+ else
+ throw PDNSException("Unsupported value for name");
+ } else if (item.first == "domain_id")
+ rec.domain_id = boost::get<int>(item.second);
+ else if (item.first == "auth")
+ rec.auth = boost::get<bool>(item.second);
+ else if (item.first == "last_modified")
+ rec.last_modified = static_cast<time_t>(boost::get<int>(item.second));
+ else if (item.first == "ttl")
+ rec.ttl = boost::get<int>(item.second);
+ else if (item.first == "content")
+ rec.setContent(boost::get<string>(item.second));
+ else if (item.first == "scopeMask")
+ rec.scopeMask = boost::get<int>(item.second);
+ else
+ L<<Logger::Warning<<"Unsupported key '"<<item.first<<"' in lookup or list result"<<endl;
+
+ }
+ logResult(rec.qname<<" IN "<<rec.qtype.getName()<<" "<<rec.ttl<<" "<<rec.getZoneRepresentation());
+ d_result.push_back(rec);
+ }
+ if (d_result.empty() && d_debug_log)
+ L<<Logger::Debug<<"["<<getPrefix()<<"] Got empty result"<<endl;
+ }
+
+ bool list(const DNSName &target, int domain_id, bool include_disabled=false) override {
+ if (f_list == nullptr) {
+ L<<Logger::Error<<"["<<getPrefix()<<"] dns_list missing - cannot do AXFR"<<endl;
+ return false;
+ }
+
+ if (d_result.size() != 0)
+ throw PDNSException("list attempted while another was running");
+
+ logCall("list", "target="<<target<<",domain_id="<<domain_id);
+ list_result_t result = f_list(target, domain_id);
+
+ if (result.which() == 0)
+ return false;
+
+ parseLookup(boost::get<lookup_result_t>(result));
+
+ return true;
+ }
+
+ void lookup(const QType &qtype, const DNSName &qname, DNSPacket *p, int domain_id) override {
+ if (d_result.size() != 0)
+ throw PDNSException("lookup attempted while another was running");
+
+ lookup_context_t ctx;
+ if (p != NULL) {
+ ctx.emplace_back(lookup_context_t::value_type{"source_address", p->getRemote().toString()});
+ ctx.emplace_back(lookup_context_t::value_type{"real_source_address", p->getRealRemote().toString()});
+ }
+
+ logCall("lookup", "qtype="<<qtype.getName()<<",qname="<<qname<<",domain_id="<<domain_id);
+ lookup_result_t result = f_lookup(qtype, qname, domain_id, ctx);
+ parseLookup(result);
+ }
+
+ bool get(DNSResourceRecord &rr) override {
+ if (d_result.size() == 0)
+ return false;
+ rr = std::move(d_result.front());
+ d_result.pop_front();
+ return true;
+ }
+
+ string directBackendCmd(const string& querystr) override {
+ string::size_type pos = querystr.find_first_of(" \t");
+ string cmd = querystr;
+ string par = "";
+ if (pos != string::npos) {
+ cmd = querystr.substr(0, pos);
+ par = querystr.substr(pos+1);
+ }
+ direct_backend_cmd_call_t f = d_lw->readVariable<boost::optional<direct_backend_cmd_call_t>>(cmd).get_value_or(0);
+ if (f == nullptr) {
+ return cmd + "not found";
+ }
+ logCall(cmd, "parameter="<<par);
+ return f(par);
+ }
+
+ void setNotified(uint32_t id, uint32_t serial) override {
+ if (f_set_notified == NULL)
+ return;
+ logCall("dns_set_notified", "id="<<static_cast<int>(id)<<",serial="<<serial);
+ f_set_notified(static_cast<int>(id), serial);
+ }
+
+ void parseDomainInfo(const domaininfo_result_t& row, DomainInfo& di) {
+ for(const auto& item: row) {
+ if (item.first == "account")
+ di.account = boost::get<string>(item.second);
+ else if (item.first == "last_check")
+ di.last_check = static_cast<time_t>(boost::get<long>(item.second));
+ else if (item.first == "masters")
+ di.masters = boost::get<vector<string>>(item.second);
+ else if (item.first == "id")
+ di.id = static_cast<int>(boost::get<long>(item.second));
+ else if (item.first == "notified_serial")
+ di.notified_serial = static_cast<unsigned int>(boost::get<long>(item.second));
+ else if (item.first == "serial")
+ di.serial = static_cast<unsigned int>(boost::get<long>(item.second));
+ else if (item.first == "kind")
+ di.kind = DomainInfo::stringToKind(boost::get<string>(item.second));
+ else
+ L<<Logger::Warning<<"Unsupported key '"<<item.first<<"' in domaininfo result"<<endl;
+ }
+ di.backend = this;
+ logResult("zone="<<di.zone<<",serial="<<di.serial<<",kind="<<di.getKindString());
+ }
+
+ bool getDomainInfo(const DNSName& domain, DomainInfo& di) override {
+ if (f_get_domaininfo == nullptr) {
+ // use getAuth instead
+ SOAData sd;
+ if (!getAuth(domain, &sd))
+ return false;
+
+ di.zone = domain;
+ di.backend = this;
+ di.serial = sd.serial;
+ return true;
+ }
+
+ logCall("get_domaininfo","domain="<<domain);
+ get_domaininfo_result_t result = f_get_domaininfo(domain);
+
+ if (result.which() == 0)
+ return false;
+
+ di.zone = domain;
+ parseDomainInfo(boost::get<domaininfo_result_t>(result), di);
+
+ return true;
+ }
+
+ void getAllDomains(vector<DomainInfo> *domains, bool include_disabled=false) override {
+ if (f_get_all_domains == nullptr)
+ return;
+
+ logCall("get_all_domains", "");
+ for(const auto& row: f_get_all_domains()) {
+ DomainInfo di;
+ di.zone = row.first;
+ logResult(di.zone);
+ parseDomainInfo(row.second, di);
+ domains->push_back(di);
+ }
+ }
+
+ bool getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string> >& meta) override {
+ if (f_get_all_domain_metadata == nullptr)
+ return false;
+
+ logCall("get_all_domain_metadata","name="<<name);
+ get_all_domain_metadata_result_t result = f_get_all_domain_metadata(name);
+ if (result.which() == 0)
+ return false;
+
+ for(const auto& row: boost::get< vector<pair<string, domain_metadata_result_t> > >(result)) {
+ meta[row.first].clear();
+ for(const auto& item: row.second)
+ meta[row.first].push_back(item.second);
+ logResult("kind="<<row.first<<",value="<<boost::algorithm::join(meta[row.first], ", "));
+ }
+
+ return true;
+ }
+
+ bool getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta) override {
+ if (f_get_domain_metadata == nullptr)
+ return false;
+
+ logCall("get_domain_metadata","name="<<name<<",kind="<<kind);
+ get_domain_metadata_result_t result = f_get_domain_metadata(name, kind);
+ if (result.which() == 0)
+ return false;
+
+ meta.clear();
+ for(const auto& item: boost::get<domain_metadata_result_t>(result))
+ meta.push_back(item.second);
+
+ logResult("value="<<boost::algorithm::join(meta, ", "));
+ return true;
+ }
+
+ bool getDomainKeys(const DNSName& name, std::vector<DNSBackend::KeyData>& keys) override {
+ if (f_get_domain_keys == nullptr)
+ return false;
+
+ logCall("get_domain_keys","name="<<name);
+ get_domain_keys_result_t result = f_get_domain_keys(name);
+
+ if (result.which() == 0)
+ return false;
+
+ for(const auto& row: boost::get<vector<pair<int, keydata_result_t> > >(result)) {
+ DNSBackend::KeyData key;
+ for(const auto& item: row.second) {
+ if (item.first == "content")
+ key.content = boost::get<string>(item.second);
+ else if (item.first == "id")
+ key.id = static_cast<unsigned int>(boost::get<int>(item.second));
+ else if (item.first == "flags")
+ key.flags = static_cast<unsigned int>(boost::get<int>(item.second));
+ else if (item.first == "active")
+ key.active = boost::get<bool>(item.second);
+ else
+ L<<Logger::Warning<<"["<<getPrefix()<<"] Unsupported key '"<<item.first<<"' in keydata result"<<endl;
+ }
+ logResult("id="<<key.id<<",flags="<<key.flags<<",active="<<(key.active ? "true" : "false"));
+ keys.push_back(key);
+ }
+
+ return true;
+ }
+
+ bool getBeforeAndAfterNamesAbsolute(uint32_t id, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after) override {
+ if (f_get_before_and_after_names_absolute == nullptr)
+ return false;
+
+ logCall("get_before_and_after_names_absolute", "id=<<"<<id<<",qname="<<qname);
+ get_before_and_after_names_absolute_result_t result = f_get_before_and_after_names_absolute(id, qname);
+
+ if (result.which() == 0)
+ return false;
+
+ before_and_after_names_result_t row = boost::get<before_and_after_names_result_t>(result);
+ if (row.size() != 3) {
+ L<<Logger::Error<<"Invalid result from dns_get_before_and_after_names_absolute, expected array with 3 items, got "<<row.size()<<"item(s)"<<endl;
+ return false;
+ }
+ for(const auto& item: row) {
+ DNSName value;
+ if (item.second.which() == 0)
+ value = DNSName(boost::get<string>(item.second));
+ else
+ value = DNSName(boost::get<DNSName>(item.second));
+ if (item.first == "unhashed")
+ unhashed = value;
+ else if (item.first == "before")
+ before = value;
+ else if (item.first == "after")
+ after = value;
+ else {
+ L<<Logger::Error<<"Invalid result from dns_get_before_and_after_names_absolute, unexpected key "<<item.first;
+ return false;
+ }
+ }
+
+ logResult("unhashed="<<unhashed<<",before="<<before<<",after="<<after);
+ return true;
+ }
+
+private:
+ std::list<DNSResourceRecord> d_result;
+ bool d_debug_log;
+ bool d_dnssec;
+
+ lookup_call_t f_lookup;
+ list_call_t f_list;
+
+ get_domaininfo_call_t f_get_domaininfo;
+ get_all_domains_call_t f_get_all_domains;
+
+ get_domain_metadata_call_t f_get_domain_metadata;
+ get_all_domain_metadata_call_t f_get_all_domain_metadata;
+
+ get_domain_keys_call_t f_get_domain_keys;
+
+ get_before_and_after_names_absolute_call_t f_get_before_and_after_names_absolute;
+
+ set_notified_call_t f_set_notified;
+
+ deinit_call_t f_deinit;
+};
+
+#endif
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTAPILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "pdns/logger.hh"
+#include "pdns/arguments.hh"
+#include "lua2backend.hh"
+
+class Lua2Factory : public BackendFactory
+{
+public:
+ Lua2Factory() : BackendFactory("lua2") {}
+
+ void declareArguments(const string &suffix="")
+ {
+ declare(suffix,"filename","Filename of the script for lua backend","powerdns-luabackend.lua");
+ declare(suffix,"query-logging","Logging of the Lua2 Backend","no");
+ declare(suffix,"api","Lua backend API version","2");
+ }
+
+ DNSBackend *make(const string &suffix="")
+ {
+ const std::string apiSet = "lua2" + suffix + "-api";
+ const int api = ::arg().asNum(apiSet);
+ DNSBackend *be;
+ switch(api) {
+ case 1:
+ throw PDNSException("Use luabackend for api version 1");
+ case 2:
+ be = new Lua2BackendAPIv2(suffix); break;
+ default:
+ throw PDNSException("Unsupported ABI version " + ::arg()[apiSet]);
+ }
+ return be;
+ }
+};
+
+class Lua2Loader
+{
+public:
+ Lua2Loader()
+ {
+ BackendMakers().report(new Lua2Factory);
+
+ L << Logger::Info << "[lua2backend] This is the lua2 backend version " VERSION
+#ifndef REPRODUCIBLE
+ << " (" __DATE__ " " __TIME__ ")"
+#endif
+ << " reporting" << endl;
+ }
+};
+
+static Lua2Loader luaLoader;
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+#ifndef LUA2BACKEND_HH
+#define LUA2BACKEND_HH 1
+
+#include "pdns/dnsbackend.hh"
+#include "pdns/lua-auth4.hh"
+#include "lua2api2.hh"
+
+#endif
--- /dev/null
+diff
+real_result
+*.out
--- /dev/null
+#!/bin/sh
+cleandig test.invalid AXFR showflags unhash
--- /dev/null
+This test tries to AXFR a domain
--- /dev/null
+_ssh._tcp.service.test.invalid. 60 IN SRV 0 0 22 shell.test.invalid.
+ns1.test.invalid. 60 IN A 127.0.0.1
+ns1.test.invalid. 60 IN AAAA fe80::1
+ns2.test.invalid. 60 IN A 127.0.0.2
+ns2.test.invalid. 60 IN AAAA fe80::2
+shell.test.invalid. 60 IN A 127.0.0.4
+shell.test.invalid. 60 IN AAAA fe80::4
+test.invalid. 60 IN NS ns1.test.invalid.
+test.invalid. 60 IN NS ns2.test.invalid.
+test.invalid. 60 IN SOA ns1.test.invalid. root.test.invalid. 20180115 1 2 3 4
+test.invalid. 60 IN SOA ns1.test.invalid. root.test.invalid. 20180115 1 2 3 4
+test.invalid. 60 IN TXT "this is a test record" "in two parts"
+www.test.invalid. 60 IN A 127.0.0.3
+www.test.invalid. 60 IN AAAA fe80::3
--- /dev/null
+_ssh._tcp.service.test.invalid. 4 IN NSEC shell.test.invalid. SRV RRSIG NSEC
+_ssh._tcp.service.test.invalid. 4 IN RRSIG NSEC 13 5 4 [expiry] [inception] [keytag] test.invalid. ...
+_ssh._tcp.service.test.invalid. 60 IN RRSIG SRV 13 5 60 [expiry] [inception] [keytag] test.invalid. ...
+_ssh._tcp.service.test.invalid. 60 IN SRV 0 0 22 shell.test.invalid.
+ns1.test.invalid. 4 IN NSEC ns2.test.invalid. A AAAA RRSIG NSEC
+ns1.test.invalid. 4 IN RRSIG NSEC 13 3 4 [expiry] [inception] [keytag] test.invalid. ...
+ns1.test.invalid. 60 IN A 127.0.0.1
+ns1.test.invalid. 60 IN AAAA fe80::1
+ns1.test.invalid. 60 IN RRSIG A 13 3 60 [expiry] [inception] [keytag] test.invalid. ...
+ns1.test.invalid. 60 IN RRSIG AAAA 13 3 60 [expiry] [inception] [keytag] test.invalid. ...
+ns2.test.invalid. 4 IN NSEC _ssh._tcp.service.test.invalid. A AAAA RRSIG NSEC
+ns2.test.invalid. 4 IN RRSIG NSEC 13 3 4 [expiry] [inception] [keytag] test.invalid. ...
+ns2.test.invalid. 60 IN A 127.0.0.2
+ns2.test.invalid. 60 IN AAAA fe80::2
+ns2.test.invalid. 60 IN RRSIG A 13 3 60 [expiry] [inception] [keytag] test.invalid. ...
+ns2.test.invalid. 60 IN RRSIG AAAA 13 3 60 [expiry] [inception] [keytag] test.invalid. ...
+shell.test.invalid. 4 IN NSEC www.test.invalid. A AAAA RRSIG NSEC
+shell.test.invalid. 4 IN RRSIG NSEC 13 3 4 [expiry] [inception] [keytag] test.invalid. ...
+shell.test.invalid. 60 IN A 127.0.0.4
+shell.test.invalid. 60 IN AAAA fe80::4
+shell.test.invalid. 60 IN RRSIG A 13 3 60 [expiry] [inception] [keytag] test.invalid. ...
+shell.test.invalid. 60 IN RRSIG AAAA 13 3 60 [expiry] [inception] [keytag] test.invalid. ...
+test.invalid. 4 IN DNSKEY 256 3 13 ...
+test.invalid. 4 IN DNSKEY 257 3 13 ...
+test.invalid. 4 IN NSEC ns1.test.invalid. NS SOA TXT RRSIG NSEC DNSKEY
+test.invalid. 4 IN RRSIG DNSKEY 13 2 4 [expiry] [inception] [keytag] test.invalid. ...
+test.invalid. 4 IN RRSIG NSEC 13 2 4 [expiry] [inception] [keytag] test.invalid. ...
+test.invalid. 60 IN NS ns1.test.invalid.
+test.invalid. 60 IN NS ns2.test.invalid.
+test.invalid. 60 IN RRSIG NS 13 2 60 [expiry] [inception] [keytag] test.invalid. ...
+test.invalid. 60 IN RRSIG SOA 13 2 60 [expiry] [inception] [keytag] test.invalid. ...
+test.invalid. 60 IN RRSIG TXT 13 2 60 [expiry] [inception] [keytag] test.invalid. ...
+test.invalid. 60 IN SOA ns1.test.invalid. root.test.invalid. 20180115 1 2 3 4
+test.invalid. 60 IN SOA ns1.test.invalid. root.test.invalid. 20180115 1 2 3 4
+test.invalid. 60 IN TXT "this is a test record" "in two parts"
+www.test.invalid. 4 IN NSEC test.invalid. A AAAA RRSIG NSEC
+www.test.invalid. 4 IN RRSIG NSEC 13 3 4 [expiry] [inception] [keytag] test.invalid. ...
+www.test.invalid. 60 IN A 127.0.0.3
+www.test.invalid. 60 IN AAAA fe80::3
+www.test.invalid. 60 IN RRSIG A 13 3 60 [expiry] [inception] [keytag] test.invalid. ...
+www.test.invalid. 60 IN RRSIG AAAA 13 3 60 [expiry] [inception] [keytag] test.invalid. ...
--- /dev/null
+#!/bin/sh
+
+cleandig www.test.invalid A dnssec
--- /dev/null
+Basic DNSSEC test
--- /dev/null
+0 www.test.invalid. IN A 60 127.0.0.3
+0 www.test.invalid. IN RRSIG 60 A 13 3 60 [expiry] [inception] [keytag] test.invalid. ...
+2 . IN OPT 32768
+Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='www.test.invalid.', qtype=A
--- /dev/null
+#!/bin/sh
+cleandig www.test.invalid A
+
--- /dev/null
+This test tries to resolve a straight A record that is directly available in
+the database.
--- /dev/null
+0 www.test.invalid. IN A 60 127.0.0.3
+Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='www.test.invalid.', qtype=A
--- /dev/null
+#!/bin/sh
+cleandig www.test.invalid AAAA
+
--- /dev/null
+This test tries to resolve a straight AAAA record that is directly available in
+the database backend.
--- /dev/null
+0 www.test.invalid. IN AAAA 60 fe80::3
+Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='www.test.invalid.', qtype=AAAA
--- /dev/null
+dns_dnssec = true
+
+domains = { "test.invalid.", "test.unit." }
+
+records = {}
+
+records["test.invalid."] = {
+ SOA = { "ns1.test.invalid. root.test.invalid. 20180115 1 2 3 4" },
+ NS = { "ns1.test.invalid.", "ns2.test.invalid." },
+ TXT = { "\"this is a test record\" \"in two parts\"" }
+}
+
+records["ns1.test.invalid."] = {
+ A = { "127.0.0.1" },
+ AAAA = { "fe80::1" }
+}
+
+records["ns2.test.invalid."] = {
+ A = { "127.0.0.2" },
+ AAAA = { "fe80::2" }
+}
+
+records["www.test.invalid."] = {
+ A = { "127.0.0.3" },
+ AAAA = { "fe80::3" }
+}
+
+records["shell.test.invalid."] = {
+ A = { "127.0.0.4" },
+ AAAA = { "fe80::4" }
+}
+
+records["_ssh._tcp.service.test.invalid."] = {
+ SRV = { "0 0 22 shell.test.invalid." }
+}
+
+records["test.unit."] = {
+ SOA = { "ns1.test.invalid. root.test.invalid. 20180115 1 2 3 4" },
+ NS = { "ns1.test.invalid.", "ns2.test.invalid." },
+}
+
+function dnsname_compare(a, b)
+ return a:canonCompare(b)
+end
+
+function table_keys(t)
+ local keyset = {}
+ for k,v in pairs(t) do
+ table.insert(keyset, newDN(k))
+ end
+ table.sort(keyset, dnsname_compare)
+ return keyset
+end
+
+function get_domain_id(qname)
+ for id, dom in ipairs(domains) do
+ if qname == newDN(dom) or qname:isPartOf(newDN(dom)) then
+ return id
+ end
+ end
+ return -1
+end
+
+function dns_lookup(qtype, qname, d_id, ctx)
+ ret = {}
+
+ d_id = get_domain_id(qname)
+
+ if d_id == -1 then
+ return {}
+ end
+
+ rr = records[tostring(qname)]
+ if rr ~= nil then
+ if qtype:getName() == "ANY" then
+ for k, v in pairs(rr) do
+ for idx,row in ipairs(v) do
+ table.insert(ret, { name = qname, type = newQType(k), content = row, ttl = 60, domain_id = d_id })
+ end
+ end
+ elseif rr[qtype:getName()] ~= nil then
+ for idx,row in ipairs(rr[qtype:getName()]) do
+ table.insert(ret, { name = qname, type = qtype, content = row, ttl = 60, domain_id = d_id })
+ end
+ end
+ end
+
+ return ret
+end
+
+function dns_list(qname, id)
+ if id == -1 then
+ id = get_domain_id(qname)
+ if id == -1 then
+ return false
+ end
+ end
+ qname = newDN(domains[id])
+
+ ret = {}
+
+ for name,rr in pairs(records) do
+ if newDN(name):isPartOf(qname) then
+ for k, v in pairs(rr) do
+ for idx,row in ipairs(v) do
+ table.insert(ret, { name = newDN(name), type = newQType(k), content = row, ttl = 60, domain_id = d_id })
+ end
+ end
+ end
+ end
+
+ return ret
+end
+
+function dns_get_domaininfo(dom)
+ if dom == newDN("test.invalid") then
+ return { id=1, serial=20180115 }
+ end
+ if dom == newDN("test.unit") then
+ return { id=2, serial=20180115 }
+ end
+
+ return false
+end
+
+function dns_get_domain_metadata(dom)
+ return false
+end
+
+function dns_get_domain_keys(dom)
+ if dom == newDN("test.unit") then
+ return { { flags=257, content="Private-key-format: v1.2\nAlgorithm: 13 (ECDSAP256SHA256)\nPrivateKey: 5CuAdTI5Y4btRkcDr9y2V2SDB7C/yVwRYe2nbbMc2wQ=", active=true, id=1 }, { flags=256, content="Private-key-format: v1.2\nAlgorithm: 13 (ECDSAP256SHA256)\nPrivateKey: ISTmkTAwILggbEbgyQmsaSpWgpBk/SbYWVJfWllH+Fo=", active=true, id=2 } }
+ end
+ if dom == newDN("test.invalid") then
+ return { { flags=257,content="Private-key-format: v1.2\nAlgorithm: 13 (ECDSAP256SHA256)\nPrivateKey: b4vB6HK3QqQ294d5WJOWtlXmXFDjUOHk/JHmOZ/Hf1Q=", active=true, id=3 }, { flags=256, content="Private-key-format: v1.2\nAlgorithm: 13 (ECDSAP256SHA256)\nPrivateKey: isruMlkroefkRHO0B0TOSByRXXwUy5BTfK2rLTGp8BM=", active=true, id=4 } }
+ end
+ return false
+end
+
+function dns_get_before_and_after_names_absolute(did, qname)
+ if did == -1 then
+ did = get_domain_id(qname)
+ end
+ local base = newDN(domains[did])
+ -- find out before and after name
+ local before = newDN("")
+ local after = newDN("")
+ local empty = newDN("")
+
+ for i, rr in ipairs(table_keys(records)) do
+ if rr:isPartOf(base) then
+ rr = rr:makeRelative(base)
+ if qname:canonCompare(rr) == false then
+ if before == empty then
+ before = rr
+ end
+ else
+ if after == empty then
+ after = rr
+ end
+ end
+ end
+ end
+
+ return { qname=qname,before=before,after=after }
+end
--- /dev/null
+domains = { "test.invalid.", "test.unit." }
+
+records = {}
+
+records["test.invalid."] = {
+ SOA = { "ns1.test.invalid. root.test.invalid. 20180115 1 2 3 4" },
+ NS = { "ns1.test.invalid.", "ns2.test.invalid." },
+ TXT = { "\"this is a test record\" \"in two parts\"" }
+}
+
+records["ns1.test.invalid."] = {
+ A = { "127.0.0.1" },
+ AAAA = { "fe80::1" }
+}
+
+records["ns2.test.invalid."] = {
+ A = { "127.0.0.2" },
+ AAAA = { "fe80::2" }
+}
+
+records["www.test.invalid."] = {
+ A = { "127.0.0.3" },
+ AAAA = { "fe80::3" }
+}
+
+records["shell.test.invalid."] = {
+ A = { "127.0.0.4" },
+ AAAA = { "fe80::4" }
+}
+
+records["_ssh._tcp.service.test.invalid."] = {
+ SRV = { "0 0 22 shell.test.invalid." }
+}
+
+records["test.unit."] = {
+ SOA = { "ns1.test.invalid. root.test.invalid. 20180115 1 2 3 4" },
+ NS = { "ns1.test.invalid.", "ns2.test.invalid." },
+}
+
+function get_domain_id(qname)
+ for id, dom in ipairs(domains) do
+ if qname == newDN(dom) or qname:isPartOf(newDN(dom)) then
+ return id
+ end
+ end
+ return -1
+end
+
+--function dns_get_domaininfo(dom)
+-- if dom == newDN("test.invalid") then
+-- return { id=1, serial=20180115 }
+-- end
+-- if dom == newDN("test.unit") then
+-- return { id=2, serial=20180115 }
+-- end
+--
+-- return false
+--end
+
+function dns_lookup(qtype, qname, d_id, ctx)
+ ret = {}
+
+ d_id = get_domain_id(qname)
+
+ if d_id == -1 then
+ return {}
+ end
+
+ rr = records[tostring(qname)]
+ if rr ~= nil then
+ if qtype:getName() == "ANY" then
+ for k, v in pairs(rr) do
+ for idx,row in ipairs(v) do
+ table.insert(ret, { name = qname, type = newQType(k), content = row, ttl = 60, domain_id = d_id })
+ end
+ end
+ elseif rr[qtype:getName()] ~= nil then
+ for idx,row in ipairs(rr[qtype:getName()]) do
+ table.insert(ret, { name = qname, type = qtype, content = row, ttl = 60, domain_id = d_id })
+ end
+ end
+ end
+
+ return ret
+end
+
+function dns_list(qname, id)
+ if id == -1 then
+ id = get_domain_id(qname)
+ if id == -1 then
+ return false
+ end
+ end
+ qname = newDN(domains[id])
+
+ ret = {}
+
+ for name,rr in pairs(records) do
+ if newDN(name):isPartOf(qname) then
+ for k, v in pairs(rr) do
+ for idx,row in ipairs(v) do
+ table.insert(ret, { name = newDN(name), type = newQType(k), content = row, ttl = 60, domain_id = d_id })
+ end
+ end
+ end
+ end
+
+ return ret
+end
--- /dev/null
+#!/bin/sh
+
+cleandig wap.test.invalid A dnssec
--- /dev/null
+Basic DNSSEC test
--- /dev/null
+1 ns1.test.invalid. IN NSEC 4 www.test.invalid. A AAAA RRSIG NSEC
+1 ns1.test.invalid. IN RRSIG 4 NSEC 13 3 4 [expiry] [inception] [keytag] test.invalid. ...
+1 test.invalid. IN NSEC 4 ns1.test.invalid. NS SOA TXT RRSIG NSEC DNSKEY
+1 test.invalid. IN RRSIG 4 NSEC 13 2 4 [expiry] [inception] [keytag] test.invalid. ...
+1 test.invalid. IN RRSIG 4 SOA 13 2 60 [expiry] [inception] [keytag] test.invalid. ...
+1 test.invalid. IN SOA 4 ns1.test.invalid. root.test.invalid. 20180115 1 2 3 4
+2 . IN OPT 32768
+Rcode: 3 (Non-Existent domain), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='wap.test.invalid.', qtype=A
--- /dev/null
+#!/bin/sh
+
+cleandig middle.test.invalid A dnssec
--- /dev/null
+Basic DNSSEC test
--- /dev/null
+1 test.invalid. IN NSEC 4 ns1.test.invalid. NS SOA TXT RRSIG NSEC DNSKEY
+1 test.invalid. IN RRSIG 4 NSEC 13 2 4 [expiry] [inception] [keytag] test.invalid. ...
+1 test.invalid. IN RRSIG 4 SOA 13 2 60 [expiry] [inception] [keytag] test.invalid. ...
+1 test.invalid. IN SOA 4 ns1.test.invalid. root.test.invalid. 20180115 1 2 3 4
+2 . IN OPT 32768
+Rcode: 3 (Non-Existent domain), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='middle.test.invalid.', qtype=A
source ./backends/ldap-master
;;
+ lua2*)
+ source ./backends/lua2-master
+ ;;
+
ext-nsd*)
source ./ext/nsd-master
;;
--- /dev/null
+case $context in
+ lua2*)
+ lua2sec=$(echo $context | cut -d- -f 2)
+ testsdir=../modules/lua2backend/regression-tests/
+
+ # cleanup unbound-host.conf to avoid failures
+ rm -f unbound-host.conf
+
+ skipreasons="nodnssec nodyndns"
+
+ luascript="lua2.lua"
+
+ if [ "$lua2sec" = "dnssec" ]
+ then
+ lua2dosec="yes"
+ extracontexts="dnssec"
+ skipreasons="nonsec3 nonarrow nodyndns"
+ luascript="lua2-dnssec.lua"
+ fi
+
+ # generate pdns.conf for pdnsutil
+ cat > pdns-lua2.conf <<EOF
+module-dir=./modules
+launch=lua2
+lua2-filename=$testsdir/$luascript
+lua2-api=2
+allow-axfr-ips=0.0.0.0/0,::/0
+EOF
+
+ $RUNWRAPPER $PDNS --daemon=no --local-address=$address --local-port=$port --socket-dir=./ \
+ --no-shuffle --launch=lua2 \
+ --cache-ttl=$cachettl --dname-processing --no-config \
+ --distributor-threads=1 \
+ --allow-axfr-ips=0.0.0.0/0,::/0 \
+ --lua2-filename=$testsdir/$luascript --lua2-api=2 --module-dir=./modules &
+ ;;
+
+ *)
+ nocontext=yes
+esac
--- /dev/null
+../../modules/lua2backend/.libs/liblua2backend.so
\ No newline at end of file
#remotebackend-pipe-nsec3-narrow remotebackend-unix-nsec3-narrow remotebackend-http-nsec3-narrow
tinydns
ldap-tree ldap-simple ldap-strict
+lua2 lua2-dnssec lua2-nsec3 lua2-nsec3-narrow
#ext-nsd ext-nsd-nsec ext-nsd-nsec3 ext-bind ext-bind-nsec ext-bind-nsec3
* Add -presigned to any ext-nsd, ext-bind, bind, gmysql or gsqlite3 test (except narrow)