From 3bf2f94d0a282e46f7d39ad8860fece759c24cb4 Mon Sep 17 00:00:00 2001 From: Peter van Dijk Date: Fri, 23 Dec 2022 12:07:47 +0100 Subject: [PATCH] auth LUA: new dblookup() function --- .github/actions/spell-check/expect.txt | 1 + docs/lua-records/functions.rst | 17 +++++++++ pdns/lua-record.cc | 42 +++++++++++++++++++++ regression-tests.auth-py/test_LuaRecords.py | 21 +++++++++++ 4 files changed, 81 insertions(+) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 0465e6d06b..5a1b4066e0 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -257,6 +257,7 @@ Davids Dayneko dbfile dblfilename +dblookup dbpf dbr dcobject diff --git a/docs/lua-records/functions.rst b/docs/lua-records/functions.rst index 6902d761ad..7c6a22fec8 100644 --- a/docs/lua-records/functions.rst +++ b/docs/lua-records/functions.rst @@ -454,3 +454,20 @@ Helper functions Returns true if ``bestwho`` is within any of the listed subnets. :param [string] netmasks: The list of IP addresses to check against + +.. function:: dblookup(name, type) + + Does a database lookup for name and type, and returns a (possibly empty) array of string results. + + Please keep the following in mind: + + * it does not evaluate any LUA code found + * if you needed just one string, perhaps you want ``dblookup('www.example.org', 'A')[1]`` to take the first item from the array + * some things, like ifurlup, don't like empty tables, so be careful not to accidentally look up a name that does not have any records of that type, if you are going to use the result in ``ifurlup`` + + Example usage: :: + + www IN LUA A "ifurlup('https://www.example.com/', {dblookup('www1.example.com', 'A'), dblookup('www2.example.com', 'A'), dblookup('www3.example.com', 'A')})" + + :param string name: Name to look up in the database + :param string type: DNS type to look for diff --git a/pdns/lua-record.cc b/pdns/lua-record.cc index a9786ad950..920c0ccf4c 100644 --- a/pdns/lua-record.cc +++ b/pdns/lua-record.cc @@ -4,6 +4,7 @@ #include #include #include +#include "qtype.hh" #include "version.hh" #include "ext/luawrapper/include/LuaContext.hpp" #include "lock.hh" @@ -503,6 +504,17 @@ static std::vector lookup(const DNSName& name, uint16_t qtype, in return ret; } +static bool getAuth(const DNSName& name, uint16_t qtype, SOAData* soaData) +{ + static LockGuarded s_ub; + + vector ret; + { + auto ueback = s_ub.lock(); + return ueback->getAuth(name, qtype, soaData); + } +} + static std::string getOptionValue(const boost::optional>& options, const std::string &name, const std::string &defaultValue) { string selector=defaultValue; @@ -1103,6 +1115,36 @@ static void setupLuaRecords(LuaContext& lua) return result; }); + lua.writeFunction("dblookup", [](const string& record, const string& type) { + DNSName rec; + QType qtype; + vector ret; + try { + rec = DNSName(record); + qtype = type; + } + catch (const std::exception& e) { + g_log << Logger::Error << "DB lookup cannot be performed, the name (" << record << ") or type (" << type << ") is malformed: " << e.what() << endl; + return ret; + } + try { + SOAData soaData; + + if (!getAuth(rec, qtype, &soaData)) { + return ret; + } + + vector drs = lookup(rec, qtype, soaData.domain_id); + for (const auto& drec : drs) { + ret.push_back(drec.dr.getContent()->getZoneRepresentation()); + } + } + catch (std::exception& e) { + g_log << Logger::Error << "Failed to do DB lookup for " << rec << "/" << type << ": " << e.what() << endl; + } + return ret; + }); + lua.writeFunction("include", [&lua](string record) { DNSName rec; try { diff --git a/regression-tests.auth-py/test_LuaRecords.py b/regression-tests.auth-py/test_LuaRecords.py index e9df7c8ba7..0d6a3d725f 100644 --- a/regression-tests.auth-py/test_LuaRecords.py +++ b/regression-tests.auth-py/test_LuaRecords.py @@ -149,6 +149,9 @@ newcafromraw IN LUA A "newCAFromRaw('ABCD'):toString()" newcafromraw IN LUA AAAA "newCAFromRaw('ABCD020340506070'):toString()" counter IN LUA TXT ";counter = counter or 0 counter=counter+1 return tostring(counter)" + +lookmeup IN A 192.0.2.5 +dblookup IN LUA A "dblookup('lookmeup.example.org', 'A')[1]" """, 'createforward6.example.org': """ createforward6.example.org. 3600 IN SOA {soa} @@ -1025,6 +1028,24 @@ createforward6.example.org. 3600 IN NS ns2.example.org. self.assertEqual(len(resUDP), 1) self.assertEqual(len(resTCP), 1) + def testDblookup(self): + """ + Test dblookup() function + """ + + name = 'dblookup.example.org.' + + query = dns.message.make_query(name, 'A') + + response = dns.message.make_response(query) + + response.answer.append(dns.rrset.from_text(name, 0, dns.rdataclass.IN, 'A', '192.0.2.5')) + + res = self.sendUDPQuery(query) + self.assertRcodeEqual(res, dns.rcode.NOERROR) + self.assertEqual(self.sortRRsets(res.answer), self.sortRRsets(response.answer)) + + class TestLuaRecordsShared(TestLuaRecords): _config_template = """ geoip-database-files=../modules/geoipbackend/regression-tests/GeoLiteCity.mmdb -- 2.47.2