setupLuaBindingsDNSCrypt(luaCtx, client);
setupLuaBindingsDNSQuestion(luaCtx);
setupLuaBindingsKVS(luaCtx, client);
+ setupLuaBindingsNetwork(luaCtx, client);
setupLuaBindingsPacketCache(luaCtx, client);
setupLuaBindingsProtoBuf(luaCtx, client, configCheck);
setupLuaBindingsRings(luaCtx, client);
void setupLuaBindingsDNSCrypt(LuaContext& luaCtx, bool client);
void setupLuaBindingsDNSQuestion(LuaContext& luaCtx);
void setupLuaBindingsKVS(LuaContext& luaCtx, bool client);
+void setupLuaBindingsNetwork(LuaContext& luaCtx, bool client);
void setupLuaBindingsPacketCache(LuaContext& luaCtx, bool client);
void setupLuaBindingsProtoBuf(LuaContext& luaCtx, bool client, bool configCheck);
void setupLuaBindingsRings(LuaContext& luaCtx, bool client);
dnsdist-lua-bindings-dnscrypt.cc \
dnsdist-lua-bindings-dnsquestion.cc \
dnsdist-lua-bindings-kvs.cc \
+ dnsdist-lua-bindings-network.cc \
dnsdist-lua-bindings-packetcache.cc \
dnsdist-lua-bindings-protobuf.cc \
dnsdist-lua-bindings-rings.cc \
dnsdist-lua-ffi.cc dnsdist-lua-ffi.hh \
dnsdist-lua-inspection-ffi.cc dnsdist-lua-inspection-ffi.hh \
dnsdist-lua-inspection.cc \
+ dnsdist-lua-network.cc dnsdist-lua-network.hh \
dnsdist-lua-rules.cc \
dnsdist-lua-vars.cc \
dnsdist-lua-web.cc \
dnsdist-lua-bindings.cc \
dnsdist-lua-ffi-interface.h dnsdist-lua-ffi-interface.inc \
dnsdist-lua-ffi.cc dnsdist-lua-ffi.hh \
+ dnsdist-lua-network.cc dnsdist-lua-network.hh \
dnsdist-lua-vars.cc \
dnsdist-mac-address.cc dnsdist-mac-address.hh \
dnsdist-nghttp2.cc dnsdist-nghttp2.hh \
test-dnsdistdynblocks_hh.cc \
test-dnsdistkvs_cc.cc \
test-dnsdistlbpolicies_cc.cc \
+ test-dnsdistluanetwork.cc \
test-dnsdistnghttp2_cc.cc \
test-dnsdistpacketcache_cc.cc \
test-dnsdistrings_cc.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.hh"
+#include "dnsdist-lua.hh"
+#include "dnsdist-lua-network.hh"
+#include "dolog.hh"
+
+void setupLuaBindingsNetwork(LuaContext& luaCtx, bool client)
+{
+ luaCtx.writeFunction("newNetworkEndpoint", [client](const std::string& path) {
+ if (client) {
+ return std::shared_ptr<dnsdist::NetworkEndpoint>(nullptr);
+ }
+
+ try {
+ return std::make_shared<dnsdist::NetworkEndpoint>(path);
+ }
+ catch (const std::exception& e) {
+ warnlog("Error connecting to network endpoint: %s", e.what());
+ }
+ return std::shared_ptr<dnsdist::NetworkEndpoint>(nullptr);
+ });
+
+ luaCtx.registerFunction<bool (std::shared_ptr<dnsdist::NetworkEndpoint>::*)() const>("isValid", [](const std::shared_ptr<dnsdist::NetworkEndpoint>& endpoint) {
+ return endpoint != nullptr;
+ });
+
+ luaCtx.registerFunction<bool (std::shared_ptr<dnsdist::NetworkEndpoint>::*)(const std::string&) const>("send", [client](const std::shared_ptr<dnsdist::NetworkEndpoint>& endpoint, const std::string& payload) {
+ if (client || !endpoint || payload.empty()) {
+ return false;
+ }
+
+ return endpoint->send(payload);
+ });
+
+ luaCtx.writeFunction("newNetworkListener", [client]() {
+ if (client) {
+ return std::shared_ptr<dnsdist::NetworkListener>(nullptr);
+ }
+
+ return std::make_shared<dnsdist::NetworkListener>();
+ });
+
+ luaCtx.registerFunction<bool (std::shared_ptr<dnsdist::NetworkListener>::*)(const std::string&, uint16_t, std::function<void(uint16_t, std::string & dgram, const std::string& from)>)>("addUnixListeningEndpoint", [client](std::shared_ptr<dnsdist::NetworkListener>& listener, const std::string& path, uint16_t endpointID, std::function<void(uint16_t endpoint, std::string & dgram, const std::string& from)> cb) {
+ if (client) {
+ return false;
+ }
+
+ if (!listener) {
+ return false;
+ }
+
+ return listener->addUnixListeningEndpoint(path, endpointID, [cb](dnsdist::NetworkListener::EndpointID endpoint, std::string&& dgram, const std::string& from) {
+ auto lock = g_lua.lock();
+ cb(endpoint, dgram, from);
+ });
+ });
+
+ luaCtx.registerFunction<void (std::shared_ptr<dnsdist::NetworkListener>::*)()>("start", [client](std::shared_ptr<dnsdist::NetworkListener>& listener) {
+ if (client || !listener) {
+ return;
+ }
+
+ listener->start();
+ });
+};
--- /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 <sys/socket.h>
+#include <sys/un.h>
+
+#include "dnsdist-lua-network.hh"
+#include "dolog.hh"
+
+namespace dnsdist
+{
+NetworkListener::NetworkListener() :
+ d_mplexer(std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent()))
+{
+}
+
+void NetworkListener::readCB(int desc, FDMultiplexer::funcparam_t& param)
+{
+ auto cbData = boost::any_cast<std::shared_ptr<NetworkListener::CBData>>(param);
+ /* reuse ? */
+ std::string packet;
+ packet.resize(65535);
+
+ struct sockaddr_un from;
+ memset(&from, 0, sizeof(from));
+
+ socklen_t fromLen = sizeof(from);
+ auto got = recvfrom(desc, &packet.at(0), packet.size(), 0, reinterpret_cast<sockaddr*>(&from), &fromLen);
+ if (got > 0) {
+ packet.resize(static_cast<size_t>(got));
+ std::string fromAddr;
+ if (fromLen <= sizeof(from)) {
+ fromAddr = std::string(from.sun_path, strlen(from.sun_path));
+ }
+ try {
+ cbData->d_cb(cbData->d_endpoint, std::move(packet), fromAddr);
+ }
+ catch (const std::exception& e) {
+ vinfolog("Exception in the read callback of a NetworkListener: %s", e.what());
+ }
+ catch (...) {
+ vinfolog("Exception in the read callback of a NetworkListener");
+ }
+ }
+}
+
+bool NetworkListener::addUnixListeningEndpoint(const std::string& path, NetworkListener::EndpointID id, NetworkListener::NetworkDatagramCB cb)
+{
+ if (d_running == true) {
+ throw std::runtime_error("NetworkListener should not be altered at runtime");
+ }
+
+ struct sockaddr_un sun;
+ if (makeUNsockaddr(path, &sun) != 0) {
+ throw std::runtime_error("Invalid Unix socket path '" + path + "'");
+ }
+
+ bool abstractPath = path.at(0) == '\0';
+ if (!abstractPath) {
+ int err = unlink(path.c_str());
+ if (err != 0) {
+ err = errno;
+ if (err != ENOENT) {
+ vinfolog("Error removing Unix socket to path '%s': %s", path, stringerror(err));
+ }
+ }
+ }
+
+ Socket sock(sun.sun_family, SOCK_DGRAM, 0);
+ socklen_t sunLength = sizeof(sun);
+ if (abstractPath) {
+ /* abstract paths can contain null bytes so we need to set the actual size */
+ sunLength = sizeof(sa_family_t) + path.size();
+ }
+
+ if (bind(sock.getHandle(), reinterpret_cast<const struct sockaddr*>(&sun), sunLength) != 0) {
+ std::string sanitizedPath(path);
+ if (abstractPath) {
+ sanitizedPath[0] = '@';
+ }
+ throw std::runtime_error("Error binding Unix socket to path '" + sanitizedPath + "': " + stringerror());
+ }
+
+ sock.setNonBlocking();
+
+ auto cbData = std::make_shared<CBData>();
+ cbData->d_endpoint = id;
+ cbData->d_cb = cb;
+ d_mplexer->addReadFD(sock.getHandle(), readCB, cbData);
+
+ d_sockets.insert({path, std::move(sock)});
+ return true;
+}
+
+void NetworkListener::runOnce(struct timeval& now, uint32_t timeout)
+{
+ d_running = true;
+ if (d_sockets.empty()) {
+ throw runtime_error("NetworkListener started with no sockets");
+ }
+
+ d_mplexer->run(&now, timeout);
+}
+
+void NetworkListener::mainThread()
+{
+ struct timeval now;
+
+ while (true) {
+ runOnce(now, 5000);
+ }
+}
+
+void NetworkListener::start()
+{
+ std::thread main = std::thread([this] {
+ mainThread();
+ });
+ main.detach();
+}
+
+NetworkEndpoint::NetworkEndpoint(const std::string& path) :
+ d_socket(AF_UNIX, SOCK_DGRAM, 0)
+{
+ struct sockaddr_un sun;
+ if (makeUNsockaddr(path, &sun) != 0) {
+ throw std::runtime_error("Invalid Unix socket path '" + path + "'");
+ }
+
+ socklen_t sunLength = sizeof(sun);
+ bool abstractPath = path.at(0) == '\0';
+
+ if (abstractPath) {
+ /* abstract paths can contain null bytes so we need to set the actual size */
+ sunLength = sizeof(sa_family_t) + path.size();
+ }
+ if (connect(d_socket.getHandle(), reinterpret_cast<const struct sockaddr*>(&sun), sunLength) != 0) {
+ std::string sanitizedPath(path);
+ if (abstractPath) {
+ sanitizedPath[0] = '@';
+ }
+ throw std::runtime_error("Error connecting Unix socket to path '" + sanitizedPath + "': " + stringerror());
+ }
+
+ d_socket.setNonBlocking();
+}
+
+bool NetworkEndpoint::send(const std::string_view& payload) const
+{
+ auto sent = ::send(d_socket.getHandle(), payload.data(), payload.size(), 0);
+ if (sent <= 0) {
+ return false;
+ }
+
+ return static_cast<size_t>(sent) == payload.size();
+}
+}
--- /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 <thread>
+#include <unordered_map>
+
+#include "lock.hh"
+#include "mplexer.hh"
+#include "sstuff.hh"
+
+namespace dnsdist
+{
+class NetworkListener
+{
+public:
+ NetworkListener();
+
+ using EndpointID = uint16_t;
+ using NetworkDatagramCB = std::function<void(EndpointID endpoint, std::string&& dgram, const std::string& from)>;
+ bool addUnixListeningEndpoint(const std::string& path, EndpointID id, NetworkDatagramCB cb);
+ void start();
+ void runOnce(struct timeval& now, uint32_t timeout);
+
+private:
+ static void readCB(int desc, FDMultiplexer::funcparam_t& param);
+ void mainThread();
+
+ struct CBData
+ {
+ NetworkDatagramCB d_cb;
+ EndpointID d_endpoint;
+ };
+
+ std::unique_ptr<FDMultiplexer> d_mplexer;
+ std::unordered_map<std::string, Socket> d_sockets;
+ std::atomic<bool> d_running{false};
+};
+
+class NetworkEndpoint
+{
+public:
+ NetworkEndpoint(const std::string& path);
+ bool send(const std::string_view& payload) const;
+
+private:
+ Socket d_socket;
+};
+}
--- /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.
+ */
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_NO_MAIN
+
+#include <boost/test/unit_test.hpp>
+
+#include "dnsdist-lua-network.hh"
+
+BOOST_AUTO_TEST_SUITE(test_dnsdistluanetwork)
+
+BOOST_AUTO_TEST_CASE(test_Basic)
+{
+ dnsdist::NetworkListener listener;
+ bool received = false;
+
+ std::string payload = {'h', 'e', 'l', 'l', 'o'};
+ char socketPath[] = "/tmp/test_dnsdistluanetwork.XXXXXX";
+ int fd = mkstemp(socketPath);
+ BOOST_REQUIRE(fd >= 0);
+
+ listener.addUnixListeningEndpoint(socketPath, 0, [&received, payload](dnsdist::NetworkListener::EndpointID endpoint, std::string&& dgram, const std::string& from) {
+ BOOST_CHECK_EQUAL(endpoint, 0U);
+ BOOST_CHECK(dgram == payload);
+ received = true;
+ });
+
+ dnsdist::NetworkEndpoint client(socketPath);
+ BOOST_CHECK(client.send(payload));
+
+ struct timeval now;
+ listener.runOnce(now, 1000);
+ BOOST_CHECK(received);
+
+ unlink(socketPath);
+ close(fd);
+}
+
+#ifdef __linux__
+BOOST_AUTO_TEST_CASE(test_Abstract)
+{
+ dnsdist::NetworkListener listener;
+ bool received = false;
+
+ std::string payload = {'h', 'e', 'l', 'l', 'o'};
+ std::string socketPath("test_dnsdistluanetwork");
+ socketPath.insert(0, 1, 0);
+
+ listener.addUnixListeningEndpoint(socketPath, 0, [&received, payload](dnsdist::NetworkListener::EndpointID endpoint, std::string&& dgram, const std::string& from) {
+ BOOST_CHECK_EQUAL(endpoint, 0U);
+ BOOST_CHECK(dgram == payload);
+ received = true;
+ });
+
+ dnsdist::NetworkEndpoint client(socketPath);
+ BOOST_CHECK(client.send(payload));
+
+ struct timeval now;
+ listener.runOnce(now, 1000);
+ BOOST_CHECK(received);
+}
+#endif /* __linux__ */
+
+BOOST_AUTO_TEST_SUITE_END();