--- /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.
+ */
+
+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);
+}
d_ipfilter = d_lw->readVariable<boost::optional<ipfilter_t>>("ipfilter").get_value_or(0);
d_gettag = d_lw->readVariable<boost::optional<gettag_t>>("gettag").get_value_or(0);
+ d_gettag_ffi = d_lw->readVariable<boost::optional<gettag_ffi_t>>("gettag_ffi").get_value_or(0);
}
bool RecursorLua4::prerpz(DNSQuestion& dq, int& ret)
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<std::string>& policyTags_, const std::map<uint16_t, EDNSOptionView>& 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<std::string> qnameStr{nullptr};
+ std::unique_ptr<std::string> localStr{nullptr};
+ std::unique_ptr<std::string> remoteStr{nullptr};
+ std::unique_ptr<std::string> ednssubnetStr{nullptr};
+ std::vector<pdns_ednsoption_t> ednsOptionsVect;
+
+ const DNSName& qname;
+ const ComboAddress& local;
+ const ComboAddress& remote;
+ const Netmask& ednssubnet;
+ std::vector<std::string>& policyTags;
+ const std::map<uint16_t, EDNSOptionView>& 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<std::string>* policyTags, LuaContext::LuaObject& data, const std::map<uint16_t, EDNSOptionView>& 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)
}
RecursorLua4::~RecursorLua4(){}
+
+const char* pdns_ffi_param_get_qname(pdns_ffi_param_t* ref)
+{
+ if (!ref->qnameStr) {
+ ref->qnameStr = std::unique_ptr<std::string>(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<std::string>(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<std::string>(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<std::string>(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<const char*>(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;
+}
#include "lua-base4.hh"
#include <unordered_map>
+#include "lua-recursor4-ffi.hh"
+
string GenUDPQueryResponse(const ComboAddress& dest, const string& query);
unsigned int getRecursorThreadId();
};
unsigned int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags, LuaContext::LuaObject& data, const std::map<uint16_t, EDNSOptionView>&, 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<std::string>* policyTags, LuaContext::LuaObject& data, const std::map<uint16_t, EDNSOptionView>&, 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);
typedef std::function<std::tuple<unsigned int,boost::optional<std::unordered_map<int,string> >,boost::optional<LuaContext::LuaObject>,boost::optional<std::string>,boost::optional<std::string> >(ComboAddress, Netmask, ComboAddress, DNSName, uint16_t, const std::map<uint16_t, EDNSOptionView>&, bool)> gettag_t;
gettag_t d_gettag; // public so you can query if we have this hooked
+ typedef std::function<boost::optional<LuaContext::LuaObject>(pdns_ffi_param_t*)> gettag_ffi_t;
+ gettag_ffi_t d_gettag_ffi;
+
protected:
virtual void postPrepareContext() override;
virtual void postLoad() override;
vector<pair<uint16_t, string> > d_ednsOpts;
std::vector<std::string> d_policyTags;
LuaContext::LuaObject d_data;
+ uint32_t d_ttlCap{std::numeric_limits<uint32_t>::max()};
+ bool d_variable{false};
};
MT_t* getMT()
}
}
-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);
pw.getHeader()->rd=dc->d_mdp.d_header.rd;
pw.getHeader()->cd=dc->d_mdp.d_header.cd;
- uint32_t minTTL=std::numeric_limits<uint32_t>::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);
sr.setQuerySource(dc->d_remote, g_useIncomingECS && !dc->d_ednssubnet.source.empty() ? boost::optional<const EDNSSubnetOpts&>(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 */
continue;
}
- if (!addRecordToPacket(pw, *i, minTTL, maxanswersize)) {
+ if (!addRecordToPacket(pw, *i, minTTL, dc->d_ttlCap, maxanswersize)) {
needCommit = false;
break;
}
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();
}
}
}
if(sendmsg(dc->d_socket, &msgh, 0) < 0 && g_logCommonErrors)
L<<Logger::Warning<<"Sending UDP reply to client "<<dc->getRemote()<<" failed with: "<<strerror(errno)<<endl;
+
if(!SyncRes::s_nopacketcache && !variableAnswer && !sr.wasVariable() ) {
t_packetCache->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()),
}
#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<uint16_t, EDNSOptionView> ednsOptions;
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<<Logger::Warning<<"Error parsing a query packet qname='"<<qname<<"' for tag determination, setting tag=0: "<<e.what()<<endl;
}
}
}
- catch(std::exception& e)
+ catch(const std::exception& e)
{
if(g_logCommonErrors)
L<<Logger::Warning<<"Error parsing a query packet for tag determination, setting tag=0: "<<e.what()<<endl;
EDNSSubnetOpts ednssubnet;
bool ecsFound = false;
bool ecsParsed = false;
+ uint32_t ttlCap = std::numeric_limits<uint32_t>::max();
+ bool variable = false;
try {
DNSName qname;
uint16_t qtype=0;
*/
#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<uint16_t, EDNSOptionView> ednsOptions;
bool xpfFound = false;
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<<Logger::Warning<<"Error parsing a query packet qname='"<<qname<<"' for tag determination, setting tag=0: "<<e.what()<<endl;
}
}
}
- catch(std::exception& e)
+ catch(const std::exception& e)
{
if(g_logCommonErrors)
L<<Logger::Warning<<"Error parsing a query packet for tag determination, setting tag=0: "<<e.what()<<endl;
}
#endif /* HAVE_PROTOBUF */
+ /* It might seem like a good idea to skip the packet cache lookup if we know that the answer is not cacheable,
+ but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
+ as cacheable we would cache it with a wrong tag, so better safe than sorry. */
if (qnameParsed) {
cacheHit = (!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(ctag, question, qname, qtype, qclass, g_now.tv_sec, &response, &age, &qhash, &pbMessage));
}
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;
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 \
--- /dev/null
+../lua-recursor4-ffi.hh
\ No newline at end of file