diff --git a/configure.ac b/configure.ac index e03561e129353..022c43bbaec42 100644 --- a/configure.ac +++ b/configure.ac @@ -237,6 +237,10 @@ AM_CONDITIONAL([SQLITE3], [test "x$needsqlite3" = "xyes"]) 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" @@ -314,6 +318,7 @@ AC_CONFIG_FILES([ modules/gsqlite3backend/Makefile modules/ldapbackend/Makefile modules/luabackend/Makefile + modules/lua2backend/Makefile modules/mydnsbackend/Makefile modules/opendbxbackend/Makefile modules/oraclebackend/Makefile diff --git a/docs/backends/index.rst b/docs/backends/index.rst index 986ea0fb8c07e..caa25276aa2a8 100644 --- a/docs/backends/index.rst +++ b/docs/backends/index.rst @@ -22,6 +22,8 @@ The following table describes the supported backends and some of their capabilit +------------------------------------------------+--------+--------+-------+--------------+-------------+---------------------------------+--------------+ | :doc:`LDAP ` | Yes | No | No | No | No | No | ``ldap`` | +------------------------------------------------+--------+--------+-------+--------------+-------------+---------------------------------+--------------+ +| :doc:`Lua2 ` | Yes | Yes | No | No | Yes | Yes | ``lua2`` | ++------------------------------------------------+--------+--------+-------+--------------+-------------+---------------------------------+--------------+ | :doc:`MyDNS ` | Yes | No | No | No | No | No | ``mydns`` | +------------------------------------------------+--------+--------+-------+--------------+-------------+---------------------------------+--------------+ | :doc:`OpenDBX ` | Yes | Yes | Yes | Yes | No | No | ``opendbx`` | @@ -53,6 +55,7 @@ These backends have :doc:`features unique ` to the generic SQL back geoip ldap lua + lua2 mydns opendbx oracle diff --git a/docs/backends/lua2.rst b/docs/backends/lua2.rst new file mode 100644 index 0000000000000..ba6ce11169844 --- /dev/null +++ b/docs/backends/lua2.rst @@ -0,0 +1,160 @@ +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, 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)``. + +API description (v1) +^^^^^^^^^^^^^^^^^^^^ + +``bool dns_dnssec`` +~~~~~~~~~~~~~~~~~~~ +If your script supports DNSSEC, set this to true. + +``dns_lookup(qtype, qname, domain_id)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Perform lookup of given resource record name and type. + +INPUT: + - string qtype - Type of queried resource record + - DNSName qname - Name of queried resource record + - int domain_id - ID of associated domain + +OUTPUT: + Expects a table which has rows of following tables: + - DNSName name - resource record name (can also be string) + - string type - type of resource record + - 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: +Return empty table 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 of: + - string account - Associated account of this domain (default: ) + - string kind - Domain kind (NATIVE,MASTER,SLAVE) (default: master) + - int id - Associated domain ID (default: -1) + - int last_check - UNIX timestamp of last check from master + - table of strings masters - Master servers for this domain (default: ) + - long notified_serial - Notified serial to slaves + - long serial - Current domain serial + +NOTES: +This function is **optional**. +masters, account, last_check, notified_serial are for master/slave interaction only. + +``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**. + +``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: + - table 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 of string keys with table 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 table of: + - 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: + Tuple of three DNSNames. Unhashed, before and after. + +NOTES: +This function is **required** if ``dns_dnssec`` is true. diff --git a/modules/Makefile.am b/modules/Makefile.am index fa231a03cafe8..a5da8a5a65e69 100644 --- a/modules/Makefile.am +++ b/modules/Makefile.am @@ -10,6 +10,7 @@ DIST_SUBDIRS = \ gsqlite3backend \ ldapbackend \ luabackend \ + lua2backend \ mydnsbackend \ opendbxbackend \ oraclebackend \ diff --git a/modules/lua2backend/Makefile.am b/modules/lua2backend/Makefile.am new file mode 100644 index 0000000000000..f002029f88952 --- /dev/null +++ b/modules/lua2backend/Makefile.am @@ -0,0 +1,13 @@ +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 \ + lua2api1.hh lua2api1.cc + +liblua2backend_la_LDFLAGS = -module -avoid-version +liblua2backend_la_LIBADD = $(LUA_LIBS) diff --git a/modules/lua2backend/OBJECTFILES b/modules/lua2backend/OBJECTFILES new file mode 100644 index 0000000000000..1bc2bac4b3472 --- /dev/null +++ b/modules/lua2backend/OBJECTFILES @@ -0,0 +1 @@ +lua2backend.lo lua2api1.lo diff --git a/modules/lua2backend/OBJECTLIBS b/modules/lua2backend/OBJECTLIBS new file mode 100644 index 0000000000000..9094a029cd708 --- /dev/null +++ b/modules/lua2backend/OBJECTLIBS @@ -0,0 +1 @@ +$(LUA_LIBS) diff --git a/modules/lua2backend/lua2api1.cc b/modules/lua2backend/lua2api1.cc new file mode 100644 index 0000000000000..f630e71a447c4 --- /dev/null +++ b/modules/lua2backend/lua2api1.cc @@ -0,0 +1,27 @@ +/* + * 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" + +Lua2BackendAPIv1::~Lua2BackendAPIv1(){} diff --git a/modules/lua2backend/lua2api1.hh b/modules/lua2backend/lua2api1.hh new file mode 100644 index 0000000000000..103885b17af3e --- /dev/null +++ b/modules/lua2backend/lua2api1.hh @@ -0,0 +1,353 @@ +/* + * 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_1_HH +#define LUA2API_1_HH 1 + +#include "boost/lexical_cast.hpp" +#include "boost/algorithm/string/join.hpp" +#include "pdns/arguments.hh" + +class Lua2BackendAPIv1 : public DNSBackend, AuthLua4 { +private: + typedef std::vector > > > > lookup_result_t; + typedef std::function lookup_call_t; + + typedef boost::variant list_result_t; + typedef std::function list_call_t; + + typedef vector > > > domaininfo_result_t; + typedef boost::variant get_domaininfo_result_t; + typedef vector > get_all_domains_result_t; + typedef std::function get_domaininfo_call_t; + typedef std::function get_all_domains_call_t; + + typedef vector > domain_metadata_result_t; + typedef boost::variant get_domain_metadata_result_t; + typedef boost::variant > > get_all_domain_metadata_result_t; + typedef std::function get_domain_metadata_call_t; + typedef std::function get_all_domain_metadata_call_t; + + typedef vector > > keydata_result_t; + typedef boost::variant > > get_domain_keys_result_t; + typedef std::function get_domain_keys_call_t; + + typedef std::tuple before_and_after_names_result_t; + typedef boost::variant get_before_and_after_names_absolute_result_t; + typedef std::function get_before_and_after_names_absolute_call_t; + + typedef std::function direct_backend_cmd_call_t; +public: + Lua2BackendAPIv1(const string& suffix) { + setArgPrefix("lua2"+suffix); + d_debug_log = mustDo("query-logging"); + prepareContext(); + loadFile(getArg("filename")); + } + + ~Lua2BackendAPIv1(); + + #define logCall(func, var) { if (d_debug_log) { L<readVariable>("dns_lookup").get_value_or(0); + f_list = d_lw->readVariable>("dns_list").get_value_or(0); + f_get_all_domains = d_lw->readVariable>("dns_get_all_domains").get_value_or(0); + f_get_domaininfo = d_lw->readVariable>("dns_get_domaininfo").get_value_or(0); + f_get_domain_metadata = d_lw->readVariable>("dns_get_domain_metadata").get_value_or(0); + f_get_all_domain_metadata = d_lw->readVariable>("dns_get_all_domain_metadata").get_value_or(0); + f_get_domain_keys = d_lw->readVariable>("dns_get_domain_keys").get_value_or(0); + f_get_before_and_after_names_absolute = d_lw->readVariable>("dns_get_before_and_after_names_absolute").get_value_or(0); + + if (f_lookup == nullptr) + throw PDNSException("dns_lookup missing"); + + /* see if dnssec support is wanted */ + d_dnssec = d_lw->readVariable>("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) + 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 */ + } + } + + bool doesDNSSEC() { + 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") + rec.qtype = boost::get(item.second); + else if (item.first == "name") { + if (item.second.which() == 3) + rec.qname == DNSName(boost::get(item.second)); + else if (item.second.which() == 2) + rec.qname = boost::get(item.second); + } else if (item.first == "domain_id") + rec.domain_id = boost::get(item.second); + else if (item.first == "auth") + rec.auth = boost::get(item.second); + else if (item.first == "last_modified") + rec.last_modified = static_cast(boost::get(item.second)); + else if (item.first == "ttl") + rec.ttl = boost::get(item.second); + else if (item.first == "content") + rec.setContent(boost::get(item.second)); + else if (item.first == "scopeMask") + rec.scopeMask = boost::get(item.second); + else + L<readVariable>(cmd).get_value_or(0); + if (f == nullptr) { + return cmd + "not found"; + } + logCall(cmd, "parameter="<(item.second); + else if (item.first == "last_check") + di.last_check = static_cast(boost::get(item.second)); + else if (item.first == "masters") + di.masters = boost::get>(item.second); + else if (item.first == "id") + di.id = static_cast(boost::get(item.second)); + else if (item.first == "notified_serial") + di.notified_serial = static_cast(boost::get(item.second)); + else if (item.first == "serial") + di.serial = static_cast(boost::get(item.second)); + else if (item.first == "kind") + di.kind = DomainInfo::stringToKind(boost::get(item.second)); + else + L<push_back(di); + } + } + + bool getAllDomainMetadata(const DNSName& name, std::map >& meta) override { + if (f_get_all_domain_metadata == nullptr) + return false; + + logCall("get_all_domain_metadata","name="< > >(result)) { + meta[row.first].clear(); + for(const auto& item: row.second) + meta[row.first].push_back(item.second); + logResult("kind="<& meta) override { + if (f_get_domain_metadata == nullptr) + return false; + + logCall("get_domain_metadata","name="<(result)) + meta.push_back(item.second); + + logResult("value="<& keys) override { + if (f_get_domain_keys == nullptr) + return false; + + logCall("get_domain_keys","name="< > >(result)) { + DNSBackend::KeyData key; + for(const auto& item: row.second) { + if (item.first == "content") + key.content = boost::get(item.second); + else if (item.first == "id") + key.id = static_cast(boost::get(item.second)); + else if (item.first == "flags") + key.flags = static_cast(boost::get(item.second)); + else if (item.first == "active") + key.active = boost::get(item.second); + else + L<(result); + unhashed = std::get<0>(row); + before = std::get<1>(row); + after = std::get<2>(row); + + logResult("unhashed="< 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; +}; + +#endif diff --git a/modules/lua2backend/lua2backend.cc b/modules/lua2backend/lua2backend.cc new file mode 100644 index 0000000000000..8ce7617dc8013 --- /dev/null +++ b/modules/lua2backend/lua2backend.cc @@ -0,0 +1,71 @@ +/* + * 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","1"); + } + + DNSBackend *make(const string &suffix="") + { + const std::string apiSet = "lua2" + suffix + "-api"; + const int api = ::arg().asNum(apiSet); + DNSBackend *be; + switch(api) { + case 1: + be = new Lua2BackendAPIv1(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; diff --git a/modules/lua2backend/lua2backend.hh b/modules/lua2backend/lua2backend.hh new file mode 100644 index 0000000000000..e93aa45118a5f --- /dev/null +++ b/modules/lua2backend/lua2backend.hh @@ -0,0 +1,30 @@ +/* + * 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 "lua2api1.hh" + +#endif