#include "bpf-filter.hh"
#include "config.h"
#include "dnsdist.hh"
+#include "dnsdist-async.hh"
#include "dnsdist-lua.hh"
+#include "dnsdist-resolver.hh"
#include "dnsdist-svc.hh"
#include "dolog.hh"
// NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold
-void setupLuaBindings(LuaContext& luaCtx, bool client)
+void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck)
{
luaCtx.writeFunction("vinfolog", [](const string& arg) {
vinfolog("%s", arg);
}
return now;
});
+
+ luaCtx.writeFunction("getAddressInfo", [client, configCheck](std::string hostname, std::function<void(const std::string& hostname, const LuaArray<ComboAddress>& ips)> callback) {
+ if (client || configCheck) {
+ return;
+ }
+ std::thread newThread(dnsdist::resolver::asynchronousResolver, std::move(hostname), [callback=std::move(callback)](const std::string& resolvedHostname, std::vector<ComboAddress>& ips) {
+ LuaArray<ComboAddress> result;
+ result.reserve(ips.size());
+ for (auto& entry : ips) {
+ result.emplace_back(result.size() + 1, std::move(entry));
+ }
+ {
+ auto lua = g_lua.lock();
+ callback(resolvedHostname, result);
+ dnsdist::handleQueuedAsynchronousEvents();
+ }
+ });
+ newThread.detach();
+ });
}
setupLuaActions(luaCtx);
setupLuaConfig(luaCtx, client, configCheck);
- setupLuaBindings(luaCtx, client);
+ setupLuaBindings(luaCtx, client, configCheck);
setupLuaBindingsDNSCrypt(luaCtx, client);
setupLuaBindingsDNSParser(luaCtx);
setupLuaBindingsDNSQuestion(luaCtx);
vector<std::function<void(void)>> setupLua(LuaContext& luaCtx, bool client, bool configCheck, const std::string& config);
void setupLuaActions(LuaContext& luaCtx);
-void setupLuaBindings(LuaContext& luaCtx, bool client);
+void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck);
void setupLuaBindingsDNSCrypt(LuaContext& luaCtx, bool client);
void setupLuaBindingsDNSParser(LuaContext& luaCtx);
void setupLuaBindingsDNSQuestion(LuaContext& luaCtx);
dnsdist-protocols.cc dnsdist-protocols.hh \
dnsdist-proxy-protocol.cc dnsdist-proxy-protocol.hh \
dnsdist-random.cc dnsdist-random.hh \
+ dnsdist-resolver.cc dnsdist-resolver.hh \
dnsdist-rings.cc dnsdist-rings.hh \
dnsdist-rules.cc dnsdist-rules.hh \
dnsdist-secpoll.cc dnsdist-secpoll.hh \
dnsdist-protocols.cc dnsdist-protocols.hh \
dnsdist-proxy-protocol.cc dnsdist-proxy-protocol.hh \
dnsdist-random.cc dnsdist-random.hh \
+ dnsdist-resolver.cc dnsdist-resolver.hh \
dnsdist-rings.cc dnsdist-rings.hh \
dnsdist-rules.cc dnsdist-rules.hh \
dnsdist-session-cache.cc dnsdist-session-cache.hh \
void setupLuaLoadBalancingContext(LuaContext& luaCtx)
{
- setupLuaBindings(luaCtx, true);
+ setupLuaBindings(luaCtx, true, false);
setupLuaBindingsDNSQuestion(luaCtx);
setupLuaBindingsKVS(luaCtx, true);
setupLuaVars(luaCtx);
--- /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 <vector>
+
+#include "dnsdist-resolver.hh"
+#include "iputils.hh"
+
+namespace dnsdist::resolver
+{
+void asynchronousResolver(const std::string& hostname, const std::function<void(const std::string& hostname, std::vector<ComboAddress>& ips)>& callback)
+{
+ addrinfo hints{};
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
+ hints.ai_socktype = SOCK_DGRAM;
+ addrinfo* infosRaw{nullptr};
+ std::vector<ComboAddress> addresses;
+ auto ret = getaddrinfo(hostname.c_str(), nullptr, &hints, &infosRaw);
+ if (ret != 0) {
+ callback(hostname, addresses);
+ return;
+ }
+ auto infos = std::unique_ptr<addrinfo, decltype(&freeaddrinfo)>(infosRaw, &freeaddrinfo);
+ for (const auto* addr = infos.get(); addr != nullptr; addr = addr->ai_next) {
+ try {
+ addresses.emplace_back(addr->ai_addr, addr->ai_addrlen);
+ }
+ catch (...) {
+ }
+ }
+ callback(hostname, addresses);
+}
+}
--- /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 "iputils.hh"
+
+namespace dnsdist::resolver
+{
+void asynchronousResolver(const std::string& hostname, const std::function<void(const std::string& hostname, std::vector<ComboAddress>& ips)>& callback);
+}
Other functions
---------------
+.. function:: getAddressInfo(hostname, callback)
+
+ .. versionadded:: 1.9.0
+
+ Asynchronously resolve, via the system resolver (using ``getaddrinfo()``), the supplied ``hostname`` to IPv4 and IPv6 before invoking the supplied ``callback`` function with the ``hostname`` and a list of IPv4 and IPv6 addresses as :class:`ComboAddress`.
+ For example, to get the addresses of Quad9's and dynamically add them as backends:
+
+ .. code-block:: lua
+
+ function resolveCB(hostname, ips)
+ for _, ip in ipairs(ips) do
+ newServer(ip:toString())
+ end
+ end
+ getAddressInfo('dns.quad9.net.', resolveCB)
+
+ :param str hostname: The hostname to resolve.
+ :param function callback: The function to invoke when the name has been resolved.
+
.. function:: getCurrentTime -> timespec
.. versionadded:: 1.8.0
# let's wait a bit longer
time.sleep(5)
self.assertTrue(self.checkBackendsUpgraded())
+
+class TestBackendDiscoveryByHostname(DNSDistTest):
+ _consoleKey = DNSDistTest.generateConsoleKey()
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+ _config_params = ['_consoleKeyB64', '_consolePort']
+ _config_template = """
+ setKey("%s")
+ controlSocket("127.0.0.1:%d")
+
+ function resolveCB(hostname, ips)
+ print('Got response for '..hostname)
+ for _, ip in ipairs(ips) do
+ print(ip)
+ newServer(ip:toString())
+ end
+ end
+
+ getAddressInfo('dns.quad9.net.', resolveCB)
+ """
+ def checkBackends(self):
+ output = self.sendConsoleCommand('showServers()')
+ print(output)
+ backends = {}
+ for line in output.splitlines(False):
+ if line.startswith('#') or line.startswith('All'):
+ continue
+ tokens = line.split()
+ self.assertTrue(len(tokens) == 13 or len(tokens) == 14)
+ backends[tokens[1]] = tokens[2]
+
+ if len(backends) != 4:
+ return False
+
+ for expected in ['9.9.9.9:53', '149.112.112.112:53', '[2620:fe::9]:53', '[2620:fe::fe]:53']:
+ self.assertIn(expected, backends)
+ for backend in backends:
+ self.assertTrue(backends[backend])
+ return True
+
+ def testBackendFromHostname(self):
+ """
+ Backend Discovery: From hostname
+ """
+ # enough time for resolution to happen
+ time.sleep(4)
+ if not self.checkBackends():
+ time.sleep(4)
+ self.assertTrue(self.checkBackends())