Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions pdns/dnsdistdist/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ EXTRA_DIST=COPYING \
kqueuemplexer.cc \
portsmplexer.cc \
cdb.cc cdb.hh \
redis.cc redis.hh \
standalone_fuzz_target_runner.cc \
ext/lmdb-safe/lmdb-safe.cc ext/lmdb-safe/lmdb-safe.hh \
ext/protozero/include/* \
Expand Down Expand Up @@ -475,6 +476,7 @@ testrunner_LDFLAGS = \
testrunner_LDADD = \
$(BOOST_UNIT_TEST_FRAMEWORK_LIBS) \
$(FSTRM_LIBS) \
$(YAHTTP_LIBS) \
$(LIBSODIUM_LIBS) \
$(LUA_LIBS) \
$(RT_LIBS) \
Expand All @@ -489,6 +491,13 @@ dnsdist_SOURCES += cdb.cc cdb.hh
testrunner_SOURCES += cdb.cc cdb.hh
endif

if HAVE_REDIS
dnsdist_LDADD += $(REDIS_LDFLAGS) $(REDIS_LIBS)
testrunner_LDADD += $(REDIS_LDFLAGS) $(REDIS_LIBS)
dnsdist_SOURCES += redis.cc redis.hh
testrunner_SOURCES += redis.cc redis.hh
endif

if HAVE_RE2
dnsdist_LDADD += $(RE2_LIBS)
testrunner_LDADD += $(RE2_LIBS)
Expand Down
6 changes: 6 additions & 0 deletions pdns/dnsdistdist/configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ AS_IF([test "x$LUAPC" = "xluajit"], [
PDNS_CHECK_LUA_HPP

AM_CONDITIONAL([HAVE_CDB], [false])
AM_CONDITIONAL([HAVE_REDIS], [false])
AM_CONDITIONAL([HAVE_GNUTLS], [false])
AM_CONDITIONAL([HAVE_LIBSSL], [false])
AM_CONDITIONAL([HAVE_LMDB], [false])
Expand Down Expand Up @@ -141,6 +142,7 @@ AS_IF([test "x$enable_yaml" != "xno"], [
])

DNSDIST_WITH_CDB
DNSDIST_WITH_REDIS
PDNS_CHECK_LMDB
PDNS_ENABLE_IPCIPHER
PDNS_ENABLE_IPCRYPT2
Expand Down Expand Up @@ -317,6 +319,10 @@ AS_IF([test "x$LMDB_LIBS" != "x"],
[AC_MSG_NOTICE([lmdb: yes])],
[AC_MSG_NOTICE([lmdb: no])]
)
AS_IF([test "x$REDIS_LIBS" != "x"],
[AC_MSG_NOTICE([redis: yes])],
[AC_MSG_NOTICE([redis: no])]
)
AS_IF([test "x$enable_yaml" != "xno"],
[AC_MSG_NOTICE([YAML configuration: yes])],
[AC_MSG_NOTICE([YAML configuration: no])]
Expand Down
6 changes: 6 additions & 0 deletions pdns/dnsdistdist/dnsdist-configuration.hh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
#include "dnsdist-rule-chains.hh"
#include "dnsdist-server-pool.hh"
#include "iputils.hh"
#ifdef HAVE_REDIS
#include "redis-stats.hh"
#endif /* HAVE_REDIS */

class ServerPolicy;
struct ServerPool;
Expand Down Expand Up @@ -131,6 +134,9 @@ struct RuntimeConfiguration
std::vector<dnsdist::Carbon::Endpoint> d_carbonEndpoints;
#endif /* DISABLE_CARBON */
std::unordered_map<std::string, ServerPool> d_pools;
#ifdef HAVE_REDIS
std::unordered_map<std::string, std::shared_ptr<RedisStats>> d_redisStats;
#endif /* HAVE_REDIS */
std::shared_ptr<const CredentialsHolder> d_webPassword;
std::shared_ptr<const CredentialsHolder> d_webAPIKey;
std::optional<std::unordered_map<std::string, std::string>> d_webCustomHeaders;
Expand Down
59 changes: 59 additions & 0 deletions pdns/dnsdistdist/dnsdist-kvs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,62 @@ bool CDBKVStore::keyExists(const std::string& key)
}

#endif /* HAVE_CDB */

#ifdef HAVE_REDIS

RedisKVStore::RedisKVStore(const std::shared_ptr<RedisClient>& redisClient, std::optional<std::string> lookupAction, std::optional<std::string> dataName, std::shared_ptr<RedisStats> stats) :
d_stats(stats)
{
std::unique_ptr<RedisLookupAction> command;
if (lookupAction && !boost::iequals(lookupAction.value(), "get")) {
if (!dataName) {
throw std::runtime_error("Option 'dataName' is required for lookup action " + lookupAction.value());
}
if (boost::iequals(lookupAction.value(), "hget")) {
command = std::make_unique<RedisHGetLookupAction>(dataName.value());
}
else {
throw std::runtime_error("Unknown lookup action: " + lookupAction.value());
}
}
else {
if (dataName) {
command = std::make_unique<RedisGetLookupAction>(dataName.value());
}
else {
command = std::make_unique<RedisGetLookupAction>();
}
}
d_redis = std::make_unique<RedisKVClient>(redisClient, std::move(command), stats);
}

bool RedisKVStore::reload()
{
return true;
}

bool RedisKVStore::getValue(const std::string& key, std::string& value)
{
auto result = d_redis->getValue(key, value);
if (result) {
d_stats->d_successfulLookups += 1;
}
else {
d_stats->d_failedLookups += 1;
}
return result;
}

bool RedisKVStore::keyExists(const std::string& key)
{
auto result = d_redis->keyExists(key);
if (result) {
d_stats->d_successfulLookups += 1;
}
else {
d_stats->d_failedLookups += 1;
}
return result;
}

#endif // HAVE_REDIS
20 changes: 20 additions & 0 deletions pdns/dnsdistdist/dnsdist-kvs.hh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <memory>
#include "dnsdist.hh"
#include "logr.hh"
#include "iputils.hh"

class KeyValueLookupKey
{
Expand Down Expand Up @@ -231,3 +232,22 @@ private:
};

#endif /* HAVE_LMDB */

#ifdef HAVE_REDIS

#include "redis.hh"

class RedisKVStore : public KeyValueStore
{
public:
RedisKVStore(const std::shared_ptr<RedisClient>& redisClient, std::optional<std::string> lookupAction, std::optional<std::string> dataName, std::shared_ptr<RedisStats> stats);

bool keyExists(const std::string& key) override;
bool getValue(const std::string& key, std::string& value) override;
bool reload() override;

private:
std::unique_ptr<RedisKVClientInterface> d_redis{nullptr};
std::shared_ptr<RedisStats> d_stats;
};
#endif // HAVE_REDIS
32 changes: 30 additions & 2 deletions pdns/dnsdistdist/dnsdist-lua-bindings-kvs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,35 @@ void setupLuaBindingsKVS([[maybe_unused]] LuaContext& luaCtx, [[maybe_unused]] b
});
#endif /* HAVE_CDB */

#if defined(HAVE_LMDB) || defined(HAVE_CDB)
#ifdef HAVE_REDIS
luaCtx.writeFunction("newRedisKVStore", [client](const std::shared_ptr<RedisClient>& redisClient, std::optional<LuaAssociativeTable<boost::variant<std::string, bool, LuaArray<std::string>>>> vars) {
if (client) {
return std::shared_ptr<KeyValueStore>(nullptr);
}

std::optional<std::string> lookupAction;
std::optional<std::string> dataName;
getOptionalValue<std::string>(vars, "dataName", dataName);
getOptionalValue<std::string>(vars, "lookupAction", lookupAction);

checkAllParametersConsumed("newRedisKVStore", vars);

std::string uniqueId = "url=" + redisClient->getUrl().to_string() + ",action=" + lookupAction.value_or("GET") + ",data-name=" + dataName.value_or("") + ",";
std::string labels = "redis-server=" + redisClient->getUrl().host + ":" + std::to_string(redisClient->getUrl().port) + ",redis-action=" + lookupAction.value_or("GET") + ",data-name=" + dataName.value_or("");
std::shared_ptr<RedisStats> stats = std::make_shared<RedisStats>(labels);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the idea is to share metrics between all RedisKVStore instances using the same parameters, right? What's the use case for defining several of these?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I wanted to prevent that case, to prevent confusion about the metrics. I thought the runtime error I threw below would prevent duplicate instances with the exact same params.

There is a use case for different parameters of course and that should be supported.


dnsdist::configuration::updateRuntimeConfiguration([uniqueId, &stats](dnsdist::configuration::RuntimeConfiguration& config) {
if (config.d_redisStats.count(uniqueId) > 0) {
throw std::runtime_error("Duplicate redis instance. Combination of arguments has to be unique!");
}
config.d_redisStats.emplace(uniqueId, std::shared_ptr(stats));
});

return std::shared_ptr<KeyValueStore>(new RedisKVStore(redisClient, lookupAction, dataName, stats));
});
#endif /* HAVE_REDIS */

#if defined(HAVE_LMDB) || defined(HAVE_CDB) || defined(HAVE_REDIS)
/* Key Value Store objects */
luaCtx.writeFunction("KeyValueLookupKeySourceIP", [](std::optional<uint8_t> v4Mask, std::optional<uint8_t> v6Mask, std::optional<bool> includePort) {
return std::shared_ptr<KeyValueLookupKey>(new KeyValueLookupKeySourceIP(v4Mask ? *v4Mask : 32, v6Mask ? *v6Mask : 128, includePort ? *includePort : false));
Expand Down Expand Up @@ -113,5 +141,5 @@ void setupLuaBindingsKVS([[maybe_unused]] LuaContext& luaCtx, [[maybe_unused]] b

return kvs->reload();
});
#endif /* defined(HAVE_LMDB) || defined(HAVE_CDB) */
#endif /* defined(HAVE_LMDB) || defined(HAVE_CDB) || defined(HAVE_REDIS) */
}
103 changes: 103 additions & 0 deletions pdns/dnsdistdist/dnsdist-lua-bindings-redis.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* 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.hh"
#include <memory>
#ifdef HAVE_REDIS
#include "redis.hh"
#endif /* HAVE_REDIS */

void setupLuaBindingsRedis([[maybe_unused]] LuaContext& luaCtx, [[maybe_unused]] bool client)
{
#ifdef HAVE_REDIS
luaCtx.writeFunction("newRedisClient", [client](const std::string& url, std::optional<LuaAssociativeTable<boost::variant<bool, std::string>>> vars) {
if (client) {
return std::shared_ptr<RedisClient>(nullptr);
}

bool pipelineEnabled{true};
int pipelineInterval{10};
getOptionalValue<bool>(vars, "pipelineEnabled", pipelineEnabled);
getOptionalIntegerValue<int>("newRedisClient", vars, "pipelineInterval", pipelineInterval);
checkAllParametersConsumed("newRedisClient", vars);

return std::make_shared<RedisClient>(url, pipelineEnabled, pipelineInterval);
});

luaCtx.registerFunction<std::string (std::shared_ptr<RedisClient>::*)(const std::string&)>("get", [](std::shared_ptr<RedisClient>& rc, const std::string& key) {
std::string result;
if (!rc) {
return result;
}

auto reply = RedisGetCommand{}(*rc, key);

if (reply->ok()) {
result = reply->getValue();
}

return result;
});

luaCtx.registerFunction<bool (std::shared_ptr<RedisClient>::*)(const std::string&)>("exists", [](std::shared_ptr<RedisClient>& rc, const std::string& key) {
if (!rc) {
return false;
}

auto reply = RedisExistsCommand{}(*rc, key);

if (reply->ok()) {
return reply->getValue();
}

return false;
});

luaCtx.registerFunction<std::string (std::shared_ptr<RedisClient>::*)(const std::string&, const std::string&)>("hget", [](std::shared_ptr<RedisClient>& rc, const std::string& hash_key, const std::string& key) {
std::string result;
if (!rc) {
return result;
}

auto reply = RedisHGetCommand{}(*rc, hash_key, key);

if (reply->ok()) {
result = reply->getValue();
}

return result;
});

luaCtx.registerFunction<bool (std::shared_ptr<RedisClient>::*)(const std::string&, const std::string&)>("hexists", [](std::shared_ptr<RedisClient>& rc, const std::string& hash_key, const std::string& key) {
if (!rc) {
return false;
}

auto reply = RedisHExistsCommand{}(*rc, hash_key, key);

if (reply->ok()) {
return reply->getValue();
}

return false;
});
#endif /* HAVE_REDIS */
}
1 change: 1 addition & 0 deletions pdns/dnsdistdist/dnsdist-lua.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3328,6 +3328,7 @@ void setupLuaBindingsOnly(LuaContext& luaCtx, bool client, bool configCheck)
setupLuaBindingsDNSParser(luaCtx);
setupLuaBindingsDNSQuestion(luaCtx);
setupLuaBindingsKVS(luaCtx, client);
setupLuaBindingsRedis(luaCtx, client);
setupLuaBindingsLogging(luaCtx);
setupLuaBindingsNetwork(luaCtx, client, configCheck);
setupLuaBindingsPacketCache(luaCtx, client);
Expand Down
1 change: 1 addition & 0 deletions pdns/dnsdistdist/dnsdist-lua.hh
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ void setupLuaBindingsDNSCrypt(LuaContext& luaCtx, bool client);
void setupLuaBindingsDNSParser(LuaContext& luaCtx);
void setupLuaBindingsDNSQuestion(LuaContext& luaCtx);
void setupLuaBindingsKVS(LuaContext& luaCtx, bool client);
void setupLuaBindingsRedis(LuaContext& luaCtx, bool client);
void setupLuaBindingsLogging(LuaContext& luaCtx);
void setupLuaBindingsNetwork(LuaContext& luaCtx, bool client, bool configCheck);
void setupLuaBindingsPacketCache(LuaContext& luaCtx, bool client);
Expand Down
3 changes: 3 additions & 0 deletions pdns/dnsdistdist/dnsdist.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3150,6 +3150,9 @@ static void reportFeatures()
cout << "recvmmsg/sendmmsg ";
#endif
#endif /* DISABLE_RECVMMSG */
#ifdef HAVE_REDIS
cout << "redis ";
#endif
#ifdef HAVE_NET_SNMP
cout << "snmp ";
#endif
Expand Down
39 changes: 39 additions & 0 deletions pdns/dnsdistdist/m4/dnsdist_with_redis.m4
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
AC_DEFUN([DNSDIST_WITH_REDIS], [
AC_MSG_CHECKING([whether we will we linking with libhiredis])
HAVE_REDIS=0
AC_ARG_WITH([redis],
AS_HELP_STRING([--with-redis], [use Redis @<:@default=auto@:>@]),
[with_redis=$withval],
[with_redis=auto]
)
AC_MSG_RESULT([$with_redis])

AS_IF([test "x$with_redis" != "xno"], [
AS_IF([test "x$with_redis" = "xyes" -o "x$with_redis" = "xauto"], [
PKG_CHECK_MODULES([REDIS], [libhiredis], [
[HAVE_REDIS=1]
AC_DEFINE([HAVE_REDIS], [1], [Define to 1 if you have REDIS])
],
[AC_CHECK_HEADERS([hiredis/hiredis.h],
[AC_CHECK_LIB([hiredis], [redisConnect],
[
REDIS_LIBS="-lhiredis"
AC_DEFINE([HAVE_REDIS], [1], [Define to 1 if you have REDIS])
[HAVE_REDIS=1]
],
[:]
)],
[:]
)]
)
])
])
AC_SUBST(REDIS_LIBS)
AC_SUBST(REDIS_CFLAGS)
AM_CONDITIONAL([HAVE_REDIS], [test "x$REDIS_LIBS" != "x"])
AS_IF([test "x$with_redis" = "xyes"], [
AS_IF([test x"$REDIS_LIBS" = "x"], [
AC_MSG_ERROR([Redis requested but libraries were not found])
])
])
])
Loading
Loading