{ "LogAction", true, "[filename], [binary], [append], [buffered]", "Log a line for each query, to the specified file if any, to the console (require verbose) otherwise. When logging to a file, the `binary` optional parameter specifies whether we log in binary form (default) or in textual form, the `append` optional parameter specifies whether we open the file for appending or truncate each time (default), and the `buffered` optional parameter specifies whether writes to the file are buffered (default) or not." },
{ "LogResponseAction", true, "[filename], [append], [buffered]", "Log a line for each response, to the specified file if any, to the console (require verbose) otherwise. The `append` optional parameter specifies whether we open the file for appending or truncate each time (default), and the `buffered` optional parameter specifies whether writes to the file are buffered (default) or not." },
{ "LuaAction", true, "function", "Invoke a Lua function that accepts a DNSQuestion" },
+ { "LuaFFIAction", true, "function", "Invoke a Lua FFI function that accepts a DNSQuestion" },
+ { "LuaFFIResponseAction", true, "function", "Invoke a Lua FFI function that accepts a DNSResponse" },
{ "LuaResponseAction", true, "function", "Invoke a Lua function that accepts a DNSResponse" },
{ "MacAddrAction", true, "option", "Add the source MAC address to the query as EDNS0 option option. This action is currently only supported on Linux. Subsequent rules are processed after this action" },
{ "makeIPCipherKey", true, "password", "generates a 16-byte key that can be used to pseudonymize IP addresses with IP cipher" },
#include "dnsdist.hh"
#include "dnsdist-ecs.hh"
#include "dnsdist-lua.hh"
+#include "dnsdist-lua-ffi.hh"
#include "dnsdist-protobuf.hh"
#include "dnsdist-kvs.hh"
}
};
-DNSAction::Action LuaAction::operator()(DNSQuestion* dq, std::string* ruleresult) const
+class LuaAction : public DNSAction
{
- std::lock_guard<std::mutex> lock(g_luamutex);
- try {
- auto ret = d_func(dq);
- if (ruleresult) {
- if (boost::optional<std::string> rule = std::get<1>(ret)) {
- *ruleresult = *rule;
+public:
+ typedef std::function<std::tuple<int, boost::optional<string> >(DNSQuestion* dq)> func_t;
+ LuaAction(const LuaAction::func_t& func) : d_func(func)
+ {}
+
+ DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
+ {
+ std::lock_guard<std::mutex> lock(g_luamutex);
+ try {
+ auto ret = d_func(dq);
+ if (ruleresult) {
+ if (boost::optional<std::string> rule = std::get<1>(ret)) {
+ *ruleresult = *rule;
+ }
+ else {
+ // default to empty string
+ ruleresult->clear();
+ }
}
- else {
- // default to empty string
- ruleresult->clear();
+ return static_cast<Action>(std::get<0>(ret));
+ } catch (const std::exception &e) {
+ warnlog("LuaAction failed inside Lua, returning ServFail: %s", e.what());
+ } catch (...) {
+ warnlog("LuaAction failed inside Lua, returning ServFail: [unknown exception]");
+ }
+ return DNSAction::Action::ServFail;
+ }
+
+ string toString() const override
+ {
+ return "Lua script";
+ }
+private:
+ func_t d_func;
+};
+
+class LuaResponseAction : public DNSResponseAction
+{
+public:
+ typedef std::function<std::tuple<int, boost::optional<string> >(DNSResponse* dr)> func_t;
+ LuaResponseAction(const LuaResponseAction::func_t& func) : d_func(func)
+ {}
+ DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
+ {
+ std::lock_guard<std::mutex> lock(g_luamutex);
+ try {
+ auto ret = d_func(dr);
+ if(ruleresult) {
+ if (boost::optional<std::string> rule = std::get<1>(ret)) {
+ *ruleresult = *rule;
+ }
+ else {
+ // default to empty string
+ ruleresult->clear();
+ }
}
+ return static_cast<Action>(std::get<0>(ret));
+ } catch (const std::exception &e) {
+ warnlog("LuaResponseAction failed inside Lua, returning ServFail: %s", e.what());
+ } catch (...) {
+ warnlog("LuaResponseAction failed inside Lua, returning ServFail: [unknown exception]");
}
- return (Action)std::get<0>(ret);
- } catch (std::exception &e) {
- warnlog("LuaAction failed inside lua, returning ServFail: %s", e.what());
- } catch (...) {
- warnlog("LuaAction failed inside lua, returning ServFail: [unknown exception]");
+ return DNSResponseAction::Action::ServFail;
}
- return DNSAction::Action::ServFail;
-}
-DNSResponseAction::Action LuaResponseAction::operator()(DNSResponse* dr, std::string* ruleresult) const
+ string toString() const override
+ {
+ return "Lua response script";
+ }
+private:
+ func_t d_func;
+};
+
+class LuaFFIAction: public DNSAction
{
- std::lock_guard<std::mutex> lock(g_luamutex);
- try {
- auto ret = d_func(dr);
- if(ruleresult) {
- if (boost::optional<std::string> rule = std::get<1>(ret)) {
- *ruleresult = *rule;
+public:
+ typedef std::function<int(dnsdist_ffi_dnsquestion_t* dq)> func_t;
+
+ LuaFFIAction(const LuaFFIAction::func_t& func): d_func(func)
+ {
+ }
+
+ DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
+ {
+ dnsdist_ffi_dnsquestion_t dqffi(dq);
+ try {
+ std::lock_guard<std::mutex> lock(g_luamutex);
+
+ auto ret = d_func(&dqffi);
+ if (ruleresult) {
+ if (dqffi.result) {
+ *ruleresult = *dqffi.result;
+ }
+ else {
+ // default to empty string
+ ruleresult->clear();
+ }
}
- else {
- // default to empty string
- ruleresult->clear();
+ return static_cast<DNSAction::Action>(ret);
+ } catch (const std::exception &e) {
+ warnlog("LuaFFIAction failed inside Lua, returning ServFail: %s", e.what());
+ } catch (...) {
+ warnlog("LuaFFIAction failed inside Lua, returning ServFail: [unknown exception]");
+ }
+ return DNSAction::Action::ServFail;
+ }
+
+ string toString() const override
+ {
+ return "Lua FFI script";
+ }
+private:
+ func_t d_func;
+};
+
+
+class LuaFFIResponseAction: public DNSResponseAction
+{
+public:
+ typedef std::function<int(dnsdist_ffi_dnsquestion_t* dq)> func_t;
+
+ LuaFFIResponseAction(const LuaFFIResponseAction::func_t& func): d_func(func)
+ {
+ }
+
+ DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
+ {
+ DNSQuestion* dq = dynamic_cast<DNSQuestion*>(dr);
+ if (dq == nullptr) {
+ return DNSResponseAction::Action::ServFail;
+ }
+
+ dnsdist_ffi_dnsquestion_t dqffi(dq);
+ try {
+ std::lock_guard<std::mutex> lock(g_luamutex);
+
+ auto ret = d_func(&dqffi);
+ if (ruleresult) {
+ if (dqffi.result) {
+ *ruleresult = *dqffi.result;
+ }
+ else {
+ // default to empty string
+ ruleresult->clear();
+ }
}
+ return static_cast<DNSResponseAction::Action>(ret);
+ } catch (const std::exception &e) {
+ warnlog("LuaFFIResponseAction failed inside Lua, returning ServFail: %s", e.what());
+ } catch (...) {
+ warnlog("LuaFFIResponseAction failed inside Lua, returning ServFail: [unknown exception]");
}
- return (Action)std::get<0>(ret);
- } catch (std::exception &e) {
- warnlog("LuaResponseAction failed inside lua, returning ServFail: %s", e.what());
- } catch (...) {
- warnlog("LuaResponseAction failed inside lua, returning ServFail: [unknown exception]");
+ return DNSResponseAction::Action::ServFail;
}
- return DNSResponseAction::Action::ServFail;
-}
+
+ string toString() const override
+ {
+ return "Lua FFI script";
+ }
+private:
+ func_t d_func;
+};
DNSAction::Action SpoofAction::operator()(DNSQuestion* dq, std::string* ruleresult) const
{
return std::shared_ptr<DNSAction>(new LuaAction(func));
});
+ g_lua.writeFunction("LuaFFIAction", [](LuaFFIAction::func_t func) {
+ setLuaSideEffect();
+ return std::shared_ptr<DNSAction>(new LuaFFIAction(func));
+ });
+
g_lua.writeFunction("NoRecurseAction", []() {
return std::shared_ptr<DNSAction>(new NoRecurseAction);
});
return std::shared_ptr<DNSResponseAction>(new LuaResponseAction(func));
});
+ g_lua.writeFunction("LuaFFIResponseAction", [](LuaFFIResponseAction::func_t func) {
+ setLuaSideEffect();
+ return std::shared_ptr<DNSResponseAction>(new LuaFFIResponseAction(func));
+ });
+
g_lua.writeFunction("RemoteLogAction", [](std::shared_ptr<RemoteLoggerInterface> logger, boost::optional<std::function<void(DNSQuestion*, DNSDistProtoBufMessage*)> > alterFunc, boost::optional<std::unordered_map<std::string, std::string>> vars) {
if (logger) {
// avoids potentially-evaluated-expression warning with clang.
g_lua.writeFunction("KeyValueStoreLookupRule", [](std::shared_ptr<KeyValueStore>& kvs, std::shared_ptr<KeyValueLookupKey>& lookupKey) {
return std::shared_ptr<DNSRule>(new KeyValueStoreLookupRule(kvs, lookupKey));
});
+
+ g_lua.writeFunction("LuaFFIRule", [](LuaFFIRule::func_t func) {
+ return std::shared_ptr<DNSRule>(new LuaFFIRule(func));
+ });
}
};
void setResponseHeadersFromConfig(dnsheader& dh, const ResponseConfig& config);
-class LuaAction : public DNSAction
-{
-public:
- typedef std::function<std::tuple<int, boost::optional<string> >(DNSQuestion* dq)> func_t;
- LuaAction(const LuaAction::func_t& func) : d_func(func)
- {}
- Action operator()(DNSQuestion* dq, string* ruleresult) const override;
- string toString() const override
- {
- return "Lua script";
- }
-private:
- func_t d_func;
-};
-
-class LuaResponseAction : public DNSResponseAction
-{
-public:
- typedef std::function<std::tuple<int, boost::optional<string> >(DNSResponse* dr)> func_t;
- LuaResponseAction(const LuaResponseAction::func_t& func) : d_func(func)
- {}
- Action operator()(DNSResponse* dr, string* ruleresult) const override;
- string toString() const override
- {
- return "Lua response script";
- }
-private:
- func_t d_func;
-};
-
class SpoofAction : public DNSAction
{
public:
dnsdist-lua-bindings-kvs.cc \
dnsdist-lua-bindings-packetcache.cc \
dnsdist-lua-bindings-protobuf.cc \
+ dnsdist-lua-ffi.cc dnsdist-lua-ffi.hh \
dnsdist-lua-inspection.cc \
dnsdist-lua-inspection-ffi.cc dnsdist-lua-inspection-ffi.hh \
dnsdist-lua-rules.cc \
--- /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.
+ */
+
+#include "dnsdist-lua-ffi.hh"
+#include "dnsdist-ecs.hh"
+
+uint16_t dnsdist_ffi_dnsquestion_get_qtype(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ return dq->dq->qtype;
+}
+
+uint16_t dnsdist_ffi_dnsquestion_get_qclass(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ return dq->dq->qclass;
+}
+
+static void dnsdist_ffi_comboaddress_to_raw(const ComboAddress& ca, const void** addr, size_t* addrSize)
+{
+ if (ca.isIPv4()) {
+ *addr = &ca.sin4.sin_addr.s_addr;
+ *addrSize = sizeof(ca.sin4.sin_addr.s_addr);
+ }
+ else {
+ *addr = &ca.sin6.sin6_addr.s6_addr;
+ *addrSize = sizeof(ca.sin6.sin6_addr.s6_addr);
+ }
+}
+
+void dnsdist_ffi_dnsquestion_get_localaddr(const dnsdist_ffi_dnsquestion_t* dq, const void** addr, size_t* addrSize)
+{
+ dnsdist_ffi_comboaddress_to_raw(*dq->dq->local, addr, addrSize);
+}
+
+void dnsdist_ffi_dnsquestion_get_remoteaddr(const dnsdist_ffi_dnsquestion_t* dq, const void** addr, size_t* addrSize)
+{
+ dnsdist_ffi_comboaddress_to_raw(*dq->dq->remote, addr, addrSize);
+}
+
+void dnsdist_ffi_dnsquestion_get_qname_raw(const dnsdist_ffi_dnsquestion_t* dq, const char** qname, size_t* qnameSize)
+{
+ const auto& storage = dq->dq->qname->getStorage();
+ *qname = storage.data();
+ *qnameSize = storage.size();
+}
+
+int dnsdist_ffi_dnsquestion_get_rcode(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ return dq->dq->dh->rcode;
+}
+
+void* dnsdist_ffi_dnsquestion_get_header(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ return dq->dq->dh;
+}
+
+uint16_t dnsdist_ffi_dnsquestion_get_len(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ return dq->dq->len;
+}
+
+size_t dnsdist_ffi_dnsquestion_get_size(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ return dq->dq->size;
+}
+
+uint8_t dnsdist_ffi_dnsquestion_get_opcode(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ return dq->dq->dh->opcode;
+}
+
+bool dnsdist_ffi_dnsquestion_get_tcp(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ return dq->dq->tcp;
+}
+
+bool dnsdist_ffi_dnsquestion_get_skip_cache(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ return dq->dq->skipCache;
+}
+
+bool dnsdist_ffi_dnsquestion_get_use_ecs(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ return dq->dq->useECS;
+}
+
+bool dnsdist_ffi_dnsquestion_get_add_xpf(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ return dq->dq->addXPF;
+}
+
+bool dnsdist_ffi_dnsquestion_get_ecs_override(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ return dq->dq->ecsOverride;
+}
+
+uint16_t dnsdist_ffi_dnsquestion_get_ecs_prefix_length(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ return dq->dq->ecsPrefixLength;
+}
+
+bool dnsdist_ffi_dnsquestion_is_temp_failure_ttl_set(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ return dq->dq->tempFailureTTL != boost::none;
+}
+
+uint32_t dnsdist_ffi_dnsquestion_get_temp_failure_ttl(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ if (dq->dq->tempFailureTTL) {
+ return *dq->dq->tempFailureTTL;
+ }
+ return 0;
+}
+
+bool dnsdist_ffi_dnsquestion_get_do(const dnsdist_ffi_dnsquestion_t* dq)
+{
+ return getEDNSZ(*dq->dq) & EDNS_HEADER_FLAG_DO;
+}
+
+void dnsdist_ffi_dnsquestion_get_sni(const dnsdist_ffi_dnsquestion_t* dq, const char** sni, size_t* sniSize)
+{
+ *sniSize = dq->dq->sni.size();
+ *sni = dq->dq->sni.c_str();
+}
+
+const char* dnsdist_ffi_dnsquestion_get_tag(const dnsdist_ffi_dnsquestion_t* dq, const char* label)
+{
+ const char * result = nullptr;
+
+ if (dq->dq->qTag != nullptr) {
+ const auto it = dq->dq->qTag->find(label);
+ if (it != dq->dq->qTag->cend()) {
+ result = it->second.c_str();
+ }
+ }
+
+ return result;
+}
+
+const char* dnsdist_ffi_dnsquestion_get_http_path(dnsdist_ffi_dnsquestion_t* dq)
+{
+ if (!dq->httpPath) {
+ if (dq->dq->du == nullptr) {
+ return nullptr;
+ }
+#ifdef HAVE_DNS_OVER_HTTPS
+ dq->httpPath = dq->dq->du->getHTTPPath();
+#endif /* HAVE_DNS_OVER_HTTPS */
+ }
+ if (dq->httpPath) {
+ return dq->httpPath->c_str();
+ }
+ return nullptr;
+}
+
+const char* dnsdist_ffi_dnsquestion_get_http_query_string(dnsdist_ffi_dnsquestion_t* dq)
+{
+ if (!dq->httpQueryString) {
+ if (dq->dq->du == nullptr) {
+ return nullptr;
+ }
+#ifdef HAVE_DNS_OVER_HTTPS
+ dq->httpQueryString = dq->dq->du->getHTTPQueryString();
+#endif /* HAVE_DNS_OVER_HTTPS */
+ }
+ if (dq->httpQueryString) {
+ return dq->httpQueryString->c_str();
+ }
+ return nullptr;
+}
+
+const char* dnsdist_ffi_dnsquestion_get_http_host(dnsdist_ffi_dnsquestion_t* dq)
+{
+ if (!dq->httpHost) {
+ if (dq->dq->du == nullptr) {
+ return nullptr;
+ }
+#ifdef HAVE_DNS_OVER_HTTPS
+ dq->httpHost = dq->dq->du->getHTTPHost();
+#endif /* HAVE_DNS_OVER_HTTPS */
+ }
+ if (dq->httpHost) {
+ return dq->httpHost->c_str();
+ }
+ return nullptr;
+}
+
+const char* dnsdist_ffi_dnsquestion_get_http_scheme(dnsdist_ffi_dnsquestion_t* dq)
+{
+ if (!dq->httpScheme) {
+ if (dq->dq->du == nullptr) {
+ return nullptr;
+ }
+#ifdef HAVE_DNS_OVER_HTTPS
+ dq->httpScheme = dq->dq->du->getHTTPScheme();
+#endif /* HAVE_DNS_OVER_HTTPS */
+ }
+ if (dq->httpScheme) {
+ return dq->httpScheme->c_str();
+ }
+ return nullptr;
+}
+
+static void fill_edns_option(const EDNSOptionViewValue& value, dnsdist_ednsoption_t& option)
+{
+ option.len = value.size;
+ option.data = nullptr;
+
+ if (value.size > 0) {
+ option.data = value.content;
+ }
+}
+
+// returns the length of the resulting 'out' array. 'out' is not set if the length is 0
+size_t dnsdist_ffi_dnsquestion_get_edns_options(dnsdist_ffi_dnsquestion_t* dq, const dnsdist_ednsoption_t** out)
+{
+ if (dq->dq->ednsOptions == nullptr) {
+ parseEDNSOptions(*(dq->dq));
+ }
+
+ size_t totalCount = 0;
+ for (const auto& option : *dq->dq->ednsOptions) {
+ totalCount += option.second.values.size();
+ }
+
+ dq->ednsOptionsVect.clear();
+ dq->ednsOptionsVect.resize(totalCount);
+ size_t pos = 0;
+ for (const auto& option : *dq->dq->ednsOptions) {
+ for (const auto& entry : option.second.values) {
+ fill_edns_option(entry, dq->ednsOptionsVect.at(pos));
+ dq->ednsOptionsVect.at(pos).optionCode = option.first;
+ pos++;
+ }
+ }
+
+ if (totalCount > 0) {
+ *out = dq->ednsOptionsVect.data();
+ }
+
+ return totalCount;
+}
+
+size_t dnsdist_ffi_dnsquestion_get_http_headers(dnsdist_ffi_dnsquestion_t* dq, const dnsdist_http_header_t** out)
+{
+ if (dq->dq->du == nullptr) {
+ return 0;
+ }
+
+#ifdef HAVE_DNS_OVER_HTTPS
+ dq->httpHeaders = dq->dq->du->getHTTPHeaders();
+ dq->httpHeadersVect.clear();
+ dq->httpHeadersVect.resize(dq->httpHeaders.size());
+ size_t pos = 0;
+ for (const auto& header : dq->httpHeaders) {
+ dq->httpHeadersVect.at(pos).name = header.first.c_str();
+ dq->httpHeadersVect.at(pos).value = header.second.c_str();
+ ++pos;
+ }
+
+ if (!dq->httpHeadersVect.empty()) {
+ *out = dq->httpHeadersVect.data();
+ }
+
+ return dq->httpHeadersVect.size();
+#else
+ return 0;
+#endif
+}
+
+void dnsdist_ffi_dnsquestion_set_http_response(dnsdist_ffi_dnsquestion_t* dq, uint16_t statusCode, const char* body, const char* contentType)
+{
+ if (dq->dq->du == nullptr) {
+ return;
+ }
+
+#ifdef HAVE_DNS_OVER_HTTPS
+ dq->dq->du->setHTTPResponse(statusCode, body, contentType);
+ dq->dq->dh->qr = true;
+#endif
+}
+
+void dnsdist_ffi_dnsquestion_set_rcode(dnsdist_ffi_dnsquestion_t* dq, int rcode)
+{
+ dq->dq->dh->rcode = rcode;
+ dq->dq->dh->qr = true;
+}
+
+void dnsdist_ffi_dnsquestion_set_len(dnsdist_ffi_dnsquestion_t* dq, uint16_t len)
+{
+ dq->dq->len = len;
+}
+
+void dnsdist_ffi_dnsquestion_set_skip_cache(dnsdist_ffi_dnsquestion_t* dq, bool skipCache)
+{
+ dq->dq->skipCache = skipCache;
+}
+
+void dnsdist_ffi_dnsquestion_set_use_ecs(dnsdist_ffi_dnsquestion_t* dq, bool useECS)
+{
+ dq->dq->useECS = useECS;
+}
+
+void dnsdist_ffi_dnsquestion_set_ecs_override(dnsdist_ffi_dnsquestion_t* dq, bool ecsOverride)
+{
+ dq->dq->ecsOverride = ecsOverride;
+}
+
+void dnsdist_ffi_dnsquestion_set_ecs_prefix_length(dnsdist_ffi_dnsquestion_t* dq, uint16_t ecsPrefixLength)
+{
+ dq->dq->ecsPrefixLength = ecsPrefixLength;
+}
+
+void dnsdist_ffi_dnsquestion_set_temp_failure_ttl(dnsdist_ffi_dnsquestion_t* dq, uint32_t tempFailureTTL)
+{
+ dq->dq->tempFailureTTL = tempFailureTTL;
+}
+
+void dnsdist_ffi_dnsquestion_set_tag(dnsdist_ffi_dnsquestion_t* dq, const char* label, const char* value)
+{
+ if (!dq->dq->qTag) {
+ dq->dq->qTag = std::make_shared<QTag>();
+ }
+
+ dq->dq->qTag->insert({label, value});
+}
--- /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
+
+#include "dnsdist.hh"
+
+extern "C" {
+ typedef struct dnsdist_ffi_dnsquestion_t dnsdist_ffi_dnsquestion_t;
+
+ typedef struct dnsdist_ednsoption {
+ uint16_t optionCode;
+ uint16_t len;
+ const void* data;
+ } dnsdist_ednsoption_t;
+
+ typedef struct dnsdist_http_header {
+ const char* name;
+ const char* value;
+ } dnsdist_http_header_t;
+
+ void dnsdist_ffi_dnsquestion_get_localaddr(const dnsdist_ffi_dnsquestion_t* dq, const void** addr, size_t* addrSize) __attribute__ ((visibility ("default")));
+ void dnsdist_ffi_dnsquestion_get_remoteaddr(const dnsdist_ffi_dnsquestion_t* dq, const void** addr, size_t* addrSize) __attribute__ ((visibility ("default")));
+ void dnsdist_ffi_dnsquestion_get_qname_raw(const dnsdist_ffi_dnsquestion_t* dq, const char** qname, size_t* qnameSize) __attribute__ ((visibility ("default")));
+ uint16_t dnsdist_ffi_dnsquestion_get_qtype(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ uint16_t dnsdist_ffi_dnsquestion_get_qclass(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ int dnsdist_ffi_dnsquestion_get_rcode(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ void* dnsdist_ffi_dnsquestion_get_header(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ uint16_t dnsdist_ffi_dnsquestion_get_len(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ size_t dnsdist_ffi_dnsquestion_get_size(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ uint8_t dnsdist_ffi_dnsquestion_get_opcode(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ bool dnsdist_ffi_dnsquestion_get_tcp(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ bool dnsdist_ffi_dnsquestion_get_skip_cache(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ bool dnsdist_ffi_dnsquestion_get_use_ecs(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ bool dnsdist_ffi_dnsquestion_get_add_xpf(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ bool dnsdist_ffi_dnsquestion_get_ecs_override(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ uint16_t dnsdist_ffi_dnsquestion_get_ecs_prefix_length(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ bool dnsdist_ffi_dnsquestion_is_temp_failure_ttl_set(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ uint32_t dnsdist_ffi_dnsquestion_get_temp_failure_ttl(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ bool dnsdist_ffi_dnsquestion_get_do(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ void dnsdist_ffi_dnsquestion_get_sni(const dnsdist_ffi_dnsquestion_t* dq, const char** sni, size_t* sniSize) __attribute__ ((visibility ("default")));
+ const char* dnsdist_ffi_dnsquestion_get_tag(const dnsdist_ffi_dnsquestion_t* dq, const char* label) __attribute__ ((visibility ("default")));
+ const char* dnsdist_ffi_dnsquestion_get_http_path(dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ const char* dnsdist_ffi_dnsquestion_get_http_query_string(dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ const char* dnsdist_ffi_dnsquestion_get_http_host(dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+ const char* dnsdist_ffi_dnsquestion_get_http_scheme(dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
+
+ // returns the length of the resulting 'out' array. 'out' is not set if the length is 0
+ size_t dnsdist_ffi_dnsquestion_get_edns_options(dnsdist_ffi_dnsquestion_t* ref, const dnsdist_ednsoption_t** out) __attribute__ ((visibility ("default")));
+ size_t dnsdist_ffi_dnsquestion_get_http_headers(dnsdist_ffi_dnsquestion_t* ref, const dnsdist_http_header_t** out) __attribute__ ((visibility ("default")));
+
+ void dnsdist_ffi_dnsquestion_set_rcode(dnsdist_ffi_dnsquestion_t* dq, int rcode) __attribute__ ((visibility ("default")));
+ void dnsdist_ffi_dnsquestion_set_len(dnsdist_ffi_dnsquestion_t* dq, uint16_t len) __attribute__ ((visibility ("default")));
+ void dnsdist_ffi_dnsquestion_set_skip_cache(dnsdist_ffi_dnsquestion_t* dq, bool skipCache) __attribute__ ((visibility ("default")));
+ void dnsdist_ffi_dnsquestion_set_use_ecs(dnsdist_ffi_dnsquestion_t* dq, bool useECS) __attribute__ ((visibility ("default")));
+ void dnsdist_ffi_dnsquestion_set_ecs_override(dnsdist_ffi_dnsquestion_t* dq, bool ecsOverride) __attribute__ ((visibility ("default")));
+ void dnsdist_ffi_dnsquestion_set_ecs_prefix_length(dnsdist_ffi_dnsquestion_t* dq, uint16_t ecsPrefixLength) __attribute__ ((visibility ("default")));
+ void dnsdist_ffi_dnsquestion_set_temp_failure_ttl(dnsdist_ffi_dnsquestion_t* dq, uint32_t tempFailureTTL) __attribute__ ((visibility ("default")));
+ void dnsdist_ffi_dnsquestion_set_tag(dnsdist_ffi_dnsquestion_t* dq, const char* label, const char* value) __attribute__ ((visibility ("default")));
+
+ void dnsdist_ffi_dnsquestion_set_http_response(dnsdist_ffi_dnsquestion_t* dq, uint16_t statusCode, const char* body, const char* contentType) __attribute__ ((visibility ("default")));
+}
+
+// dnsdist_ffi_dnsquestion_t is a lightuserdata
+template<>
+struct LuaContext::Pusher<dnsdist_ffi_dnsquestion_t*> {
+ static const int minSize = 1;
+ static const int maxSize = 1;
+
+ static PushedObject push(lua_State* state, dnsdist_ffi_dnsquestion_t* ptr) noexcept {
+ lua_pushlightuserdata(state, ptr);
+ return PushedObject{state, 1};
+ }
+};
+
+struct dnsdist_ffi_dnsquestion_t
+{
+ dnsdist_ffi_dnsquestion_t(DNSQuestion* dq_): dq(dq_)
+ {
+ }
+
+ DNSQuestion* dq{nullptr};
+ std::vector<dnsdist_ednsoption_t> ednsOptionsVect;
+ std::vector<dnsdist_http_header> httpHeadersVect;
+ std::unordered_map<std::string, std::string> httpHeaders;
+ boost::optional<std::string> result{boost::none};
+ boost::optional<std::string> httpPath{boost::none};
+ boost::optional<std::string> httpQueryString{boost::none};
+ boost::optional<std::string> httpHost{boost::none};
+ boost::optional<std::string> httpScheme{boost::none};
+};
#include "dnsdist.hh"
#include "dnsdist-ecs.hh"
#include "dnsdist-kvs.hh"
+#include "dnsdist-lua-ffi.hh"
+#include "dolog.hh"
#include "dnsparser.hh"
class MaxQPSIPRule : public DNSRule
std::shared_ptr<KeyValueStore> d_kvs;
std::shared_ptr<KeyValueLookupKey> d_key;
};
+
+class LuaFFIRule : public DNSRule
+{
+public:
+ typedef std::function<bool(dnsdist_ffi_dnsquestion_t* dq)> func_t;
+ LuaFFIRule(const func_t& func): d_func(func)
+ {}
+
+ bool matches(const DNSQuestion* dq) const override
+ {
+ dnsdist_ffi_dnsquestion_t dqffi(const_cast<DNSQuestion*>(dq));
+ try {
+ std::lock_guard<std::mutex> lock(g_luamutex);
+ return d_func(&dqffi);
+ } catch (const std::exception &e) {
+ warnlog("LuaRule failed inside Lua: %s", e.what());
+ } catch (...) {
+ warnlog("LuaRule failed inside Lua: [unknown exception]");
+ }
+ return false;
+ }
+
+ string toString() const override
+ {
+ return "Lua script";
+ }
+private:
+ func_t d_func;
+};