]> granicus.if.org Git - pdns/commitdiff
dnsdist: Add a KeyValueStoreLookup action based on LMDB
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 4 Jul 2019 16:12:13 +0000 (18:12 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Wed, 7 Aug 2019 09:04:28 +0000 (11:04 +0200)
18 files changed:
configure.ac
m4/pdns_check_lmdb.m4
modules/lmdbbackend/Makefile.am
modules/lmdbbackend/lmdb-typed.hh
pdns/Makefile.am
pdns/dnsdist-lua-actions.cc
pdns/dnsdist-lua-bindings.cc
pdns/dnsdist.cc
pdns/dnsdistdist/Makefile.am
pdns/dnsdistdist/configure.ac
pdns/dnsdistdist/dnsdist-kvs.cc [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-kvs.hh [new file with mode: 0644]
pdns/dnsdistdist/lmdb-safe.cc [new symlink]
pdns/dnsdistdist/lmdb-safe.hh [new symlink]
pdns/dnsdistdist/m4/pdns_check_lmdb.m4 [new symlink]
pdns/dnsdistdist/test-dnsdistkvs_cc.cc [new file with mode: 0644]
pdns/lmdb-safe.cc [moved from modules/lmdbbackend/lmdb-safe.cc with 100% similarity]
pdns/lmdb-safe.hh [moved from modules/lmdbbackend/lmdb-safe.hh with 100% similarity]

index 22fdd6a326c459230555781dca8f083b8e190bb9..c2f7c6d7e7ca9d746a0c3da6d517bbf10a00c77b 100644 (file)
@@ -217,6 +217,7 @@ for a in $modules $dynmodules; do
       )
       ;;
     lmdb)
+      needlmdb=yes
       PDNS_CHECK_LMDB
       BOOST_SERIALIZATION
       ;;
@@ -239,6 +240,18 @@ AM_CONDITIONAL([LDAP], [test "x$needldap" = "xyes"])
 PDNS_CHECK_SQLITE3
 AM_CONDITIONAL([SQLITE3], [test "x$needsqlite3" = "xyes"])
 
+AM_CONDITIONAL([LMDB], [test "x$needlmdb" != "x"])
+AM_CONDITIONAL([HAVE_LMDB], [test "x$LMDB_LIBS" != "x"])
+
+AS_IF([test "x$needlmdb" = "xyes"], [
+  AS_IF([test "$with_lmdb" = "no"], [
+    AC_MSG_ERROR([--with-lmdb is set to 'no', but lmdb support is required])
+  ])
+  AS_IF([test "x$HAVE_LMDB" != "x1"], [
+    AC_MSG_ERROR([lmdb not found via pkg-config, please install lmdb or set --with-lmdb to your lmdb installation directory])
+  ])
+])
+
 for a in $modules; do
   AC_MSG_CHECKING([whether we can build module "${a}"])
   if [[ -d "$srcdir/modules/${a}backend" ]]; then
index 611f66823a71b1dcf772c9c7303356f682057dc2..f1921bde91d312cc7b04b75495b9890327735645 100644 (file)
@@ -11,9 +11,11 @@ AC_DEFUN([PDNS_CHECK_LMDB], [
 
   AS_IF([test "$with_lmdb" != "no"], [
     AS_IF([test "x$with_lmdb" = "xyes" -o "x$with_lmdb" = "xauto"], [
-      PKG_CHECK_MODULES([LMDB], [lmdb], [ : ], [
-        AC_MSG_ERROR([lmdb not found via pkg-config, please install lmdb or set --with-lmdb to your lmdb installation directory])
-      ])
+      PKG_CHECK_MODULES([LMDB], [lmdb], [
+        AC_DEFINE([HAVE_LMDB], [1], [Define to 1 if you have LMDB])
+        [HAVE_LMDB=1]
+        ], [ : ]
+      )
     ], [
       save_CPPFLAGS=$CPPFLAGS
       save_LIBS=$LIBS
@@ -31,6 +33,8 @@ AC_DEFUN([PDNS_CHECK_LMDB], [
         AC_CHECK_HEADERS([lmdb.h], [
           dnl ac_cv_search_mdb_env_open contains '-llmdb'
           LMDB_LIBS="$LMDB_LIBS $ac_cv_search_mdb_env_open"
+          AC_DEFINE([HAVE_LMDB], [1], [Define to 1 if you have LMDB])
+          [HAVE_LMDB=1]
         ], [
           AC_MSG_ERROR([lmdb headers not found in $with_lmdb])
         ])
@@ -40,7 +44,6 @@ AC_DEFUN([PDNS_CHECK_LMDB], [
         AC_SUBST([LMDB_LIBS])
       ])
     ])
-  ], [
-    AC_MSG_ERROR([--with-lmdb is set to 'no', but lmdb support is required])
   ])
+  AM_CONDITIONAL([HAVE_LMDB], [test "x$LMDB_LIBS" != "x"])
 ])
index 6ac36e9c3d0e557111687bb1c1b76803dc1c1246..0ad0eb2cfd69f7c02fa0e933725c942636b2b23b 100644 (file)
@@ -4,6 +4,7 @@ pkglib_LTLIBRARIES = liblmdbbackend.la
 
 EXTRA_DIST = OBJECTFILES OBJECTLIBS
 
-liblmdbbackend_la_SOURCES = lmdbbackend.cc lmdbbackend.hh lmdb-safe.hh lmdb-safe.cc lmdb-typed.hh lmdb-typed.cc
+liblmdbbackend_la_SOURCES = lmdbbackend.cc lmdbbackend.hh lmdb-typed.hh lmdb-typed.cc \
+       ../../pdns/lmdb-safe.hh ../../pdns/lmdb-safe.cc
 liblmdbbackend_la_LDFLAGS = -module -avoid-version
-liblmdbbackend_la_LIBADD = $(LMDB_LIBS) $(BOOST_SERIALIZATION_LIBS)
\ No newline at end of file
+liblmdbbackend_la_LIBADD = $(LMDB_LIBS) $(BOOST_SERIALIZATION_LIBS)
index e6da3dd7d21d9397c645545bcf270d4ebc887f81..d9d6f17fbeb45d323d1b409b63da7d163d34b1b1 100644 (file)
@@ -1,6 +1,6 @@
 #pragma once
 #include <iostream>
-#include "lmdb-safe.hh"
+#include "pdns/lmdb-safe.hh"
 #include <boost/archive/binary_oarchive.hpp>
 #include <boost/archive/binary_iarchive.hpp>
 #include <boost/serialization/vector.hpp>
index c1bbed005bf0150d2daa67872d5d4aabd8750a01..238bc0c8ea602e7bb97557381f9e04b0bc4090ac 100644 (file)
@@ -40,6 +40,10 @@ if LIBSODIUM
 AM_CPPFLAGS +=$(LIBSODIUM_CFLAGS)
 endif
 
+if LMDB
+AM_CPPFLAGS +=$(LMDB_CFLAGS)
+endif
+
 EXTRA_DIST = \
        dnslabeltext.rl \
        dnslabeltext.cc \
@@ -263,6 +267,10 @@ pdns_server_SOURCES += decafsigners.cc
 pdns_server_LDADD += $(LIBDECAF_LIBS)
 endif
 
+if LMDB
+pdns_server_LDADD += $(LMDB_LIBS)
+endif
+
 if SQLITE3
 pdns_server_SOURCES += ssqlite3.cc ssqlite3.hh
 pdns_server_LDADD += $(SQLITE3_LIBS)
index be4a8785d173210bb6af5dbd6a53bb40c861601b..b6526088c4395a43284e2371d80a5f6596902663 100644 (file)
@@ -25,6 +25,7 @@
 #include "dnsdist-ecs.hh"
 #include "dnsdist-lua.hh"
 #include "dnsdist-protobuf.hh"
+#include "dnsdist-kvs.hh"
 
 #include "dolog.hh"
 #include "dnstap.hh"
@@ -1076,7 +1077,6 @@ private:
   std::shared_ptr<DNSAction> d_action;
 };
 
-
 #ifdef HAVE_DNS_OVER_HTTPS
 class HTTPStatusAction: public DNSAction
 {
@@ -1107,6 +1107,38 @@ private:
 };
 #endif /* HAVE_DNS_OVER_HTTPS */
 
+class KeyValueStoreLookupAction : public DNSAction
+{
+public:
+  KeyValueStoreLookupAction(std::shared_ptr<KeyValueStore>& kvs, std::shared_ptr<KeyValueLookupKey>& lookupKey, const std::string& destinationTag): d_kvs(kvs), d_key(lookupKey), d_tag(destinationTag)
+  {
+  }
+
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
+  {
+    std::string key = d_key->getKey(*dq);
+    std::string result = d_kvs->getValue(key);
+
+    if (!dq->qTag) {
+      dq->qTag = std::make_shared<QTag>();
+    }
+
+    dq->qTag->insert({d_tag, std::move(result)});
+
+    return Action::None;
+  }
+
+  std::string toString() const override
+  {
+    return "lookup key-value store based on '" + d_key->toString() + "' and set the result in tag '" + d_tag + "'";
+  }
+
+private:
+  std::shared_ptr<KeyValueStore> d_kvs;
+  std::shared_ptr<KeyValueLookupKey> d_key;
+  std::string d_tag;
+};
+
 template<typename T, typename ActionT>
 static void addAction(GlobalStateHolder<vector<T> > *someRulActions, luadnsrule_t var, std::shared_ptr<ActionT> action, boost::optional<luaruleparams_t> params) {
   setLuaSideEffect();
@@ -1416,4 +1448,8 @@ void setupLuaActions()
       return std::shared_ptr<DNSAction>(new HTTPStatusAction(status, body, contentType ? *contentType : ""));
     });
 #endif /* HAVE_DNS_OVER_HTTPS */
+
+  g_lua.writeFunction("KeyValueStoreLookupAction", [](std::shared_ptr<KeyValueStore>& kvs, std::shared_ptr<KeyValueLookupKey>& lookupKey, const std::string& destinationTag) {
+      return std::shared_ptr<DNSAction>(new KeyValueStoreLookupAction(kvs, lookupKey, destinationTag));
+    });
 }
index e642f929a8e69efbe1b83ad9e37a26b0e7fab246..4dad8fbe45f3873c032025abc094d9362954554c 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "config.h"
 #include "dnsdist.hh"
+#include "dnsdist-kvs.hh"
 #include "dnsdist-lua.hh"
 #include "dnsdist-protobuf.hh"
 
@@ -713,4 +714,25 @@ void setupLuaBindings(bool client)
     }
     return values;
   });
+
+  /* Key Value Store objects */
+  g_lua.writeFunction("KeyValueLookupKeySourceIP", []() {
+    return std::shared_ptr<KeyValueLookupKey>(new KeyValueLookupKeySourceIP());
+  });
+  g_lua.writeFunction("KeyValueLookupKeyQName", []() {
+    return std::shared_ptr<KeyValueLookupKey>(new KeyValueLookupKeyQName());
+  });
+  g_lua.writeFunction("KeyValueLookupKeyTag", [](const std::string& tag) {
+    return std::shared_ptr<KeyValueLookupKey>(new KeyValueLookupKeyTag(tag));
+  });
+
+#ifdef HAVE_LMDB
+  g_lua.writeFunction("newLMDBKVStore", [client](const std::string& fname, const std::string& dbName) {
+    if (client) {
+      return std::shared_ptr<KeyValueStore>(nullptr);
+    }
+    return std::shared_ptr<KeyValueStore>(new LMDBKVStore(fname, dbName));
+  });
+#endif /* HAVE_LMDB */
+
 }
index 46ad4dfa39de1a1f83a820c74fd663096eef739a..6089b854e0b5c7a61691f56727c5dcaeb45051d1 100644 (file)
@@ -2541,6 +2541,9 @@ try
 #ifdef HAVE_LIBSODIUM
       cout<<"libsodium ";
 #endif
+#ifdef HAVE_LMDB
+      cout<<"lmdb ";
+#endif
 #ifdef HAVE_PROTOBUF
       cout<<"protobuf ";
 #endif
index 97ce694630931b5df8bf7b67c49bf7deee2bdb1c..98c66296512ac20e5f73be86deaac4bab19456a7 100644 (file)
@@ -49,6 +49,10 @@ if HAVE_LIBCRYPTO
 AM_CPPFLAGS += $(LIBCRYPTO_INCLUDES)
 endif
 
+if HAVE_LMDB
+AM_CPPFLAGS += $(LMDB_CFLAGS)
+endif
+
 if HAVE_DNS_OVER_HTTPS
 if HAVE_LIBSSL
 AM_CPPFLAGS += $(LIBSSL_CFLAGS)
@@ -115,6 +119,7 @@ dnsdist_SOURCES = \
        dnsdist-dynblocks.hh \
        dnsdist-ecs.cc dnsdist-ecs.hh \
        dnsdist-idstate.cc \
+       dnsdist-kvs.hh dnsdist-kvs.cc \
        dnsdist-lua.hh dnsdist-lua.cc \
        dnsdist-lua-actions.cc \
        dnsdist-lua-bindings.cc \
@@ -170,6 +175,53 @@ dnsdist_SOURCES = \
        ext/incbin/incbin.h \
        ext/libbpf/libbpf.h
 
+testrunner_SOURCES = \
+       base64.hh \
+       dns.hh \
+       test-base64_cc.cc \
+       test-delaypipe_hh.cc \
+       test-dnscrypt_cc.cc \
+       test-dnsdist_cc.cc \
+       test-dnsdistdynblocks_hh.cc \
+       test-dnsdistkvs_cc.cc \
+       test-dnsdistpacketcache_cc.cc \
+       test-dnsdistrings_cc.cc \
+       test-dnsdistrules_cc.cc \
+       test-dnsparser_cc.cc \
+       test-iputils_hh.cc \
+       test-mplexer.cc \
+       cachecleaner.hh \
+       circular_buffer.hh \
+       dnsdist.hh \
+       dnsdist-cache.cc dnsdist-cache.hh \
+       dnsdist-ecs.cc dnsdist-ecs.hh \
+       dnsdist-kvs.cc dnsdist-kvs.hh \
+       dnsdist-rings.hh \
+       dnsdist-xpf.cc dnsdist-xpf.hh \
+       dnscrypt.cc dnscrypt.hh \
+       dnslabeltext.cc \
+       dnsname.cc dnsname.hh \
+       dnsparser.hh dnsparser.cc \
+       dnswriter.cc dnswriter.hh \
+       dolog.hh \
+       ednsoptions.cc ednsoptions.hh \
+       ednscookies.cc ednscookies.hh \
+       ednssubnet.cc ednssubnet.hh \
+       gettime.cc gettime.hh \
+       iputils.cc iputils.hh \
+       misc.cc misc.hh \
+       namespaces.hh \
+       pdnsexception.hh \
+       pollmplexer.cc \
+       qtype.cc qtype.hh \
+       sholder.hh \
+       sodcrypto.cc \
+       sstuff.hh \
+       statnode.cc statnode.hh \
+       threadname.hh threadname.cc \
+       testrunner.cc \
+       xpf.cc xpf.hh
+
 dnsdist_LDFLAGS = \
        $(AM_LDFLAGS) \
        $(PROGRAM_LDFLAGS) \
@@ -188,6 +240,20 @@ dnsdist_LDADD = \
        $(LIBCAP_LIBS) \
        $(IPCRYPT_LIBS)
 
+testrunner_LDFLAGS = \
+       $(AM_LDFLAGS) \
+       $(PROGRAM_LDFLAGS) \
+       $(BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS) \
+       -pthread
+
+testrunner_LDADD = \
+       $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) \
+       $(LIBSODIUM_LIBS) \
+       $(FSTRM_LIBS) \
+       $(RT_LIBS) \
+       $(SANITIZER_FLAGS) \
+       $(LIBCAP_LIBS)
+
 if HAVE_RE2
 dnsdist_LDADD += $(RE2_LIBS)
 endif
@@ -201,6 +267,13 @@ dnsdist_LDADD += $(LIBCRYPTO_LDFLAGS) $(LIBCRYPTO_LIBS)
 dnsdist_SOURCES += ipcipher.cc ipcipher.hh
 endif
 
+if HAVE_LMDB
+dnsdist_LDADD += $(LMDB_LDFLAGS) $(LMDB_LIBS)
+testrunner_LDADD += $(LMDB_LDFLAGS) $(LMDB_LIBS)
+dnsdist_SOURCES += lmdb-safe.cc lmdb-safe.hh
+testrunner_SOURCES += lmdb-safe.cc lmdb-safe.hh
+endif
+
 if HAVE_DNS_OVER_TLS
 if HAVE_GNUTLS
 dnsdist_LDADD += -lgnutls
@@ -238,51 +311,6 @@ dnsdist.$(OBJEXT): dnsmessage.pb.cc dnstap.pb.cc
 endif
 endif
 
-testrunner_SOURCES = \
-       base64.hh \
-       dns.hh \
-       test-base64_cc.cc \
-       test-delaypipe_hh.cc \
-       test-dnscrypt_cc.cc \
-       test-dnsdist_cc.cc \
-       test-dnsdistdynblocks_hh.cc \
-       test-dnsdistpacketcache_cc.cc \
-       test-dnsdistrings_cc.cc \
-       test-dnsdistrules_cc.cc \
-       test-dnsparser_cc.cc \
-       test-iputils_hh.cc \
-       test-mplexer.cc \
-       cachecleaner.hh \
-       circular_buffer.hh \
-       dnsdist.hh \
-       dnsdist-cache.cc dnsdist-cache.hh \
-       dnsdist-ecs.cc dnsdist-ecs.hh \
-       dnsdist-rings.hh \
-       dnsdist-xpf.cc dnsdist-xpf.hh \
-       dnscrypt.cc dnscrypt.hh \
-       dnslabeltext.cc \
-       dnsname.cc dnsname.hh \
-       dnsparser.hh dnsparser.cc \
-       dnswriter.cc dnswriter.hh \
-       dolog.hh \
-       ednsoptions.cc ednsoptions.hh \
-       ednscookies.cc ednscookies.hh \
-       ednssubnet.cc ednssubnet.hh \
-       gettime.cc gettime.hh \
-       iputils.cc iputils.hh \
-       misc.cc misc.hh \
-       namespaces.hh \
-       pdnsexception.hh \
-       pollmplexer.cc \
-       qtype.cc qtype.hh \
-       sholder.hh \
-       sodcrypto.cc \
-       sstuff.hh \
-       statnode.cc statnode.hh \
-       threadname.hh threadname.cc \
-       testrunner.cc \
-       xpf.cc xpf.hh
-
 if HAVE_FREEBSD
 dnsdist_SOURCES += kqueuemplexer.cc
 testrunner_SOURCES += kqueuemplexer.cc
@@ -302,20 +330,6 @@ testrunner_SOURCES += \
         portsmplexer.cc
 endif
 
-testrunner_LDFLAGS = \
-       $(AM_LDFLAGS) \
-       $(PROGRAM_LDFLAGS) \
-       $(BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS) \
-       -pthread
-
-testrunner_LDADD = \
-       $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) \
-       $(LIBSODIUM_LIBS) \
-       $(FSTRM_LIBS) \
-       $(RT_LIBS) \
-       $(SANITIZER_FLAGS) \
-       $(LIBCAP_LIBS)
-
 MANPAGES=dnsdist.1
 
 dist_man_MANS=$(MANPAGES)
index 83563abadacc821771417919d7f8442f779efc00..34b484fddab741432ea5f1386c248932ec638c34 100644 (file)
@@ -63,6 +63,7 @@ PDNS_CHECK_LUA_HPP
 
 AM_CONDITIONAL([HAVE_GNUTLS], [false])
 AM_CONDITIONAL([HAVE_LIBSSL], [false])
+AM_CONDITIONAL([HAVE_LMDB], [false])
 
 PDNS_CHECK_LIBCRYPTO
 
@@ -92,6 +93,8 @@ AS_IF([test "x$enable_dns_over_https" != "xno"], [
   ])
 ])
 
+PDNS_CHECK_LMDB
+
 AX_CXX_COMPILE_STDCXX_11([ext], [mandatory])
 
 AC_MSG_CHECKING([whether we will enable compiler security checks])
@@ -213,5 +216,9 @@ AS_IF([test "x$enable_dns_over_tls" != "xno" -o "x$enable_dns_over_https" != "xn
     [AC_MSG_NOTICE([OpenSSL: no])]
   )]
 )
+AS_IF([test "x$LMDB_LIBS" != "x"],
+  [AC_MSG_NOTICE([lmdb: yes])],
+  [AC_MSG_NOTICE([lmdb: no])]
+)
 
 AC_MSG_NOTICE([])
diff --git a/pdns/dnsdistdist/dnsdist-kvs.cc b/pdns/dnsdistdist/dnsdist-kvs.cc
new file mode 100644 (file)
index 0000000..f29e3d6
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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-kvs.hh"
+#include "dolog.hh"
+
+#ifdef HAVE_LMDB
+
+#include "lmdb-safe.hh"
+
+std::string LMDBKVStore::getValue(const std::string& key)
+{
+  string_view result;
+  try {
+    auto transaction = d_env.getROTransaction();
+    auto dbi = transaction.openDB(d_dbName, 0);
+    int rc = transaction.get(dbi, MDBInVal(key), result);
+    if (rc == 0) {
+      return result.to_string();
+    }
+    else if (rc == MDB_NOTFOUND) {
+      return std::string();
+    }
+  }
+  catch(const std::exception& e) {
+    warnlog("Error while looking up key '%s' from LMDB file '%s', database '%s': %s", key, d_fname, d_dbName);
+  }
+  return std::string();
+}
+
+#endif /* HAVE_LMDB */
diff --git a/pdns/dnsdistdist/dnsdist-kvs.hh b/pdns/dnsdistdist/dnsdist-kvs.hh
new file mode 100644 (file)
index 0000000..aa56545
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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"
+
+class KeyValueLookupKey
+{
+public:
+  virtual ~KeyValueLookupKey()
+  {
+  }
+  virtual std::string getKey(const DNSQuestion&) = 0;
+  virtual std::string toString() const = 0;
+};
+
+class KeyValueLookupKeySourceIP: public KeyValueLookupKey
+{
+public:
+  std::string getKey(const DNSQuestion& dq) override
+  {
+    std::string key;
+    if (dq.remote->sin4.sin_family == AF_INET) {
+      key = std::string(reinterpret_cast<const char*>(&dq.remote->sin4.sin_addr.s_addr), sizeof(dq.remote->sin4.sin_addr.s_addr));
+    }
+    else if (dq.remote->sin4.sin_family == AF_INET6) {
+      key = std::string(reinterpret_cast<const char*>(&dq.remote->sin6.sin6_addr.s6_addr), sizeof(dq.remote->sin6.sin6_addr.s6_addr));
+    }
+    return key;
+  }
+
+  std::string toString() const override
+  {
+    return "source IP";
+  }
+};
+
+class KeyValueLookupKeyQName: public KeyValueLookupKey
+{
+public:
+  std::string getKey(const DNSQuestion& dq) override
+  {
+    return dq.qname->toDNSStringLC();
+  }
+
+  std::string toString() const override
+  {
+    return "qname";
+  }
+};
+
+class KeyValueLookupKeyTag: public KeyValueLookupKey
+{
+public:
+  KeyValueLookupKeyTag(const std::string& tag): d_tag(tag)
+  {
+  }
+
+  std::string getKey(const DNSQuestion& dq) override
+  {
+    std::string key;
+    if (dq.qTag) {
+      const auto& it = dq.qTag->find(d_tag);
+      if (it != dq.qTag->end()) {
+        key = it->second;
+      }
+    }
+    return key;
+  }
+
+  std::string toString() const override
+  {
+    return " value of the tag named '" + d_tag + '"';
+  }
+
+private:
+  std::string d_tag;
+};
+
+class KeyValueStore
+{
+public:
+  virtual ~KeyValueStore()
+  {
+  }
+
+  virtual std::string getValue(const std::string& key) = 0;
+};
+
+#ifdef HAVE_LMDB
+
+#include "lmdb-safe.hh"
+
+class LMDBKVStore: public KeyValueStore
+{
+public:
+  LMDBKVStore(const std::string& fname, const std::string& dbName): d_env(fname.c_str(), MDB_NOSUBDIR, 0600), d_fname(fname), d_dbName(dbName)
+  {
+  }
+
+  std::string getValue(const std::string& key) override;
+
+private:
+  MDBEnv d_env;
+  std::string d_fname;
+  std::string d_dbName;
+};
+
+#endif /* HAVE_LMDB */
diff --git a/pdns/dnsdistdist/lmdb-safe.cc b/pdns/dnsdistdist/lmdb-safe.cc
new file mode 120000 (symlink)
index 0000000..bdc1219
--- /dev/null
@@ -0,0 +1 @@
+../lmdb-safe.cc
\ No newline at end of file
diff --git a/pdns/dnsdistdist/lmdb-safe.hh b/pdns/dnsdistdist/lmdb-safe.hh
new file mode 120000 (symlink)
index 0000000..4e3d388
--- /dev/null
@@ -0,0 +1 @@
+../lmdb-safe.hh
\ No newline at end of file
diff --git a/pdns/dnsdistdist/m4/pdns_check_lmdb.m4 b/pdns/dnsdistdist/m4/pdns_check_lmdb.m4
new file mode 120000 (symlink)
index 0000000..2b4a9ee
--- /dev/null
@@ -0,0 +1 @@
+../../../m4/pdns_check_lmdb.m4
\ No newline at end of file
diff --git a/pdns/dnsdistdist/test-dnsdistkvs_cc.cc b/pdns/dnsdistdist/test-dnsdistkvs_cc.cc
new file mode 100644 (file)
index 0000000..00e5300
--- /dev/null
@@ -0,0 +1,45 @@
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_NO_MAIN
+
+#include <boost/test/unit_test.hpp>
+
+#include "dnsdist-kvs.hh"
+
+BOOST_AUTO_TEST_SUITE(dnsdistkvs_cc)
+
+#ifdef HAVE_LMDB
+BOOST_AUTO_TEST_CASE(test_LMDB) {
+
+  auto lmdb = make_unique<LMDBKVStore>("/data/Dumps/lmdb", "db-name");
+  auto key = make_unique<KeyValueLookupKeySourceIP>();
+
+  DNSName qname("powerdns.com.");
+  uint16_t qtype = QType::A;
+  uint16_t qclass = QClass::IN;
+  ComboAddress lc("127.0.0.1:53");
+  ComboAddress rem("127.0.0.1:42");
+  struct dnsheader dh;
+  memset(&dh, 0, sizeof(dh));
+  size_t bufferSize = 0;
+  size_t queryLen = 0;
+  bool isTcp = false;
+  struct timespec queryRealTime;
+  gettime(&queryRealTime, true);
+  struct timespec expiredTime;
+  /* the internal QPS limiter does not use the real time */
+  gettime(&expiredTime);
+
+  DNSQuestion dq(&qname, qtype, qclass, qname.wirelength(), &lc, &rem, &dh, bufferSize, queryLen, isTcp, &queryRealTime);
+
+  DTime dt;
+  dt.set();
+  for (size_t idx = 0; idx < 10000000; idx++) {
+    std::string value = lmdb->getValue(key->getKey(dq));
+    BOOST_CHECK_EQUAL(value, "this is the value of the tag");
+  }
+  cerr<<dt.udiff()/1000/1000<<endl;
+}
+#endif /* HAVE_LMDB */
+
+BOOST_AUTO_TEST_SUITE_END()