From: Remi Gacogne Date: Mon, 12 Mar 2018 10:39:45 +0000 (+0100) Subject: rec: Add a new Lua FFI hook, gettag-ffi X-Git-Tag: dnsdist-1.3.0~34^2~3 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=70fb28d91018e174998a024f6bc71f9963d4cd7e;p=pdns rec: Add a new Lua FFI hook, gettag-ffi --- diff --git a/pdns/lua-recursor4-ffi.hh b/pdns/lua-recursor4-ffi.hh new file mode 100644 index 000000000..64e5a433a --- /dev/null +++ b/pdns/lua-recursor4-ffi.hh @@ -0,0 +1,52 @@ +/* + * 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. + */ + +extern "C" { + typedef struct pdns_ffi_param pdns_ffi_param_t; + + typedef struct pdns_ednsoption { + uint16_t optionCode; + uint16_t len; + const void* data; + } pdns_ednsoption_t; + + const char* pdns_ffi_param_get_qname(pdns_ffi_param_t* ref); + uint16_t pdns_ffi_param_get_qtype(const pdns_ffi_param_t* ref); + const char* pdns_ffi_param_get_remote(pdns_ffi_param_t* ref); + uint16_t pdns_ffi_param_get_remote_port(const pdns_ffi_param_t* ref); + const char* pdns_ffi_param_get_local(pdns_ffi_param_t* ref); + uint16_t pdns_ffi_param_get_local_port(const pdns_ffi_param_t* ref); + const char* pdns_ffi_param_get_edns_cs(pdns_ffi_param_t* ref); + uint8_t pdns_ffi_param_get_edns_cs_source_mask(const pdns_ffi_param_t* ref); + + // returns the length of the resulting 'out' array. 'out' is not set if the length is 0 + size_t pdns_ffi_param_get_edns_options(pdns_ffi_param_t* ref, const pdns_ednsoption_t** out); + size_t pdns_ffi_param_get_edns_options_by_code(pdns_ffi_param_t* ref, uint16_t optionCode, const pdns_ednsoption_t** out); + + void pdns_ffi_param_set_tag(pdns_ffi_param_t* ref, unsigned int tag); + void pdns_ffi_param_add_policytag(pdns_ffi_param_t *ref, const char* name); + void pdns_ffi_param_set_requestorid(pdns_ffi_param_t* ref, const char* name); + void pdns_ffi_param_set_devicename(pdns_ffi_param_t* ref, const char* name); + void pdns_ffi_param_set_deviceid(pdns_ffi_param_t* ref, size_t len, const void* name); + void pdns_ffi_param_set_variable(pdns_ffi_param_t* ref, bool variable); + void pdns_ffi_param_set_ttl_cap(pdns_ffi_param_t* ref, uint32_t ttl); +} diff --git a/pdns/lua-recursor4.cc b/pdns/lua-recursor4.cc index e05e80e32..5c9a4cbe6 100644 --- a/pdns/lua-recursor4.cc +++ b/pdns/lua-recursor4.cc @@ -405,6 +405,7 @@ void RecursorLua4::postLoad() { d_ipfilter = d_lw->readVariable>("ipfilter").get_value_or(0); d_gettag = d_lw->readVariable>("gettag").get_value_or(0); + d_gettag_ffi = d_lw->readVariable>("gettag_ffi").get_value_or(0); } bool RecursorLua4::prerpz(DNSQuestion& dq, int& ret) @@ -479,6 +480,50 @@ unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& edn return 0; } +struct pdns_ffi_param +{ +public: + pdns_ffi_param(const DNSName& qname_, uint16_t qtype_, const ComboAddress& local_, const ComboAddress& remote_, const Netmask& ednssubnet_, std::vector& policyTags_, const std::map& ednsOptions_, std::string& requestorId_, std::string& deviceId_, uint32_t& ttlCap_, bool& variable_, bool tcp_): qname(qname_), local(local_), remote(remote_), ednssubnet(ednssubnet_), policyTags(policyTags_), ednsOptions(ednsOptions_), requestorId(requestorId_), deviceId(deviceId_), ttlCap(ttlCap_), variable(variable_), qtype(qtype_), tcp(tcp_) + { + } + + std::unique_ptr qnameStr{nullptr}; + std::unique_ptr localStr{nullptr}; + std::unique_ptr remoteStr{nullptr}; + std::unique_ptr ednssubnetStr{nullptr}; + std::vector ednsOptionsVect; + + const DNSName& qname; + const ComboAddress& local; + const ComboAddress& remote; + const Netmask& ednssubnet; + std::vector& policyTags; + const std::map& ednsOptions; + std::string& requestorId; + std::string& deviceId; + uint32_t& ttlCap; + bool& variable; + + unsigned int tag{0}; + uint16_t qtype; + bool tcp; +}; + +unsigned int RecursorLua4::gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector* policyTags, LuaContext::LuaObject& data, const std::map& ednsOptions, bool tcp, std::string& requestorId, std::string& deviceId, uint32_t& ttlCap, bool& variable) +{ + if (d_gettag_ffi) { + pdns_ffi_param_t param(qname, qtype, local, remote, ednssubnet, *policyTags, ednsOptions, requestorId, deviceId, ttlCap, variable, tcp); + + auto ret = d_gettag_ffi(¶m); + if (ret) { + data = *ret; + } + + return param.tag; + } + return 0; +} + bool RecursorLua4::genhook(luacall_t& func, DNSQuestion& dq, int& ret) { if(!func) @@ -538,3 +583,146 @@ loop:; } RecursorLua4::~RecursorLua4(){} + +const char* pdns_ffi_param_get_qname(pdns_ffi_param_t* ref) +{ + if (!ref->qnameStr) { + ref->qnameStr = std::unique_ptr(new std::string(ref->qname.toStringNoDot())); + } + + return ref->qnameStr->c_str(); +} + +uint16_t pdns_ffi_param_get_qtype(const pdns_ffi_param_t* ref) +{ + return ref->qtype; +} + +const char* pdns_ffi_param_get_remote(pdns_ffi_param_t* ref) +{ + if (!ref->remoteStr) { + ref->remoteStr = std::unique_ptr(new std::string(ref->remote.toString())); + } + + return ref->remoteStr->c_str(); +} + +uint16_t pdns_ffi_param_get_remote_port(const pdns_ffi_param_t* ref) +{ + return ref->remote.getPort(); +} + +const char* pdns_ffi_param_get_local(pdns_ffi_param_t* ref) +{ + if (!ref->localStr) { + ref->localStr = std::unique_ptr(new std::string(ref->local.toString())); + } + + return ref->localStr->c_str(); +} + +uint16_t pdns_ffi_param_get_local_port(const pdns_ffi_param_t* ref) +{ + return ref->local.getPort(); +} + +const char* pdns_ffi_param_get_edns_cs(pdns_ffi_param_t* ref) +{ + if (ref->ednssubnet.empty()) { + return nullptr; + } + + if (!ref->ednssubnetStr) { + ref->ednssubnetStr = std::unique_ptr(new std::string(ref->ednssubnet.toStringNoMask())); + } + + return ref->ednssubnetStr->c_str(); +} + +uint8_t pdns_ffi_param_get_edns_cs_source_mask(const pdns_ffi_param_t* ref) +{ + return ref->ednssubnet.getBits(); +} + +static void fill_edns_option(const EDNSOptionView& view, pdns_ednsoption_t& option) +{ + option.len = view.size; + option.data = nullptr; + + if (view.size > 0) { + option.data = view.content; + } +} + +size_t pdns_ffi_param_get_edns_options(pdns_ffi_param_t* ref, const pdns_ednsoption_t** out) +{ + if (ref->ednsOptions.empty()) { + return 0; + } + + size_t count = ref->ednsOptions.size(); + ref->ednsOptionsVect.resize(count); + + size_t pos = 0; + for (const auto& entry : ref->ednsOptions) { + fill_edns_option(entry.second, ref->ednsOptionsVect.at(pos)); + ref->ednsOptionsVect.at(pos).optionCode = entry.first; + pos++; + } + + *out = ref->ednsOptionsVect.data(); + + return count; +} + +size_t pdns_ffi_param_get_edns_options_by_code(pdns_ffi_param_t* ref, uint16_t optionCode, const pdns_ednsoption_t** out) +{ + const auto& it = ref->ednsOptions.find(optionCode); + if (it == ref->ednsOptions.cend()) { + return 0; + } + + /* the current code deals with only one entry per code, but we will fix that */ + ref->ednsOptionsVect.resize(1); + fill_edns_option(it->second, ref->ednsOptionsVect.at(0)); + ref->ednsOptionsVect.at(0).optionCode = it->first; + + *out = ref->ednsOptionsVect.data(); + + return 1; +} + +void pdns_ffi_param_set_tag(pdns_ffi_param_t* ref, unsigned int tag) +{ + ref->tag = tag; +} + +void pdns_ffi_param_add_policytag(pdns_ffi_param_t *ref, const char* name) +{ + ref->policyTags.push_back(std::string(name)); +} + +void pdns_ffi_param_set_requestorid(pdns_ffi_param_t* ref, const char* name) +{ + ref->requestorId = std::string(name); +} + +void pdns_ffi_param_set_devicename(pdns_ffi_param_t* ref, const char* name) +{ + ref->deviceId = std::string(name); +} + +void pdns_ffi_param_set_deviceid(pdns_ffi_param_t* ref, size_t len, const void* name) +{ + ref->deviceId = std::string(reinterpret_cast(name), len); +} + +void pdns_ffi_param_set_variable(pdns_ffi_param_t* ref, bool variable) +{ + ref->variable = variable; +} + +void pdns_ffi_param_set_ttl_cap(pdns_ffi_param_t* ref, uint32_t ttl) +{ + ref->ttlCap = ttl; +} diff --git a/pdns/lua-recursor4.hh b/pdns/lua-recursor4.hh index 44451f5f1..6f6308314 100644 --- a/pdns/lua-recursor4.hh +++ b/pdns/lua-recursor4.hh @@ -35,6 +35,8 @@ #include "lua-base4.hh" #include +#include "lua-recursor4-ffi.hh" + string GenUDPQueryResponse(const ComboAddress& dest, const string& query); unsigned int getRecursorThreadId(); @@ -96,6 +98,7 @@ public: }; unsigned int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector* policyTags, LuaContext::LuaObject& data, const std::map&, bool tcp, std::string& requestorId, std::string& deviceId); + unsigned int gettag_ffi(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector* policyTags, LuaContext::LuaObject& data, const std::map&, bool tcp, std::string& requestorId, std::string& deviceId, uint32_t& ttlCap, bool& variable); bool prerpz(DNSQuestion& dq, int& ret); bool preresolve(DNSQuestion& dq, int& ret); @@ -117,6 +120,9 @@ public: typedef std::function >,boost::optional,boost::optional,boost::optional >(ComboAddress, Netmask, ComboAddress, DNSName, uint16_t, const std::map&, bool)> gettag_t; gettag_t d_gettag; // public so you can query if we have this hooked + typedef std::function(pdns_ffi_param_t*)> gettag_ffi_t; + gettag_ffi_t d_gettag_ffi; + protected: virtual void postPrepareContext() override; virtual void postLoad() override; diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index 60fc1ccab..15486f3e0 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -250,6 +250,8 @@ struct DNSComboWriter { vector > d_ednsOpts; std::vector d_policyTags; LuaContext::LuaObject d_data; + uint32_t d_ttlCap{std::numeric_limits::max()}; + bool d_variable{false}; }; MT_t* getMT() @@ -741,9 +743,9 @@ static void handleRPZCustom(const DNSRecord& spoofed, const QType& qtype, SyncRe } } -static bool addRecordToPacket(DNSPacketWriter& pw, const DNSRecord& rec, uint32_t& minTTL, const uint16_t maxAnswerSize) +static bool addRecordToPacket(DNSPacketWriter& pw, const DNSRecord& rec, uint32_t& minTTL, uint32_t ttlCap, const uint16_t maxAnswerSize) { - pw.startRecord(rec.d_name, rec.d_type, rec.d_ttl, rec.d_class, rec.d_place); + pw.startRecord(rec.d_name, rec.d_type, (rec.d_ttl > ttlCap ? ttlCap : rec.d_ttl), rec.d_class, rec.d_place); if(rec.d_type != QType::OPT) // their TTL ain't real minTTL = min(minTTL, rec.d_ttl); @@ -819,7 +821,11 @@ static void startDoResolve(void *p) pw.getHeader()->rd=dc->d_mdp.d_header.rd; pw.getHeader()->cd=dc->d_mdp.d_header.cd; - uint32_t minTTL=std::numeric_limits::max(); + /* This is the lowest TTL seen in the records of the response, + so we can't cache it for longer than this value. + If we have a TTL cap, this value can't be larger than the + cap no matter what. */ + uint32_t minTTL = dc->d_ttlCap; SyncRes sr(dc->d_now); @@ -848,7 +854,7 @@ static void startDoResolve(void *p) sr.setQuerySource(dc->d_remote, g_useIncomingECS && !dc->d_ednssubnet.source.empty() ? boost::optional(dc->d_ednssubnet) : boost::none); bool tracedQuery=false; // we could consider letting Lua know about this too - bool variableAnswer = false; + bool variableAnswer = dc->d_variable; bool shouldNotValidate = false; /* preresolve expects res (dq.rcode) to be set to RCode::NoError by default */ @@ -1164,7 +1170,7 @@ static void startDoResolve(void *p) continue; } - if (!addRecordToPacket(pw, *i, minTTL, maxanswersize)) { + if (!addRecordToPacket(pw, *i, minTTL, dc->d_ttlCap, maxanswersize)) { needCommit = false; break; } @@ -1188,7 +1194,7 @@ static void startDoResolve(void *p) OPT record. This MUST also occur when a truncated response (using the DNS header's TC bit) is returned." */ - if (addRecordToPacket(pw, makeOpt(edo.d_packetsize, 0, edo.d_Z), minTTL, maxanswersize)) { + if (addRecordToPacket(pw, makeOpt(edo.d_packetsize, 0, edo.d_Z), minTTL, dc->d_ttlCap, maxanswersize)) { pw.commit(); } } @@ -1222,6 +1228,7 @@ static void startDoResolve(void *p) } if(sendmsg(dc->d_socket, &msgh, 0) < 0 && g_logCommonErrors) L<getRemote()<<" failed with: "<insertResponsePacket(dc->d_tag, dc->d_qhash, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_mdp.d_qclass, string((const char*)&*packet.begin(), packet.size()), @@ -1545,7 +1552,7 @@ static void handleRunningTCPQuestion(int fd, FDMultiplexer::funcparam_t& var) } #endif - if(needECS || needXPF || (t_pdl && t_pdl->d_gettag)) { + if(needECS || needXPF || (t_pdl && (t_pdl->d_gettag_ffi || t_pdl->d_gettag))) { try { std::map ednsOptions; @@ -1556,17 +1563,22 @@ static void handleRunningTCPQuestion(int fd, FDMultiplexer::funcparam_t& var) dc->d_ecsFound, &dc->d_ednssubnet, g_gettagNeedsEDNSOptions ? &ednsOptions : nullptr, xpfFound, needXPF ? &dc->d_source : nullptr, needXPF ? &dc->d_destination : nullptr); - if(t_pdl && t_pdl->d_gettag) { + if(t_pdl) { try { - dc->d_tag = t_pdl->gettag(dc->d_source, dc->d_ednssubnet.source, dc->d_destination, qname, qtype, &dc->d_policyTags, dc->d_data, ednsOptions, true, requestorId, deviceId); + if (t_pdl->d_gettag_ffi) { + dc->d_tag = t_pdl->gettag_ffi(dc->d_source, dc->d_ednssubnet.source, dc->d_destination, qname, qtype, &dc->d_policyTags, dc->d_data, ednsOptions, true, requestorId, deviceId, dc->d_ttlCap, dc->d_variable); + } + else if (t_pdl->d_gettag) { + dc->d_tag = t_pdl->gettag(dc->d_source, dc->d_ednssubnet.source, dc->d_destination, qname, qtype, &dc->d_policyTags, dc->d_data, ednsOptions, true, requestorId, deviceId); + } } - catch(std::exception& e) { + catch(const std::exception& e) { if(g_logCommonErrors) L<::max(); + bool variable = false; try { DNSName qname; uint16_t qtype=0; @@ -1729,7 +1743,7 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr */ #endif - if(needECS || needXPF || (t_pdl && t_pdl->d_gettag)) { + if(needECS || needXPF || (t_pdl && (t_pdl->d_gettag || t_pdl->d_gettag_ffi))) { try { std::map ednsOptions; bool xpfFound = false; @@ -1743,17 +1757,22 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr qnameParsed = true; ecsParsed = true; - if(t_pdl && t_pdl->d_gettag) { + if(t_pdl) { try { - ctag=t_pdl->gettag(source, ednssubnet.source, destination, qname, qtype, &policyTags, data, ednsOptions, false, requestorId, deviceId); + if (t_pdl->d_gettag_ffi) { + ctag = t_pdl->gettag_ffi(source, ednssubnet.source, destination, qname, qtype, &policyTags, data, ednsOptions, false, requestorId, deviceId, ttlCap, variable); + } + else if (t_pdl->d_gettag) { + ctag = t_pdl->gettag(source, ednssubnet.source, destination, qname, qtype, &policyTags, data, ednsOptions, false, requestorId, deviceId); + } } - catch(std::exception& e) { + catch(const std::exception& e) { if(g_logCommonErrors) L<getResponsePacket(ctag, question, qname, qtype, qclass, g_now.tv_sec, &response, &age, &qhash, &pbMessage)); } @@ -1855,6 +1877,8 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr dc->d_ecsFound = ecsFound; dc->d_ecsParsed = ecsParsed; dc->d_ednssubnet = ednssubnet; + dc->d_ttlCap = ttlCap; + dc->d_variable = variable; #ifdef HAVE_PROTOBUF if (luaconfsLocal->protobufServer || luaconfsLocal->outgoingProtobufServer) { dc->d_uuid = uniqueId; diff --git a/pdns/recursordist/Makefile.am b/pdns/recursordist/Makefile.am index 1cce535d8..e9ec0ee57 100644 --- a/pdns/recursordist/Makefile.am +++ b/pdns/recursordist/Makefile.am @@ -112,6 +112,7 @@ pdns_recursor_SOURCES = \ logger.hh logger.cc \ lua-base4.cc lua-base4.hh \ lua-recursor4.cc lua-recursor4.hh \ + lua-recursor4-ffi.hh \ lwres.cc lwres.hh \ misc.hh misc.cc \ mplexer.hh \ diff --git a/pdns/recursordist/lua-recursor4-ffi.hh b/pdns/recursordist/lua-recursor4-ffi.hh new file mode 120000 index 000000000..5e234608c --- /dev/null +++ b/pdns/recursordist/lua-recursor4-ffi.hh @@ -0,0 +1 @@ +../lua-recursor4-ffi.hh \ No newline at end of file