From f942c74cb34c701eae229ee13322cc2a70ea9fc2 Mon Sep 17 00:00:00 2001
From: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
Date: Thu, 20 Nov 2014 15:09:44 -0500
Subject: [PATCH] daemon: remove dhtcpp from tree, use from contrib or system

Refs #60968

Change-Id: I4f91136f29261762f87c2cd0d575a04d6635e27b
---
 daemon/configure.ac                     |    8 +-
 daemon/src/Makefile.am                  |    8 +
 daemon/src/ringdht/Makefile.am          |    5 +-
 daemon/src/ringdht/dhtcpp/Makefile.am   |   16 -
 daemon/src/ringdht/dhtcpp/crypto.cpp    |  404 ----
 daemon/src/ringdht/dhtcpp/crypto.h      |  139 --
 daemon/src/ringdht/dhtcpp/dht.cpp       | 2532 -----------------------
 daemon/src/ringdht/dhtcpp/dht.h         |  594 ------
 daemon/src/ringdht/dhtcpp/dhtrunner.cpp |  329 ---
 daemon/src/ringdht/dhtcpp/dhtrunner.h   |  165 --
 daemon/src/ringdht/dhtcpp/infohash.cpp  |   82 -
 daemon/src/ringdht/dhtcpp/infohash.h    |  159 --
 daemon/src/ringdht/dhtcpp/securedht.cpp |  292 ---
 daemon/src/ringdht/dhtcpp/securedht.h   |  158 --
 daemon/src/ringdht/dhtcpp/serialize.h   |  316 ---
 daemon/src/ringdht/dhtcpp/value.cpp     |  221 --
 daemon/src/ringdht/dhtcpp/value.h       |  328 ---
 daemon/src/ringdht/ringaccount.cpp      |    2 +-
 daemon/src/ringdht/ringaccount.h        |    3 +-
 daemon/tools/Makefile.am                |    6 +-
 daemon/tools/dhtnode.cpp                |    3 +-
 21 files changed, 21 insertions(+), 5749 deletions(-)
 delete mode 100644 daemon/src/ringdht/dhtcpp/Makefile.am
 delete mode 100644 daemon/src/ringdht/dhtcpp/crypto.cpp
 delete mode 100644 daemon/src/ringdht/dhtcpp/crypto.h
 delete mode 100644 daemon/src/ringdht/dhtcpp/dht.cpp
 delete mode 100644 daemon/src/ringdht/dhtcpp/dht.h
 delete mode 100644 daemon/src/ringdht/dhtcpp/dhtrunner.cpp
 delete mode 100644 daemon/src/ringdht/dhtcpp/dhtrunner.h
 delete mode 100644 daemon/src/ringdht/dhtcpp/infohash.cpp
 delete mode 100644 daemon/src/ringdht/dhtcpp/infohash.h
 delete mode 100644 daemon/src/ringdht/dhtcpp/securedht.cpp
 delete mode 100644 daemon/src/ringdht/dhtcpp/securedht.h
 delete mode 100644 daemon/src/ringdht/dhtcpp/serialize.h
 delete mode 100644 daemon/src/ringdht/dhtcpp/value.cpp
 delete mode 100644 daemon/src/ringdht/dhtcpp/value.h

diff --git a/daemon/configure.ac b/daemon/configure.ac
index 3517cd2fa5..41a789f04f 100644
--- a/daemon/configure.ac
+++ b/daemon/configure.ac
@@ -544,7 +544,12 @@ AC_ARG_ENABLE([dht],
 
 AS_IF([test "x$enable_dht" != "xno" -a "$HAVE_GNUTLS" -eq 1],
       [AC_DEFINE([HAVE_DHT], 1, [Define to enable dht])
-       AM_CONDITIONAL(USE_DHT, true)],
+       PKG_CHECK_MODULES([OPENDHT], opendht,
+                         AC_DEFINE([HAVE_DHT], 1, [Define to enable dht])
+                         AM_CONDITIONAL(USE_DHT, true),
+                         AC_DEFINE([HAVE_DHT], 0, [Define to enable dht])
+                         AM_CONDITIONAL(USE_DHT, false)
+                         AC_MSG_WARN([Missing OpenDTH]))],
       [AC_DEFINE([HAVE_DHT], 0, [Define to enable dht])
        AM_CONDITIONAL(USE_DHT, false)])
 
@@ -601,7 +606,6 @@ AC_CONFIG_FILES([Makefile \
                  src/im/Makefile \
                  src/iax/Makefile \
                  src/ringdht/Makefile \
-                 src/ringdht/dhtcpp/Makefile \
                  src/audio/Makefile \
                  src/audio/audiortp/Makefile \
                  src/audio/pulseaudio/Makefile \
diff --git a/daemon/src/Makefile.am b/daemon/src/Makefile.am
index ccb70182fb..5c36b9eb77 100644
--- a/daemon/src/Makefile.am
+++ b/daemon/src/Makefile.am
@@ -68,6 +68,10 @@ libsflphone_la_LDFLAGS = \
 		$(IM_LIB) \
 		$(PCRE_LIBS)
 
+if USE_DHT
+libsflphone_la_LDFLAGS += $(OPENDHT_LIBS)
+endif
+
 libsflphone_la_CFLAGS = \
 		@ZRTPCPP_CFLAGS@ \
 		@PJPROJECT_CFLAGS@ \
@@ -78,6 +82,10 @@ libsflphone_la_CFLAGS = \
 		@SPEEXDSP_CFLAGS@ \
 		$(TLS_CFLAGS)
 
+if USE_DHT
+libsflphone_la_CFLAGS += $(OPENDHT_CFLAGS)
+endif
+
 libsflphone_la_SOURCES = conference.cpp \
 		account_factory.cpp \
 		call_factory.cpp \
diff --git a/daemon/src/ringdht/Makefile.am b/daemon/src/ringdht/Makefile.am
index 1cf4735900..1936cee77d 100644
--- a/daemon/src/ringdht/Makefile.am
+++ b/daemon/src/ringdht/Makefile.am
@@ -5,10 +5,7 @@ if USE_DHT
 noinst_LTLIBRARIES = libringacc.la
 libringacc_la_CXXFLAGS = @CXXFLAGS@
 
-SUBDIRS = dhtcpp
-
-libringacc_la_LIBADD = \
-        dhtcpp/libdhtcpp.la
+libringacc_la_LIBADD = $(DHT_LIBS)
 
 libringacc_la_SOURCES = \
         ringaccount.cpp \
diff --git a/daemon/src/ringdht/dhtcpp/Makefile.am b/daemon/src/ringdht/dhtcpp/Makefile.am
deleted file mode 100644
index 827282ec38..0000000000
--- a/daemon/src/ringdht/dhtcpp/Makefile.am
+++ /dev/null
@@ -1,16 +0,0 @@
-noinst_LTLIBRARIES = libdhtcpp.la
-libdhtcpp_la_CXXFLAGS = @CXXFLAGS@
-
-libdhtcpp_la_SOURCES = \
-        dht.cpp \
-        infohash.cpp \
-        value.cpp \
-        crypto.cpp \
-        securedht.cpp \
-        dhtrunner.cpp \
-        dht.h \
-        infohash.h \
-        value.h \
-        crypto.h \
-        securedht.h \
-        dhtrunner.h
diff --git a/daemon/src/ringdht/dhtcpp/crypto.cpp b/daemon/src/ringdht/dhtcpp/crypto.cpp
deleted file mode 100644
index c358039f66..0000000000
--- a/daemon/src/ringdht/dhtcpp/crypto.cpp
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- *  Copyright (C) 2014 Savoir-Faire Linux Inc.
- *  Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  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.
- *
- *  Additional permission under GNU GPL version 3 section 7:
- *
- *  If you modify this program, or any covered work, by linking or
- *  combining it with the OpenSSL project's OpenSSL library (or a
- *  modified version of that library), containing parts covered by the
- *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
- *  grants you additional permission to convey the resulting work.
- *  Corresponding Source for a non-source form of such a combination
- *  shall include the source code for the parts of OpenSSL used as well
- *  as that of the covered work.
- */
-
-#include "securedht.h"
-
-extern "C" {
-#include <gnutls/gnutls.h>
-#include <gnutls/abstract.h>
-#include <gnutls/x509.h>
-}
-
-#include <random>
-#include <sstream>
-#include <random>
-
-static gnutls_digest_algorithm_t get_dig_for_pub(gnutls_pubkey_t pubkey)
-{
-    gnutls_digest_algorithm_t dig;
-    int result = gnutls_pubkey_get_preferred_hash_algorithm(pubkey, &dig, nullptr);
-    if (result < 0)
-        return GNUTLS_DIG_UNKNOWN;
-    return dig;
-}
-
-static gnutls_digest_algorithm_t get_dig(gnutls_x509_crt_t crt)
-{
-    gnutls_pubkey_t pubkey;
-    gnutls_pubkey_init(&pubkey);
-
-    int result = gnutls_pubkey_import_x509(pubkey, crt, 0);
-    if (result < 0) {
-        gnutls_pubkey_deinit(pubkey);
-        return GNUTLS_DIG_UNKNOWN;
-    }
-
-    gnutls_digest_algorithm_t dig = get_dig_for_pub(pubkey);
-    gnutls_pubkey_deinit(pubkey);
-    return dig;
-}
-
-namespace dht {
-namespace crypto {
-
-PrivateKey::PrivateKey(gnutls_x509_privkey_t k) : x509_key(k)
-{
-    gnutls_privkey_init(&key);
-    if (gnutls_privkey_import_x509(key, k, GNUTLS_PRIVKEY_IMPORT_COPY) != GNUTLS_E_SUCCESS) {
-        key = nullptr;
-        throw DhtException("Can't load private key !");
-    }
-}
-
-PrivateKey::PrivateKey(const Blob& import)
-{
-    const gnutls_datum_t dt {(unsigned char*)import.data(), static_cast<unsigned>(import.size())};
-    gnutls_x509_privkey_init(&x509_key);
-    int err = gnutls_x509_privkey_import(x509_key, &dt, GNUTLS_X509_FMT_PEM);
-    if (err != GNUTLS_E_SUCCESS) {
-        err = gnutls_x509_privkey_import(x509_key, &dt, GNUTLS_X509_FMT_DER);
-    }
-    if (err != GNUTLS_E_SUCCESS) {
-        gnutls_x509_privkey_deinit(x509_key);
-        throw DhtException("Can't load private key !");
-    }
-    gnutls_privkey_init(&key);
-    if (gnutls_privkey_import_x509(key, x509_key, GNUTLS_PRIVKEY_IMPORT_COPY) != GNUTLS_E_SUCCESS) {
-        throw DhtException("Can't load private key !");
-    }
-}
-
-PrivateKey::~PrivateKey()
-{
-    if (key) {
-        gnutls_privkey_deinit(key);
-        key = nullptr;
-    }
-    if (x509_key) {
-        gnutls_x509_privkey_deinit(x509_key);
-        x509_key = nullptr;
-    }
-}
-
-PrivateKey&
-PrivateKey::operator=(PrivateKey&& o) noexcept
-{
-    if (key) {
-        gnutls_privkey_deinit(key);
-        key = nullptr;
-    }
-    if (x509_key) {
-        gnutls_x509_privkey_deinit(x509_key);
-        x509_key = nullptr;
-    }
-    key = o.key; x509_key = o.x509_key;
-    o.key = nullptr; o.x509_key = nullptr;
-    return *this;
-}
-
-Blob
-PrivateKey::sign(const Blob& data) const
-{
-    if (!key)
-        throw DhtException("Can't sign data: no private key set !");
-    gnutls_datum_t sig;
-    const gnutls_datum_t dat {(unsigned char*)data.data(), (unsigned)data.size()};
-    if (gnutls_privkey_sign_data(key, GNUTLS_DIG_SHA512, 0, &dat, &sig) != GNUTLS_E_SUCCESS)
-        throw DhtException("Can't sign data !");
-    Blob ret(sig.data, sig.data+sig.size);
-    gnutls_free(sig.data);
-    return ret;
-}
-
-Blob
-PrivateKey::decrypt(const Blob& cipher) const
-{
-    if (!key)
-        throw DhtException("Can't decrypt data without private key !");
-    const gnutls_datum_t dat {(uint8_t*)cipher.data(), (unsigned)cipher.size()};
-    gnutls_datum_t out;
-    if (gnutls_privkey_decrypt_data(key, 0, &dat, &out) != GNUTLS_E_SUCCESS)
-        throw DhtException("Can't decrypt data !");
-    Blob ret {out.data, out.data+out.size};
-    gnutls_free(out.data);
-    return ret;
-}
-
-Blob
-PrivateKey::serialize() const
-{
-    if (!x509_key)
-        return {};
-    size_t buf_sz = 8192;
-    Blob buffer;
-    buffer.resize(buf_sz);
-    int err = gnutls_x509_privkey_export_pkcs8(x509_key, GNUTLS_X509_FMT_PEM, nullptr, GNUTLS_PKCS_PLAIN, buffer.data(), &buf_sz);
-    if (err != GNUTLS_E_SUCCESS) {
-        std::cerr << "Could not export certificate - " << gnutls_strerror(err) << std::endl;
-        return {};
-    }
-    buffer.resize(buf_sz);
-    return buffer;
-}
-
-PublicKey
-PrivateKey::getPublicKey() const
-{
-    gnutls_pubkey_t pk;
-    gnutls_pubkey_init(&pk);
-    PublicKey pk_ret {pk};
-    if (gnutls_pubkey_import_privkey(pk, key, GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN, 0) != GNUTLS_E_SUCCESS)
-        return {};
-    return pk_ret;
-}
-
-PublicKey::PublicKey(const Blob& dat) : pk(nullptr)
-{
-    unpackBlob(dat);
-}
-
-PublicKey::~PublicKey()
-{
-    if (pk) {
-        gnutls_pubkey_deinit(pk);
-        pk = nullptr;
-    }
-}
-
-PublicKey&
-PublicKey::operator=(PublicKey&& o) noexcept
-{
-    if (pk)
-        gnutls_pubkey_deinit(pk);
-    pk = o.pk;
-    o.pk = nullptr;
-    return *this;
-}
-
-void
-PublicKey::pack(Blob& b) const
-{
-    std::vector<uint8_t> tmp(2048);
-    size_t sz = tmp.size();
-    int err = gnutls_pubkey_export(pk, GNUTLS_X509_FMT_DER, tmp.data(), &sz);
-    if (err != GNUTLS_E_SUCCESS)
-        throw std::invalid_argument(std::string("Could not export public key: ") + gnutls_strerror(err));
-    tmp.resize(sz);
-    serialize<Blob>(tmp, b);
-}
-
-void
-PublicKey::unpack(Blob::const_iterator& begin, Blob::const_iterator& end)
-{
-    Blob tmp = deserialize<Blob>(begin, end);
-    if (pk)
-        gnutls_pubkey_deinit(pk);
-    gnutls_pubkey_init(&pk);
-    const gnutls_datum_t dat {(uint8_t*)tmp.data(), (unsigned)tmp.size()};
-    int err = gnutls_pubkey_import(pk, &dat, GNUTLS_X509_FMT_DER);
-    if (err != GNUTLS_E_SUCCESS)
-        throw std::invalid_argument(std::string("Could not read public key: ") + gnutls_strerror(err));
-}
-
-bool
-PublicKey::checkSignature(const Blob& data, const Blob& signature) const {
-    if (!pk)
-        return false;
-    const gnutls_datum_t sig {(uint8_t*)signature.data(), (unsigned)signature.size()};
-    const gnutls_datum_t dat {(uint8_t*)data.data(), (unsigned)data.size()};
-    int rc = gnutls_pubkey_verify_data2(pk, GNUTLS_SIGN_RSA_SHA512, 0, &dat, &sig);
-    return rc >= 0;
-}
-
-Blob
-PublicKey::encrypt(const Blob& data) const
-{
-    if (!pk)
-        throw DhtException("Can't read public key !");
-    const gnutls_datum_t dat {(uint8_t*)data.data(), (unsigned)data.size()};
-    gnutls_datum_t encrypted;
-    int err = gnutls_pubkey_encrypt_data(pk, 0, &dat, &encrypted);
-    if (err != GNUTLS_E_SUCCESS)
-        throw DhtException(std::string("Can't encrypt data: ") + gnutls_strerror(err));
-    Blob ret {encrypted.data, encrypted.data+encrypted.size};
-    gnutls_free(encrypted.data);
-    return ret;
-}
-
-InfoHash
-PublicKey::getId() const
-{
-    InfoHash id;
-    size_t sz = id.size();
-    gnutls_pubkey_get_key_id(pk, 0, id.data(), &sz);
-    return id;
-}
-
-Certificate::Certificate(const Blob& certData) : cert(nullptr)
-{
-    unpackBlob(certData);
-}
-
-Certificate&
-Certificate::operator=(Certificate&& o) noexcept
-{
-    if (cert)
-        gnutls_x509_crt_deinit(cert);
-    cert = o.cert;
-    o.cert = nullptr;
-    return *this;
-}
-
-void
-Certificate::unpack(Blob::const_iterator& begin, Blob::const_iterator& end)
-{
-    if (cert)
-        gnutls_x509_crt_deinit(cert);
-    gnutls_x509_crt_init(&cert);
-    const gnutls_datum_t crt_dt {(uint8_t*)&(*begin), (unsigned)(end-begin)};
-    int err = gnutls_x509_crt_import(cert, &crt_dt, GNUTLS_X509_FMT_PEM);
-    if (err != GNUTLS_E_SUCCESS) {
-        cert = nullptr;
-        throw std::invalid_argument(std::string("Could not read certificate - ") + gnutls_strerror(err));
-    }
-}
-
-void
-Certificate::pack(Blob& b) const
-{
-    auto b_size = b.size();
-    size_t buf_sz = 8192;
-    b.resize(b_size + buf_sz);
-    int err = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, b.data()+b_size, &buf_sz);
-    if (err != GNUTLS_E_SUCCESS) {
-        std::cerr << "Could not export certificate - " << gnutls_strerror(err) << std::endl;
-        b.resize(b_size);
-    }
-    b.resize(b_size + buf_sz);
-}
-
-Certificate::~Certificate()
-{
-    if (cert) {
-        gnutls_x509_crt_deinit(cert);
-        cert = nullptr;
-    }
-}
-
-PublicKey
-Certificate::getPublicKey() const
-{
-    gnutls_pubkey_t pk;
-    gnutls_pubkey_init(&pk);
-    PublicKey pk_ret(pk);
-    if (gnutls_pubkey_import_x509(pk, cert, 0) != GNUTLS_E_SUCCESS)
-        return {};
-    return pk_ret;
-}
-
-PrivateKey
-PrivateKey::generate()
-{
-    gnutls_x509_privkey_t key;
-    if (gnutls_x509_privkey_init(&key) != GNUTLS_E_SUCCESS)
-        throw std::runtime_error("Can't initialize private key.");
-    if (gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0) != GNUTLS_E_SUCCESS) {
-        gnutls_x509_privkey_deinit(key);
-        throw std::runtime_error("Can't initialize RSA key pair.");
-    }
-    return PrivateKey{key};
-}
-
-crypto::Identity
-generateIdentity(const std::string& name, crypto::Identity ca)
-{
-    int rc = gnutls_global_init();
-    if (rc != GNUTLS_E_SUCCESS)
-        return {};
-
-    auto shared_key = std::make_shared<PrivateKey>(PrivateKey::generate());
-
-    gnutls_x509_crt_t cert;
-    if (gnutls_x509_crt_init(&cert) != GNUTLS_E_SUCCESS)
-        return {};
-    auto shared_crt = std::make_shared<Certificate>(cert);
-
-    gnutls_x509_crt_set_activation_time(cert, time(NULL));
-    gnutls_x509_crt_set_expiration_time(cert, time(NULL) + (700 * 24 * 60 * 60));
-    if (gnutls_x509_crt_set_key(cert, shared_key->x509_key) != GNUTLS_E_SUCCESS) {
-        std::cerr << "Error when setting certificate key" << std::endl;
-        return {};
-    }
-    if (gnutls_x509_crt_set_version(cert, 3) != GNUTLS_E_SUCCESS) {
-        std::cerr << "Error when setting certificate version" << std::endl;
-        return {};
-    }
-
-    // TODO: compute the subject key using the recommended RFC method
-    auto pk_id = shared_key->getPublicKey().getId();
-    gnutls_x509_crt_set_subject_key_id(cert, &pk_id, sizeof(pk_id));
-
-    gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, name.data(), name.length());
-
-    const std::string& uid_str = shared_key->getPublicKey().getId().toString();
-    gnutls_x509_crt_set_dn_by_oid(cert, GNUTLS_OID_LDAP_UID, 0, uid_str.data(), uid_str.length());
-
-    {
-        std::random_device rdev;
-        std::uniform_int_distribution<uint64_t> dist{};
-        uint64_t cert_serial = dist(rdev);
-        gnutls_x509_crt_set_serial(cert, &cert_serial, sizeof(cert_serial));
-    }
-
-    if (ca.first && ca.second) {
-        gnutls_x509_crt_set_key_usage (cert, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_DATA_ENCIPHERMENT);
-        //if (gnutls_x509_crt_sign2(cert, ca.second->cert, ca.first->x509_key, get_dig(cert), 0) != GNUTLS_E_SUCCESS) {
-        if (gnutls_x509_crt_privkey_sign(cert, ca.second->cert, ca.first->key, get_dig(cert), 0) != GNUTLS_E_SUCCESS) {
-            std::cerr << "Error when signing certificate" << std::endl;
-            return {};
-        }
-    } else {
-        gnutls_x509_crt_set_ca_status(cert, 1);
-        gnutls_x509_crt_set_key_usage (cert, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_CERT_SIGN);
-        //if (gnutls_x509_crt_sign2(cert, cert, key, get_dig(cert), 0) != GNUTLS_E_SUCCESS) {
-        if (gnutls_x509_crt_privkey_sign(cert, cert, shared_key->key, get_dig(cert), 0) != GNUTLS_E_SUCCESS) {
-            std::cerr << "Error when signing certificate" << std::endl;
-            return {};
-        }
-    }
-
-    gnutls_global_deinit();
-
-    return {shared_key, shared_crt};
-}
-
-}
-
-}
diff --git a/daemon/src/ringdht/dhtcpp/crypto.h b/daemon/src/ringdht/dhtcpp/crypto.h
deleted file mode 100644
index 5b0606f293..0000000000
--- a/daemon/src/ringdht/dhtcpp/crypto.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- *  Copyright (C) 2014 Savoir-Faire Linux Inc.
- *  Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  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.
- *
- *  Additional permission under GNU GPL version 3 section 7:
- *
- *  If you modify this program, or any covered work, by linking or
- *  combining it with the OpenSSL project's OpenSSL library (or a
- *  modified version of that library), containing parts covered by the
- *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
- *  grants you additional permission to convey the resulting work.
- *  Corresponding Source for a non-source form of such a combination
- *  shall include the source code for the parts of OpenSSL used as well
- *  as that of the covered work.
- */
-
-#pragma once
-
-#include "serialize.h"
-
-extern "C" {
-#include <gnutls/gnutls.h>
-#include <gnutls/abstract.h>
-#include <gnutls/x509.h>
-}
-
-#include <vector>
-#include <memory>
-
-namespace dht {
-namespace crypto {
-
-struct PrivateKey;
-struct Certificate;
-
-typedef std::pair<std::shared_ptr<PrivateKey>, std::shared_ptr<Certificate>> Identity;
-
-/**
- * Generate an RSA key pair (2048 bits) and a certificate.
- * If a certificate authority (ca) is provided, it will be used to
- * sign the certificate, otherwise the certificate will be self-signed.
- */
-Identity generateIdentity(const std::string& name = "dhtnode", Identity ca = {});
-
-struct PublicKey : public Serializable
-{
-    PublicKey() {}
-    PublicKey(gnutls_pubkey_t k) : pk(k) {}
-    PublicKey(const Blob& pk);
-    PublicKey(PublicKey&& o) noexcept : pk(o.pk) { o.pk = nullptr; };
-
-    ~PublicKey();
-    operator bool() const { return pk; }
-
-    PublicKey& operator=(PublicKey&& o) noexcept;
-
-    InfoHash getId() const;
-    bool checkSignature(const Blob& data, const Blob& signature) const;
-    Blob encrypt(const Blob&) const;
-
-    void pack(Blob& b) const override;
-
-    void unpack(Blob::const_iterator& begin, Blob::const_iterator& end) override;
-
-    gnutls_pubkey_t pk {};
-private:
-    PublicKey(const PublicKey&) = delete;
-    PublicKey& operator=(const PublicKey&) = delete;
-};
-
-struct PrivateKey
-{
-    PrivateKey() {}
-    //PrivateKey(gnutls_privkey_t k) : key(k) {}
-    PrivateKey(gnutls_x509_privkey_t k);
-    PrivateKey(PrivateKey&& o) noexcept : key(o.key), x509_key(o.x509_key)
-        { o.key = nullptr; o.x509_key = nullptr; };
-    PrivateKey& operator=(PrivateKey&& o) noexcept;
-
-    PrivateKey(const Blob& import);
-    ~PrivateKey();
-    operator bool() const { return key; }
-    PublicKey getPublicKey() const;
-    Blob serialize() const;
-    Blob sign(const Blob&) const;
-    Blob decrypt(const Blob& cypher) const;
-
-    /**
-     * Generate a new RSA key pair
-     */
-    static PrivateKey generate();
-
-private:
-    PrivateKey(const PrivateKey&) = delete;
-    PrivateKey& operator=(const PrivateKey&) = delete;
-    gnutls_privkey_t key {};
-    gnutls_x509_privkey_t x509_key {};
-
-    friend dht::crypto::Identity dht::crypto::generateIdentity(const std::string&, dht::crypto::Identity);
-};
-
-struct Certificate : public Serializable {
-    Certificate() {}
-    Certificate(gnutls_x509_crt_t crt) : cert(crt) {}
-    Certificate(const Blob& crt);
-    Certificate(Certificate&& o) noexcept : cert(o.cert) { o.cert = nullptr; };
-    Certificate& operator=(Certificate&& o) noexcept;
-
-    ~Certificate();
-    operator bool() const { return cert; }
-    PublicKey getPublicKey() const;
-    void pack(Blob& b) const override;
-    void unpack(Blob::const_iterator& begin, Blob::const_iterator& end) override;
-
-private:
-    Certificate(const Certificate&) = delete;
-    Certificate& operator=(const Certificate&) = delete;
-    gnutls_x509_crt_t cert {};
-
-    friend dht::crypto::Identity dht::crypto::generateIdentity(const std::string&, dht::crypto::Identity);
-};
-
-
-}
-}
diff --git a/daemon/src/ringdht/dhtcpp/dht.cpp b/daemon/src/ringdht/dhtcpp/dht.cpp
deleted file mode 100644
index 09b6292683..0000000000
--- a/daemon/src/ringdht/dhtcpp/dht.cpp
+++ /dev/null
@@ -1,2532 +0,0 @@
-/*
-Copyright (c) 2009-2014 Juliusz Chroboczek
-Copyright (c) 2014 Savoir-Faire Linux Inc.
-
-Authors : Adrien Béraud <adrien.beraud@savoirfairelinux.com>,
-          Juliusz Chroboczek <jch@pps.univ–paris–diderot.fr>
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-*/
-
-#include "dht.h"
-
-extern "C" {
-#include <gnutls/gnutls.h>
-}
-
-#include <sys/time.h>
-
-#ifndef _WIN32
-#include <arpa/inet.h>
-#include <sys/types.h>
-
-#else
-#include <w32api.h>
-#define WINVER WindowsXP
-#include <ws2tcpip.h>
-#endif
-
-#include <algorithm>
-#include <sstream>
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <cstdarg>
-#include <cstring>
-
-#ifndef MSG_CONFIRM
-#define MSG_CONFIRM 0
-#endif
-
-#ifdef _WIN32
-
-#define EAFNOSUPPORT WSAEAFNOSUPPORT
-static bool
-set_nonblocking(int fd, int nonblocking)
-{
-    unsigned long mode = !!nonblocking;
-    int rc = ioctlsocket(fd, FIONBIO, &mode);
-    if (rc != 0)
-        errno = WSAGetLastError();
-    return rc == 0;
-}
-
-extern const char *inet_ntop(int, const void *, char *, socklen_t);
-
-#else
-
-static bool
-set_nonblocking(int fd, int nonblocking)
-{
-    int rc = fcntl(fd, F_GETFL, 0);
-    if (rc < 0)
-        return false;
-    rc = fcntl(fd, F_SETFL, nonblocking?(rc | O_NONBLOCK):(rc & ~O_NONBLOCK));
-    if (rc < 0)
-        return false;
-    return true;
-}
-
-#endif
-
-#define WANT4 1
-#define WANT6 2
-
-static std::mt19937 rd {std::random_device{}()};
-static std::uniform_int_distribution<uint8_t> rand_byte;
-
-static const uint8_t v4prefix[16] = {
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0
-};
-
-static std::string
-to_hex(const uint8_t *buf, size_t buflen)
-{
-    std::stringstream s;
-    s << std::hex;
-    for (size_t i = 0; i < buflen; i++)
-        s << std::setfill('0') << std::setw(2) << (unsigned)buf[i];
-    s << std::dec;
-    return s.str();
-}
-
-namespace dht {
-
-const Dht::TransPrefix Dht::TransPrefix::PING = {"pn"};
-const Dht::TransPrefix Dht::TransPrefix::FIND_NODE  = {"fn"};
-const Dht::TransPrefix Dht::TransPrefix::GET_VALUES  = {"gp"};
-const Dht::TransPrefix Dht::TransPrefix::ANNOUNCE_VALUES  = {"ap"};
-
-const uint8_t Dht::my_v[9] = "1:v4:RNG";
-
-static constexpr InfoHash zeroes {};
-static constexpr InfoHash ones = {std::array<uint8_t, HASH_LEN>{
-    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-    0xFF, 0xFF, 0xFF, 0xFF
-}};
-
-const long unsigned Dht::MAX_REQUESTS_PER_SEC = 400;
-
-void
-Dht::setLoggers(LogMethod&& error, LogMethod&& warn, LogMethod&& debug)
-{
-    DHT_DEBUG = std::move(debug);
-    DHT_WARN = std::move(warn);
-    DHT_ERROR = std::move(error);
-}
-
-Dht::Status
-Dht::getStatus(sa_family_t af) const
-{
-    unsigned good = 0, dubious = 0, cached = 0, incoming = 0;
-    int tot = getNodesStats(af, &good, &dubious, &cached, &incoming);
-    if (tot < 1)
-        return Status::Disconnected;
-    else if (good < 4)
-        return Status::Connecting;
-    return Status::Connected;
-}
-
-bool
-Dht::isRunning(sa_family_t af) const
-{
-    return (af == AF_INET  && dht_socket  >= 0)
-        || (af == AF_INET6 && dht_socket6 >= 0);
-}
-
-bool
-Dht::isMartian(const sockaddr *sa, socklen_t len)
-{
-    // Check that sa_family can be accessed safely
-    if (!sa || len < sizeof(sockaddr_in))
-        return true;
-
-    switch(sa->sa_family) {
-    case AF_INET: {
-        sockaddr_in *sin = (sockaddr_in*)sa;
-        const uint8_t *address = (const uint8_t*)&sin->sin_addr;
-        return sin->sin_port == 0 ||
-            (address[0] == 0) ||
-            (address[0] == 127) ||
-            ((address[0] & 0xE0) == 0xE0);
-    }
-    case AF_INET6: {
-        if (len < sizeof(sockaddr_in6))
-            return true;
-        sockaddr_in6 *sin6 = (sockaddr_in6*)sa;
-        const uint8_t *address = (const uint8_t*)&sin6->sin6_addr;
-        return sin6->sin6_port == 0 ||
-            (address[0] == 0xFF) ||
-            (address[0] == 0xFE && (address[1] & 0xC0) == 0x80) ||
-            (memcmp(address, zeroes.data(), 15) == 0 &&
-             (address[15] == 0 || address[15] == 1)) ||
-            (memcmp(address, v4prefix, 12) == 0);
-    }
-
-    default:
-        return true;
-    }
-}
-
-Dht::Node*
-Dht::Bucket::randomNode()
-{
-    if (nodes.empty())
-        return nullptr;
-    std::uniform_int_distribution<unsigned> rand_node(0, nodes.size()-1);
-    unsigned nn = rand_node(rd);
-    for (auto& n : nodes)
-        if (not nn--) return &n;
-    return &nodes.back();
-}
-
-InfoHash
-Dht::RoutingTable::randomId(const Dht::RoutingTable::const_iterator& it) const
-{
-    int bit1 = it->first.lowbit();
-    int bit2 = std::next(it) != end() ? std::next(it)->first.lowbit() : -1;
-    int bit = std::max(bit1, bit2) + 1;
-
-    if (bit >= 8*HASH_LEN)
-        return it->first;
-
-    int b = bit/8;
-    InfoHash id_return;
-    std::copy_n(it->first.begin(), b, id_return.begin());
-    id_return[b] = it->first[b] & (0xFF00 >> (bit % 8));
-    id_return[b] |= rand_byte(rd) >> (bit % 8);
-    for (unsigned i = b + 1; i < HASH_LEN; i++)
-        id_return[i] = rand_byte(rd);
-    return id_return;
-}
-
-InfoHash
-Dht::RoutingTable::middle(const RoutingTable::const_iterator& it) const
-{
-    int bit1 = it->first.lowbit();
-    int bit2 = std::next(it) != end() ? std::next(it)->first.lowbit() : -1;
-    int bit = std::max(bit1, bit2) + 1;
-
-    if (bit >= 8*HASH_LEN)
-        throw std::out_of_range("End of table");
-
-    InfoHash id = it->first;
-    id[bit / 8] |= (0x80 >> (bit % 8));
-    return id;
-}
-
-Dht::RoutingTable::iterator
-Dht::RoutingTable::findBucket(const InfoHash& id)
-{
-    if (empty())
-        return end();
-    auto b = begin();
-    while (true) {
-        auto next = std::next(b);
-        if (next == end())
-            return b;
-        if (InfoHash::cmp(id, next->first) < 0)
-            return b;
-        b = next;
-    }
-}
-
-Dht::RoutingTable::const_iterator
-Dht::RoutingTable::findBucket(const InfoHash& id) const
-{
-    /* Avoid code duplication for the const version */
-    const_iterator it = const_cast<RoutingTable*>(this)->findBucket(id);
-    return it;
-}
-
-/* Every bucket contains an unordered list of nodes. */
-Dht::Node *
-Dht::findNode(const InfoHash& id, sa_family_t af)
-{
-    Bucket* b = findBucket(id, af);
-    if (!b)
-        return nullptr;
-    for (auto& n : b->nodes)
-        if (n.id == id) return &n;
-    return nullptr;
-}
-
-const Dht::Node*
-Dht::findNode(const InfoHash& id, sa_family_t af) const
-{
-    const Bucket* b = findBucket(id, af);
-    if (!b)
-        return nullptr;
-    for (const auto& n : b->nodes)
-        if (n.id == id) return &n;
-    return nullptr;
-}
-
-/* This is our definition of a known-good node. */
-bool
-Dht::Node::isGood(time_t now) const
-{
-    return
-        pinged <= 2 &&
-        reply_time >= now - 7200 &&
-        time >= now - 15 * 60;
-}
-
-/* Every bucket caches the address of a likely node.  Ping it. */
-int
-Dht::sendCachedPing(Bucket& b)
-{
-    /* We set family to 0 when there's no cached node. */
-    if (b.cached.ss_family == 0)
-        return 0;
-
-    DHT_DEBUG("Sending ping to cached node.");
-    int rc = sendPing((sockaddr*)&b.cached, b.cachedlen, TransId{TransPrefix::PING});
-    b.cached.ss_family = 0;
-    b.cachedlen = 0;
-    return rc;
-}
-
-/* Called whenever we send a request to a node, increases the ping count
-   and, if that reaches 3, sends a ping to a new candidate. */
-void
-Dht::pinged(Node& n, Bucket *b)
-{
-    n.pinged++;
-    n.pinged_time = now.tv_sec;
-    if (n.pinged >= 3)
-        sendCachedPing(b ? *b : *findBucket(n.id, n.ss.ss_family));
-}
-
-/* The internal blacklist is an LRU cache of nodes that have sent
-   incorrect messages. */
-void
-Dht::blacklistNode(const InfoHash* id, const sockaddr *sa, socklen_t salen)
-{
-    DHT_WARN("Blacklisting broken node.");
-
-    if (id) {
-        /* Make the node easy to discard. */
-        Node *n = findNode(*id, sa->sa_family);
-        if (n) {
-            n->pinged = 3;
-            pinged(*n);
-        }
-        /* Discard it from any searches in progress. */
-        for (auto& sr : searches) {
-            sr.nodes.erase(std::remove_if (sr.nodes.begin(), sr.nodes.end(), [id](const SearchNode& sn){
-                return sn.id == *id;
-            }));
-        }
-    }
-    /* And make sure we don't hear from it again. */
-    memcpy(&blacklist[next_blacklisted], sa, salen);
-    next_blacklisted = (next_blacklisted + 1) % BLACKLISTED_MAX;
-}
-
-bool
-Dht::isNodeBlacklisted(const sockaddr *sa, socklen_t salen) const
-{
-    if (salen > sizeof(sockaddr_storage))
-        return true;
-
-    if (isBlacklisted(sa, salen))
-        return true;
-
-    for (unsigned i = 0; i < BLACKLISTED_MAX; i++) {
-        if (memcmp(&blacklist[i], sa, salen) == 0)
-            return true;
-    }
-
-    return false;
-}
-
-/* Split a bucket into two equal parts. */
-bool
-Dht::RoutingTable::split(const RoutingTable::iterator& b)
-{
-    InfoHash new_id;
-    try {
-        new_id = middle(b);
-    } catch (const std::out_of_range& e) {
-        return false;
-    }
-
-    // Insert new bucket
-    insert(std::next(b), Bucket {b->af, new_id, b->time});
-
-    // Re-assign nodes
-    std::list<Node> nodes {};
-    nodes.splice(nodes.begin(), b->nodes);
-    while (!nodes.empty()) {
-        auto n = nodes.begin();
-        auto b = findBucket(n->id);
-        if (b == end())
-            nodes.erase(n);
-        else
-            b->nodes.splice(b->nodes.begin(), nodes, n);
-    }
-    return true;
-}
-
-/* We just learnt about a node, not necessarily a new one.  Confirm is 1 if
-   the node sent a message, 2 if it sent us a reply. */
-Dht::Node*
-Dht::newNode(const InfoHash& id, const sockaddr *sa, socklen_t salen, int confirm)
-{
-    if (isMartian(sa, salen) || isNodeBlacklisted(sa, salen))
-        return nullptr;
-
-    auto& list = sa->sa_family == AF_INET ? buckets : buckets6;
-    auto b = list.findBucket(id);
-    if (b == list.end() || id == myid)
-        return nullptr;
-
-    bool mybucket = list.contains(b, myid);
-
-    if (confirm == 2)
-        b->time = now.tv_sec;
-
-    for (auto& n : b->nodes) {
-        if (n.id != id) continue;
-        if (confirm || n.time < now.tv_sec - 15 * 60) {
-            /* Known node.  Update stuff. */
-            memcpy((sockaddr*)&n.ss, sa, salen);
-            if (confirm)
-                n.time = now.tv_sec;
-            if (confirm >= 2) {
-                n.reply_time = now.tv_sec;
-                n.pinged = 0;
-                n.pinged_time = 0;
-            }
-            if (confirm) {
-                /* If this node existed in searches but was expired, give it another chance. */
-                for (auto& s : searches) {
-                    if (s.af != sa->sa_family) continue;
-                    if (s.insertNode(id, sa, salen, now.tv_sec, true)) {
-                        time_t tm = s.getNextStepTime(types, now.tv_sec);
-                        if (tm != 0 && (search_time == 0 || search_time > tm))
-                            search_time = tm;
-                    }
-                }
-            }
-        }
-        return &n;
-    }
-
-    /* New node. */
-
-    /* Try adding the node to searches */
-    for (auto& s : searches) {
-        if (s.af != sa->sa_family) continue;
-        if (s.insertNode(id, sa, salen, now.tv_sec)) {
-            time_t tm = s.getNextStepTime(types, now.tv_sec);
-            if (tm != 0 && (search_time == 0 || search_time > tm))
-                search_time = tm;
-        }
-    }
-
-    if (mybucket) {
-        if (sa->sa_family == AF_INET)
-            mybucket_grow_time = now.tv_sec;
-        else
-            mybucket6_grow_time = now.tv_sec;
-    }
-
-    /* First, try to get rid of a known-bad node. */
-    for (auto& n : b->nodes) {
-        if (n.pinged < 3 || n.pinged_time >= now.tv_sec - 15)
-            continue;
-        n.id = id;
-        memcpy((sockaddr*)&n.ss, sa, salen);
-        n.time = confirm ? now.tv_sec : 0;
-        n.reply_time = confirm >= 2 ? now.tv_sec : 0;
-        n.pinged_time = 0;
-        n.pinged = 0;
-        return &n;
-    }
-
-    if (b->nodes.size() >= 8) {
-        /* Bucket full.  Ping a dubious node */
-        bool dubious = false;
-        for (auto& n : b->nodes) {
-            /* Pick the first dubious node that we haven't pinged in the
-               last 15 seconds.  This gives nodes the time to reply, but
-               tends to concentrate on the same nodes, so that we get rid
-               of bad nodes fast. */
-            if (!n.isGood(now.tv_sec)) {
-                dubious = true;
-                if (n.pinged_time < now.tv_sec - 15) {
-                    DHT_DEBUG("Sending ping to dubious node.");
-                    sendPing((sockaddr*)&n.ss, n.sslen, TransId {TransPrefix::PING});
-                    n.pinged++;
-                    n.pinged_time = now.tv_sec;
-                    break;
-                }
-            }
-        }
-
-        if (mybucket && (!dubious || list.size() == 1)) {
-            DHT_DEBUG("Splitting.");
-            sendCachedPing(*b);
-            list.split(b);
-            dumpTables();
-            return newNode(id, sa, salen, confirm);
-        }
-
-        /* No space for this node.  Cache it away for later. */
-        if (confirm || b->cached.ss_family == 0) {
-            memcpy(&b->cached, sa, salen);
-            b->cachedlen = salen;
-        }
-
-        return nullptr;
-    }
-
-    /* Create a new node. */
-    b->nodes.emplace_front(id, sa, salen, confirm ? now.tv_sec : 0, confirm >= 2 ? now.tv_sec : 0);
-    return &b->nodes.front();
-}
-
-/* Called periodically to purge known-bad nodes.  Note that we're very
-   conservative here: broken nodes in the table don't do much harm, we'll
-   recover as soon as we find better ones. */
-void
-Dht::expireBuckets(RoutingTable& list)
-{
-    for (auto& b : list) {
-        bool changed = false;
-        b.nodes.remove_if([&changed](const Node& n) {
-            if (n.pinged >= 4) {
-                changed = true;
-                return true;
-            }
-            return false;
-        });
-        if (changed)
-            sendCachedPing(b);
-    }
-    std::uniform_int_distribution<time_t> time_dis(120, 360-1);
-    expire_stuff_time = now.tv_sec + time_dis(rd);
-}
-
-/* While a search is in progress, we don't necessarily keep the nodes being
-   walked in the main bucket table.  A search in progress is identified by
-   a unique transaction id, a short (and hence small enough to fit in the
-   transaction id of the protocol packets). */
-
-Dht::Search *
-Dht::findSearch(unsigned short tid, sa_family_t af)
-{
-    auto sr = std::find_if (searches.begin(), searches.end(), [tid,af](const Search& s){
-        return s.tid == tid && s.af == af;
-    });
-    return sr == searches.end() ? nullptr : &(*sr);
-}
-
-/* A search contains a list of nodes, sorted by decreasing distance to the
-   target.  We just got a new candidate, insert it at the right spot or
-   discard it. */
-bool
-Dht::Search::insertNode(const InfoHash& nid,
-    const sockaddr *sa, socklen_t salen,
-    time_t now, bool confirmed, const Blob& token)
-{
-    if (sa->sa_family != af) {
-        //DHT_DEBUG("Attempted to insert node in the wrong family.");
-        return false;
-    }
-
-    // Fast track for the case where the node is not relevant for this search
-    if (nodes.size() == SEARCH_NODES && id.xorCmp(nid, nodes.back().id) > 0)
-        return false;
-
-    bool found = false;
-    auto n = std::find_if(nodes.begin(), nodes.end(), [=,&found](const SearchNode& sn) {
-        if (sn.id == nid) {
-            found = true;
-            return true;
-        }
-        return id.xorCmp(nid, sn.id) < 0;
-    });
-    if (!found) {
-        if (n == nodes.end() && nodes.size() == SEARCH_NODES)
-            return false;
-        n = nodes.insert(n, SearchNode{ nid });
-        if (nodes.size() > SEARCH_NODES)
-            nodes.pop_back();
-    }
-
-    memcpy(&n->ss, sa, salen);
-    n->sslen = salen;
-
-    if (confirmed) {
-        /*if (n->pinged >= 3)
-            DHT_WARN("Resurrecting node  !");*/
-        n->pinged = 0;
-    }
-    if (not token.empty()) {
-        n->reply_time = now;
-        n->request_time = 0;
-      /*  n->pinged = 0;*/
-        /*if (token.size() > 64)
-            DHT_DEBUG("Eek!  Overlong token.");
-        else*/
-        if (token.size() <= 64)
-            n->token = token;
-    }
-
-    return true;
-}
-
-void
-Dht::expireSearches()
-{
-    auto t = now.tv_sec - SEARCH_EXPIRE_TIME;
-    searches.remove_if([t](const Search& sr) {
-        return sr.announce.empty() && sr.step_time < t;
-    });
-}
-
-bool
-Dht::searchSendGetValues(Search& sr, SearchNode *n)
-{
-    time_t t = now.tv_sec;
-    if (!n) {
-        auto ni = std::find_if(sr.nodes.begin(), sr.nodes.end(), [t](const SearchNode& sn) {
-            return sn.pinged < 3 && !sn.isSynced(t) && sn.request_time < t - 15;
-        });
-        if (ni != sr.nodes.end())
-            n = &*ni;
-    }
-
-    if (!n || n->pinged >= 3 || n->isSynced(t) || n->request_time >= t - 15)
-        return false;
-
-    {
-        char hbuf[NI_MAXHOST];
-        char sbuf[NI_MAXSERV];
-        getnameinfo((sockaddr*)&n->ss, n->sslen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
-        DHT_WARN("Sending get_values to %s:%s for %s.", hbuf, sbuf, n->id.toString().c_str());
-    }
-    sendGetValues((sockaddr*)&n->ss, n->sslen, TransId {TransPrefix::GET_VALUES, sr.tid}, sr.id, -1, n->reply_time >= t - 15);
-    n->pinged++;
-    n->request_time = t;
-
-    /* If the node happens to be in our main routing table, mark it
-       as pinged. */
-    Node *node = findNode(n->id, n->ss.ss_family);
-    if (node) pinged(*node);
-    return true;
-}
-
-/* When a search is in progress, we periodically call search_step to send
-   further requests. */
-void
-Dht::searchStep(Search& sr)
-{
-    if (sr.nodes.empty()) {
-        // No nodes... yet ?
-        // Nothing to do, wait for the timeout.
-        /*
-        if (sr.step_time == 0)
-            sr.step_time = now.tv_sec;
-        if (now.tv_sec - sr.step_time > SEARCH_TIMEOUT) {
-            DHT_WARN("Search timed out.");
-            if (sr.done_callback)
-                sr.done_callback(false);
-            if (sr.announce.empty())
-                sr.done = true;
-        }
-        */
-        return;
-    }
-
-    /* Check if the first 8 live nodes have replied. */
-    if (sr.isSynced(now.tv_sec)) {
-        DHT_DEBUG("searchStep (synced).");
-        for (auto& a : sr.announce) {
-            if (!a.value) {
-                continue;
-                DHT_ERROR("Trying to announce a null value !");
-            }
-            unsigned i = 0;
-            bool all_acked = true;
-            auto vid = a.value->id;
-            const auto& type = getType(a.value->type);
-            for (auto& n : sr.nodes) {
-                if (n.pinged >= 3)
-                    continue;
-                // A proposed extension to the protocol consists in
-                // omitting the token when storage tables are full.  While
-                // I don't think this makes a lot of sense -- just sending
-                // a positive reply is just as good --, let's deal with it.
-                // if (n.token.empty())
-                //    n.acked[vid] = now.tv_sec;
-                auto a_status = n.acked.find(vid);
-                auto at = n.getAnnounceTime(a_status, type);
-                if ( at <= now.tv_sec ) {
-                    all_acked = false;
-                    //storageStore(sr.id, a.value);
-                    {
-                        char hbuf[NI_MAXHOST];
-                        char sbuf[NI_MAXSERV];
-                        getnameinfo((sockaddr*)&n.ss, n.sslen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
-                        DHT_WARN("Sending announce_value to %s:%s (%s).", hbuf, sbuf, n.id.toString().c_str());
-                    }
-                    sendAnnounceValue((sockaddr*)&n.ss, sizeof(sockaddr_storage),
-                                       TransId {TransPrefix::ANNOUNCE_VALUES, sr.tid}, sr.id, *a.value,
-                                       n.token, n.reply_time >= now.tv_sec - 15);
-                    if (a_status == n.acked.end()) {
-                        n.acked[vid] = { .request_time = now.tv_sec };
-                    } else {
-                        a_status->second.request_time = now.tv_sec;
-                    }
-                    n.pending = true;
-                }
-                if (++i == 8)
-                    break;
-            }
-            if (all_acked && a.callback) {
-                a.callback(true);
-                a.callback = nullptr;
-            }
-        }
-        for (auto& n : sr.nodes) {
-            if (n.pending) {
-                n.pending = false;
-                n.pinged++;
-                n.request_time = now.tv_sec;
-                if (auto node = findNode(n.id, n.ss.ss_family))
-                    pinged(*node);
-            }
-        }
-        DHT_DEBUG("Search done.");
-        if (sr.done_callback) {
-            sr.done_callback(true);
-            sr.done_callback = nullptr;
-        }
-        if (sr.announce.empty())
-            sr.done = true;
-    } else {
-        DHT_DEBUG("searchStep.");
-        if (sr.step_time + SEARCH_GET_STEP >= now.tv_sec)
-            return;
-        if (sr.nodes.empty() && sr.announce.empty()) {
-            sr.done = true;
-            return;
-        }
-
-        unsigned i = 0;
-        for (auto& sn : sr.nodes) {
-            i += searchSendGetValues(sr, &sn) ? 1 : 0;
-            if (i >= 3)
-                break;
-        }
-    }
-    sr.step_time = now.tv_sec;
-}
-
-
-std::list<Dht::Search>::iterator
-Dht::newSearch()
-{
-    auto oldest = searches.begin();
-    for (auto i = searches.begin(); i != searches.end(); ++i) {
-        if (i->done && (oldest->step_time > i->step_time))
-            oldest = i;
-    }
-
-    /* The oldest slot is expired. */
-    if (oldest != searches.end() && oldest->announce.empty() && oldest->step_time < now.tv_sec - SEARCH_EXPIRE_TIME)
-        return oldest;
-
-    /* Allocate a new slot. */
-    if (searches.size() < MAX_SEARCHES) {
-        searches.push_front(Search {});
-        return searches.begin();
-    }
-
-    /* Oh, well, never mind.  Reuse the oldest slot. */
-    return oldest;
-}
-
-/* Insert the contents of a bucket into a search structure. */
-void
-Dht::Search::insertBucket(const Bucket& b, time_t now)
-{
-    for (auto& n : b.nodes)
-        insertNode(n.id, (sockaddr*)&n.ss, n.sslen, now);
-}
-
-bool
-Dht::Search::isSynced(time_t now) const
-{
-    unsigned i = 0;
-    for (const auto& n : nodes) {
-        if (n.pinged >= 3)
-            continue;
-        if (!n.isSynced(now))
-            return false;
-        if (++i == 8)
-            break;
-    }
-    return i > 0;
-}
-
-time_t
-Dht::Search::getAnnounceTime(const std::map<ValueType::Id, ValueType>& types) const
-{
-    if (nodes.empty())
-        return 0;
-    time_t ret = 0;
-    for (const auto& a : announce) {
-        if (!a.value) continue;
-        auto type_it = types.find(a.value->type);
-        const ValueType& type = (type_it == types.end()) ? ValueType::USER_DATA : type_it->second;
-        unsigned i = 0;
-        for (const auto& n : nodes) {
-            if (n.pinged >= 3)
-                continue;
-            auto at = n.getAnnounceTime(a.value->id, type);
-            if (at != 0 && (ret == 0 || ret > at))
-                ret = at;
-            if (++i == 8)
-                break;
-        }
-    }
-    return ret;
-}
-
-time_t
-Dht::Search::getNextStepTime(const std::map<ValueType::Id, ValueType>& types, time_t now) const
-{
-    if (done || nodes.empty())
-        return 0;
-    if (!isSynced(now))
-        return step_time + SEARCH_GET_STEP + 1;
-    return getAnnounceTime(types);
-}
-
-void
-Dht::bootstrapSearch(Dht::Search& sr)
-{
-    auto& list = (sr.af == AF_INET) ? buckets : buckets6;
-    if (list.empty() || (list.size() == 1 && list.front().nodes.empty()))
-        return;
-    DHT_DEBUG("bootstrapSearch.");
-    auto b = list.findBucket(sr.id);
-    if (b == list.end())
-        return;
-
-    time_t t = now.tv_sec;
-    sr.insertBucket(*b, t);
-
-    if (sr.nodes.size() < SEARCH_NODES) {
-        if (std::next(b) != list.end())
-            sr.insertBucket(*std::next(b), t);
-        if (b != list.begin())
-            sr.insertBucket(*std::prev(b), t);
-    }
-    if (sr.nodes.size() < SEARCH_NODES)
-        sr.insertBucket(*list.findBucket(myid), t);
-}
-
-/* Start a search. */
-Dht::Search*
-Dht::search(const InfoHash& id, sa_family_t af, GetCallback callback, DoneCallback done_callback, Value::Filter filter)
-{
-    if (!isRunning(af)) {
-        DHT_ERROR("Unsupported protocol IPv%s bucket for %s", (af == AF_INET) ? "4" : "6", id.toString().c_str());
-        if (done_callback)
-            done_callback(false);
-        return nullptr;
-    }
-
-    auto sr = std::find_if (searches.begin(), searches.end(), [id,af](const Search& s) {
-        return s.id == id && s.af == af;
-    });
-
-    time_t t = now.tv_sec;
-    if (sr != searches.end()) {
-        sr->done = false;
-        // Discard any doubtful nodes.
-        sr->nodes.erase(std::remove_if (sr->nodes.begin(), sr->nodes.end(), [t](const SearchNode& n) {
-            return n.pinged >= 3 || n.reply_time < t - 7200;
-        }), sr->nodes.end());
-    } else {
-        sr = newSearch();
-        if (sr == searches.end()) {
-            errno = ENOSPC;
-            return nullptr;
-        }
-        sr->af = af;
-        sr->tid = search_id++;
-        sr->step_time = 0;
-        sr->id = id;
-        sr->done = false;
-        sr->nodes = {};
-        DHT_DEBUG("New IPv%s search for %s", (af == AF_INET) ? "4" : "6", id.toString().c_str());
-    }
-
-    if (callback)
-        sr->callbacks.emplace_back(filter, callback);
-    sr->done_callback = done_callback;
-
-    bootstrapSearch(*sr);
-    searchStep(*sr);
-    search_time = t;
-    return &(*sr);
-}
-
-void
-Dht::announce(const InfoHash& id, sa_family_t af, const std::shared_ptr<Value>& value, DoneCallback callback)
-{
-    if (!value) {
-        if (callback)
-            callback(false);
-        return;
-    }
-    auto sri = std::find_if (searches.begin(), searches.end(), [id,af](const Search& s) {
-        return s.id == id && s.af == af;
-    });
-    Search* sr = (sri == searches.end()) ? search(id, af, nullptr, nullptr) : &(*sri);
-    if (!sr) {
-        if (callback)
-            callback(false);
-        return;
-    }
-    sr->done = false;
-    auto a_sr = std::find_if(sr->announce.begin(), sr->announce.end(), [&](const Announce& a){
-        return a.value->id == value->id;
-    });
-    if (a_sr == sr->announce.end())
-        sr->announce.emplace_back(Announce {value, callback});
-    else {
-        if (a_sr->value != value) {
-            a_sr->value = value;
-            for (auto& n : sr->nodes)
-                n.acked[value->id] = {0, 0};
-        }
-        a_sr->callback = callback;
-    }
-}
-
-void
-Dht::put(const InfoHash& id, Value&& value, DoneCallback callback)
-{
-    if (value.id == Value::INVALID_ID) {
-        std::random_device rdev;
-        std::uniform_int_distribution<Value::Id> rand_id {};
-        value.id = rand_id(rdev);
-    }
-
-    auto val = std::make_shared<Value>(std::move(value));
-    DHT_DEBUG("put: adding %s -> %s", id.toString().c_str(), val->toString().c_str());
-
-    auto ok = std::make_shared<bool>(false);
-    auto done = std::make_shared<bool>(false);
-    auto done4 = std::make_shared<bool>(false);
-    auto done6 = std::make_shared<bool>(false);
-    auto donecb = [=]() {
-        // Callback as soon as the value is announced on one of the available networks
-        if (callback && !*done && (*ok || (*done4 && *done6))) {
-            callback(*ok);
-            *done = true;
-        }
-    };
-    announce(id, AF_INET, val, [=](bool ok4) {
-        DHT_DEBUG("search done IPv4 %d", ok4);
-        *done4 = true;
-        *ok |= ok4;
-        donecb();
-    });
-    announce(id, AF_INET6, val, [=](bool ok6) {
-        DHT_DEBUG("search done IPv6 %d", ok6);
-        *done6 = true;
-        *ok |= ok6;
-        donecb();
-    });
-}
-
-void
-Dht::get(const InfoHash& id, GetCallback getcb, DoneCallback donecb, Value::Filter filter)
-{
-    /* Try to answer this search locally. */
-    if (getcb) {
-        auto locVals = getLocal(id, filter);
-        if (not locVals.empty()) {
-            DHT_DEBUG("Found local data (%d values).", locVals.size());
-            getcb(locVals);
-        }
-    }
-
-    auto done = std::make_shared<bool>(false);
-    auto done4 = std::make_shared<bool>(false);
-    auto done6 = std::make_shared<bool>(false);
-    auto vals = std::make_shared<std::vector<std::shared_ptr<Value>>>();
-    auto done_l = [=]() {
-        if ((*done4 && *done6) || *done) {
-            *done = true;
-            donecb(true);
-        }
-    };
-    auto cb = [=](const std::vector<std::shared_ptr<Value>>& values) {
-        if (*done)
-            return false;
-        std::vector<std::shared_ptr<Value>> newvals {};
-        for (const auto& v : values) {
-            auto it = std::find_if(vals->cbegin(), vals->cend(), [&](const std::shared_ptr<Value>& sv) {
-                return sv == v || *sv == *v;
-            });
-            if (it == vals->cend()) {
-                if (filter(*v))
-                    newvals.push_back(v);
-            }
-        }
-        if (!newvals.empty()) {
-            *done = !getcb(newvals);
-            vals->insert(vals->end(), newvals.begin(), newvals.end());
-        }
-        done_l();
-        return !*done;
-    };
-    Dht::search(id, AF_INET, cb, [=](bool) {
-        *done4 = true;
-        done_l();
-    });
-    Dht::search(id, AF_INET6, cb, [=](bool) {
-        *done6 = true;
-        done_l();
-    });
-
-}
-
-std::vector<std::shared_ptr<Value>>
-Dht::getLocal(const InfoHash& id, Value::Filter f) const
-{
-    auto s = findStorage(id);
-    if (!s) return {};
-    std::vector<std::shared_ptr<Value>> vals;
-    vals.reserve(s->values.size());
-    for (auto& v : s->values)
-        if (f(*v.data)) vals.push_back(v.data);
-    return vals;
-}
-
-std::shared_ptr<Value>
-Dht::getLocal(const InfoHash& id, const Value::Id& vid) const
-{
-    if (auto s = findStorage(id)) {
-        for (auto& v : s->values)
-            if (v.data->id == vid) return v.data;
-    }
-    return {};
-}
-
-std::vector<std::shared_ptr<Value>>
-Dht::getPut(const InfoHash& id)
-{
-    std::vector<std::shared_ptr<Value>> ret;
-    for (const auto& search: searches) {
-        if (search.id != id)
-            continue;
-        ret.reserve(ret.size() + search.announce.size());
-        for (const auto& a : search.announce)
-            ret.push_back(a.value);
-    }
-    return ret;
-}
-
-std::shared_ptr<Value>
-Dht::getPut(const InfoHash& id, const Value::Id& vid)
-{
-    for (const auto& search : searches) {
-        if (search.id != id)
-            continue;
-        for (const auto& a : search.announce) {
-            if (a.value->id == vid)
-                return a.value;
-        }
-    }
-    return nullptr;
-}
-
-bool
-Dht::cancelPut(const InfoHash& id, const Value::Id& vid)
-{
-    bool canceled {false};
-    for (auto& search: searches) {
-        if (search.id != id)
-            continue;
-        for (auto it = search.announce.begin(); it != search.announce.end();) {
-            if (it->value->id == vid) {
-                canceled = true;
-                it = search.announce.erase(it);
-            }
-            else
-                ++it;
-        }
-    }
-}
-
-/* A struct storage stores all the stored peer addresses for a given info
-   hash. */
-
-Dht::Storage*
-Dht::findStorage(const InfoHash& id)
-{
-    for (auto& st : store)
-        if (st.id == id)
-            return &st;
-    return nullptr;
-}
-
-Dht::ValueStorage*
-Dht::storageStore(const InfoHash& id, const std::shared_ptr<Value>& value)
-{
-    Storage *st = findStorage(id);
-    if (!st) {
-        if (store.size() >= MAX_HASHES)
-            return nullptr;
-        store.push_back(Storage {id, {}});
-        st = &store.back();
-    }
-
-    auto it = std::find_if (st->values.begin(), st->values.end(), [&](const ValueStorage& vr) {
-        return vr.data == value || vr.data->id == value->id;
-    });
-    if (it != st->values.end()) {
-        /* Already there, only need to refresh */
-        it->time = now.tv_sec;
-        if (it->data != value) {
-            DHT_DEBUG("Updating %s -> %s", id.toString().c_str(), value->toString().c_str());
-            it->data = value;
-        }
-        return &*it;
-    } else {
-        DHT_DEBUG("Storing %s -> %s", id.toString().c_str(), value->toString().c_str());
-        if (st->values.size() >= MAX_VALUES)
-            return nullptr;
-        st->values.emplace_back(value, now.tv_sec);
-        return &st->values.back();
-    }
-}
-
-void
-Dht::expireStorage()
-{
-    auto i = store.begin();
-    while (i != store.end())
-    {
-        i->values.erase(
-            std::partition(i->values.begin(), i->values.end(),
-                [&](const ValueStorage& v)
-                {
-                    if (!v.data) return true; // should not happen
-                    const auto& type = getType(v.data->type);
-                    bool expired = v.time + type.expiration < now.tv_sec;
-                    if (expired)
-                        DHT_DEBUG("Discarding expired value %s", v.data->toString().c_str());
-                    return !expired;
-                }),
-            i->values.end());
-
-        if (i->values.size() == 0) {
-            DHT_DEBUG("Discarding expired value %s", i->id.toString().c_str());
-            i = store.erase(i);
-        }
-        else
-            ++i;
-    }
-}
-
-void
-Dht::rotateSecrets()
-{
-    std::uniform_int_distribution<time_t> time_dist(15*60, 45*60);
-    rotate_secrets_time = now.tv_sec + time_dist(rd);
-
-    oldsecret = secret;
-    {
-        std::random_device rdev;
-        std::generate_n(secret.begin(), secret.size(), std::bind(rand_byte, std::ref(rdev)));
-    }
-}
-
-Blob
-Dht::makeToken(const sockaddr *sa, bool old) const
-{
-    void *ip;
-    size_t iplen;
-    in_port_t port;
-
-    if (sa->sa_family == AF_INET) {
-        sockaddr_in *sin = (sockaddr_in*)sa;
-        ip = &sin->sin_addr;
-        iplen = 4;
-        port = htons(sin->sin_port);
-    } else if (sa->sa_family == AF_INET6) {
-        sockaddr_in6 *sin6 = (sockaddr_in6*)sa;
-        ip = &sin6->sin6_addr;
-        iplen = 16;
-        port = htons(sin6->sin6_port);
-    } else {
-        return {};
-    }
-
-    const auto& c1 = old ? oldsecret : secret;
-    Blob data;
-    data.reserve(sizeof(secret)+2+iplen);
-    data.insert(data.end(), c1.begin(), c1.end());
-    data.insert(data.end(), (uint8_t*)ip, (uint8_t*)ip+iplen);
-    data.insert(data.end(), (uint8_t*)&port, ((uint8_t*)&port)+2);
-
-    size_t sz = TOKEN_SIZE;
-    Blob ret {};
-    ret.resize(sz);
-    gnutls_datum_t gnudata = {data.data(), (unsigned int)data.size()};
-    if (gnutls_fingerprint(GNUTLS_DIG_SHA512, &gnudata, ret.data(), &sz) != GNUTLS_E_SUCCESS)
-        throw DhtException("Can't compute SHA512");
-    ret.resize(sz);
-    return ret;
-}
-
-bool
-Dht::tokenMatch(const Blob& token, const sockaddr *sa) const
-{
-    if (!sa || token.size() != TOKEN_SIZE)
-        return false;
-    if (token == makeToken(sa, false))
-        return true;
-    if (token == makeToken(sa, true))
-        return true;
-    return false;
-}
-
-int
-Dht::getNodesStats(sa_family_t af, unsigned *good_return, unsigned *dubious_return, unsigned *cached_return, unsigned *incoming_return) const
-{
-    unsigned good = 0, dubious = 0, cached = 0, incoming = 0;
-    auto& list = (af == AF_INET) ? buckets : buckets6;
-
-    for (const auto& b : list) {
-        for (auto& n : b.nodes) {
-            if (n.isGood(now.tv_sec)) {
-                good++;
-                if (n.time > n.reply_time)
-                    incoming++;
-            } else {
-                dubious++;
-            }
-        }
-        if (b.cached.ss_family > 0)
-            cached++;
-    }
-    if (good_return)
-        *good_return = good;
-    if (dubious_return)
-        *dubious_return = dubious;
-    if (cached_return)
-        *cached_return = cached;
-    if (incoming_return)
-        *incoming_return = incoming;
-    return good + dubious;
-}
-
-void
-Dht::dumpBucket(const Bucket& b, std::ostream& out) const
-{
-    out << b.first << " count " << b.nodes.size() << " age " << (int)(now.tv_sec - b.time);
-    if (b.cached.ss_family)
-        out << " (cached)";
-    out  << std::endl;
-    for (auto& n : b.nodes) {
-        std::string buf(INET6_ADDRSTRLEN, '\0');
-        unsigned short port;
-        out << "    Node " << n.id << " ";
-        if (n.ss.ss_family == AF_INET) {
-            sockaddr_in *sin = (sockaddr_in*)&n.ss;
-            inet_ntop(AF_INET, &sin->sin_addr, (char*)buf.data(), buf.size());
-            port = ntohs(sin->sin_port);
-        } else if (n.ss.ss_family == AF_INET6) {
-            sockaddr_in6 *sin6 = (sockaddr_in6*)&n.ss;
-            inet_ntop(AF_INET6, &sin6->sin6_addr, (char*)buf.data(), buf.size());
-            port = ntohs(sin6->sin6_port);
-        } else {
-            out << "unknown(" << (unsigned)n.ss.ss_family << ")";
-            port = 0;
-        }
-        buf.resize(std::char_traits<char>::length(buf.c_str()));
-
-        if (n.ss.ss_family == AF_INET6)
-            out << "[" << buf << "]:" << port;
-        else
-            out << buf << ":" << port;
-        if (n.time != n.reply_time)
-            out << " age " << (now.tv_sec - n.time) << ", " << (now.tv_sec - n.reply_time);
-        else
-            out << " age " << (now.tv_sec - n.time);
-        if (n.pinged)
-            out << " (" << n.pinged << ")";
-        if (n.isGood(now.tv_sec))
-            out << " (good)";
-        out << std::endl;
-    }
-}
-
-void
-Dht::dumpSearch(const Search& sr, std::ostream& out) const
-{
-    out << std::endl << "Search (IPv" << (sr.af == AF_INET6 ? "6" : "4") << ") " << sr.id;
-    out << " age " << (now.tv_sec - sr.step_time) << " s";
-    if (sr.done)
-        out << " [done]";
-    bool synced = sr.isSynced(now.tv_sec);
-    out << (synced ? " [synced]" : " [not synced]");
-    if (synced && not sr.announce.empty()) {
-        auto at = sr.getAnnounceTime(types);
-        if (at && at > now.tv_sec)
-            out << " [all announced]";
-        else
-            out << " announce at " << at << ", in " << (at-now.tv_sec) << " s.";
-    }
-    out << std::endl;
-
-    for (const auto& n : sr.announce) {
-        out << "   Announcement: " << *n.value << std::endl;
-    }
-
-    unsigned i = 0;
-    for (const auto& n : sr.nodes) {
-        out << "   Node " << i++ << " id " << n.id << " bits " << InfoHash::commonBits(sr.id, n.id);
-        if (n.request_time)
-            out << " req: " << (now.tv_sec - n.request_time) << " s,";
-        out << " age:" << (now.tv_sec - n.reply_time) << " s";
-        if (n.pinged)
-            out << " pinged: " << n.pinged;
-        if (findNode(n.id, AF_INET))
-            out << " [known]";
-        if (n.reply_time)
-            out << " [replied]";
-        out << (n.isSynced(now.tv_sec) ? " [synced]" : " [not synced]");
-        out << std::endl;
-    }
-}
-
-void
-Dht::dumpTables() const
-{
-    std::stringstream out;
-    out << "My id " << myid << std::endl;
-
-    out << "Buckets IPv4 :" << std::endl;
-    for (const auto& b : buckets)
-        dumpBucket(b, out);
-    out << "Buckets IPv6 :" << std::endl;
-    for (const auto& b : buckets6)
-        dumpBucket(b, out);
-
-    for (const auto& sr : searches)
-        dumpSearch(sr, out);
-    out << std::endl;
-
-    for (const auto& st : store) {
-        out << "Storage " << st.id << " " << st.values.size() << " values:" << std::endl;
-        for (const auto& v : st.values)
-            out << "   " << *v.data << " (" << (now.tv_sec - v.time) << "s)" << std::endl;
-    }
-
-    DHT_DEBUG("%s", out.str().c_str());
-}
-
-
-Dht::Dht(int s, int s6, const InfoHash& id)
- : dht_socket(s), dht_socket6(s6), myid(id)
-{
-    if (s < 0 && s6 < 0)
-        return;
-
-    if (s >= 0) {
-        buckets = {Bucket {AF_INET}};
-        if (!set_nonblocking(s, 1))
-            throw DhtException("Can't set socket to non-blocking mode");
-    }
-
-    if (s6 >= 0) {
-        buckets6 = {Bucket {AF_INET6}};
-        if (!set_nonblocking(s6, 1))
-            throw DhtException("Can't set socket to non-blocking mode");
-    }
-
-    std::uniform_int_distribution<decltype(search_id)> searchid_dis {};
-    search_id = searchid_dis(rd);
-
-    gettimeofday(&now, nullptr);
-
-    std::uniform_int_distribution<time_t> time_dis {0,3};
-    mybucket_grow_time = now.tv_sec;
-    mybucket6_grow_time = now.tv_sec;
-    confirm_nodes_time = now.tv_sec + time_dis(rd);
-    rate_limit_time = now.tv_sec;
-
-    // Fill old secret
-    {
-        std::random_device rdev;
-        std::generate_n(secret.begin(), secret.size(), std::bind(rand_byte, std::ref(rdev)));
-    }
-    rotateSecrets();
-
-    expireBuckets(buckets);
-    expireBuckets(buckets6);
-
-    DHT_DEBUG("DHT initialised with node ID %s", myid.toString().c_str());
-}
-
-
-Dht::~Dht()
-{}
-
-/* Rate control for requests we receive. */
-
-bool
-Dht::rateLimit()
-{
-    if (rate_limit_tokens == 0) {
-        rate_limit_tokens = std::min(MAX_REQUESTS_PER_SEC, 100 * static_cast<long unsigned>(now.tv_sec - rate_limit_time));
-        rate_limit_time = now.tv_sec;
-    }
-
-    if (rate_limit_tokens == 0)
-        return false;
-
-    rate_limit_tokens--;
-    return true;
-}
-
-bool
-Dht::neighbourhoodMaintenance(RoutingTable& list)
-{
-    DHT_DEBUG("neighbourhoodMaintenance");
-
-    auto b = list.findBucket(myid);
-    if (b == list.end())
-        return false;
-
-    InfoHash id = myid;
-    id[HASH_LEN-1] = rand_byte(rd);
-
-    std::binomial_distribution<bool> rand_trial(1, 1./8.);
-    auto q = b;
-    if (std::next(q) != list.end() && (q->nodes.empty() || rand_trial(rd)))
-        q = std::next(q);
-    if (b != list.begin() && (q->nodes.empty() || rand_trial(rd))) {
-        auto r = std::prev(b);
-        if (!r->nodes.empty())
-            q = r;
-    }
-
-    /* Since our node-id is the same in both DHTs, it's probably
-       profitable to query both families. */
-    int want = dht_socket >= 0 && dht_socket6 >= 0 ? (WANT4 | WANT6) : -1;
-    Node *n = q->randomNode();
-    if (n) {
-        DHT_DEBUG("Sending find_node for%s neighborhood maintenance.", q->af == AF_INET6 ? " IPv6" : "");
-        sendFindNode((sockaddr*)&n->ss, n->sslen,
-                       TransId {TransPrefix::FIND_NODE}, id, want,
-                       n->reply_time >= now.tv_sec - 15);
-        pinged(*n, &(*q));
-    }
-
-    return true;
-}
-
-bool
-Dht::bucketMaintenance(RoutingTable& list)
-{
-    std::binomial_distribution<bool> rand_trial(1, 1./8.);
-    std::binomial_distribution<bool> rand_trial_38(1, 1./38.);
-
-    for (auto b = list.begin(); b != list.end(); ++b) {
-        if (b->time < now.tv_sec - 600 || b->nodes.empty()) {
-            /* This bucket hasn't seen any positive confirmation for a long
-               time.  Pick a random id in this bucket's range, and send
-               a request to a random node. */
-            InfoHash id = list.randomId(b);
-            auto q = b;
-            /* If the bucket is empty, we try to fill it from a neighbour.
-               We also sometimes do it gratuitiously to recover from
-               buckets full of broken nodes. */
-            if (std::next(b) != list.end() && (q->nodes.empty() || rand_trial(rd)))
-                q = std::next(b);
-            if (b != list.begin() && (q->nodes.empty() || rand_trial(rd))) {
-                auto r = std::prev(b);
-                if (!r->nodes.empty())
-                    q = r;
-            }
-
-            Node *n = q->randomNode();
-            if (n) {
-                int want = -1;
-
-                if (dht_socket >= 0 && dht_socket6 >= 0) {
-                    auto otherbucket = findBucket(id, q->af == AF_INET ? AF_INET6 : AF_INET);
-                    if (otherbucket && otherbucket->nodes.size() < 8)
-                        /* The corresponding bucket in the other family
-                           is emptyish -- querying both is useful. */
-                        want = WANT4 | WANT6;
-                    else if (rand_trial_38(rd))
-                        /* Most of the time, this just adds overhead.
-                           However, it might help stitch back one of
-                           the DHTs after a network collapse, so query
-                           both, but only very occasionally. */
-                        want = WANT4 | WANT6;
-                }
-
-                DHT_DEBUG("Sending find_node for%s bucket maintenance.", q->af == AF_INET6 ? " IPv6" : "");
-                sendFindNode((sockaddr*)&n->ss, n->sslen,
-                               TransId {TransPrefix::FIND_NODE}, id, want,
-                               n->reply_time >= now.tv_sec - 15);
-                pinged(*n, &(*q));
-                /* In order to avoid sending queries back-to-back,
-                   give up for now and reschedule us soon. */
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-void
-Dht::processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, socklen_t fromlen)
-{
-    if (buflen == 0)
-        return;
-
-    //DHT_DEBUG("processMessage %p %lu %p %lu", buf, buflen, from, fromlen);
-
-    MessageType message;
-    InfoHash id, info_hash, target;
-    TransId tid;
-    Blob token {};
-    uint8_t nodes[256], nodes6[1024];
-    unsigned nodes_len = 256, nodes6_len = 1024;
-    in_port_t port;
-    Value::Id value_id;
-    uint16_t error_code;
-
-    std::vector<std::shared_ptr<Value>> values;
-
-    int want;
-    uint16_t ttid;
-
-    if (isMartian(from, fromlen))
-        return;
-
-    if (isNodeBlacklisted(from, fromlen)) {
-        DHT_DEBUG("Received packet from blacklisted node.");
-        return;
-    }
-
-    if (buf[buflen] != '\0')
-        throw DhtException("Unterminated message.");
-
-    try {
-        message = parseMessage(buf, buflen, tid, id, info_hash, target,
-                                port, token, value_id,
-                                nodes, &nodes_len, nodes6, &nodes6_len,
-                                values, &want, error_code);
-        if (message != MessageType::Error && id == zeroes)
-            throw DhtException("no or invalid InfoHash");
-    } catch (const std::exception& e) {
-        DHT_DEBUG("Can't process message of size %lu: %s.", buflen, e.what());
-        DHT_DEBUG.logPrintable(buf, buflen);
-        return;
-    }
-
-    if (id == myid) {
-        DHT_DEBUG("Received message from self.");
-        return;
-    }
-
-    if (message > MessageType::Reply) {
-        /* Rate limit requests. */
-        if (!rateLimit()) {
-            DHT_DEBUG("Dropping request due to rate limiting.");
-            return;
-        }
-    }
-
-    switch (message) {
-    case MessageType::Error:
-        if (tid.length != 4) return;
-        DHT_WARN("Received error message:");
-        DHT_WARN.logPrintable(buf, buflen);
-        if (error_code == 401 && id != zeroes && tid.matches(TransPrefix::ANNOUNCE_VALUES, &ttid)) {
-            auto sr = findSearch(ttid, from->sa_family);
-            if (!sr) return;
-            DHT_WARN("Received wrong token error for known search %s", sr->id.toString().c_str());
-            for (auto& n : sr->nodes) {
-                if (n.id != id) continue;
-                newNode(id, from, fromlen, 2);
-                n.request_time = 0;
-                n.reply_time = 0;
-                n.pinged = 0;
-            }
-            searchSendGetValues(*sr);
-        }
-        break;
-    case MessageType::Reply:
-        if (tid.length != 4) {
-            DHT_ERROR("Broken node truncates transaction ids (len: %d): ", tid.length);
-            DHT_ERROR.logPrintable(buf, buflen);
-            /* This is really annoying, as it means that we will
-               time-out all our searches that go through this node.
-               Kill it. */
-            blacklistNode(&id, from, fromlen);
-            return;
-        }
-        if (tid.matches(TransPrefix::PING)) {
-            DHT_DEBUG("Pong!");
-            newNode(id, from, fromlen, 2);
-        } else if (tid.matches(TransPrefix::FIND_NODE) or tid.matches(TransPrefix::GET_VALUES)) {
-            bool gp = false;
-            Search *sr = nullptr;
-            if (tid.matches(TransPrefix::GET_VALUES, &ttid)) {
-                gp = true;
-                sr = findSearch(ttid, from->sa_family);
-            }
-            DHT_DEBUG("Nodes found (%u+%u)%s!", nodes_len/26, nodes6_len/38, gp ? " for get_values" : "");
-            if (nodes_len % 26 != 0 || nodes6_len % 38 != 0) {
-                DHT_WARN("Unexpected length for node info!");
-                blacklistNode(&id, from, fromlen);
-            } else if (gp && sr == NULL) {
-                DHT_WARN("Unknown search!");
-                newNode(id, from, fromlen, 1);
-            } else {
-                newNode(id, from, fromlen, 2);
-                for (unsigned i = 0; i < nodes_len / 26; i++) {
-                    uint8_t *ni = nodes + i * 26;
-                    const InfoHash& ni_id = *reinterpret_cast<InfoHash*>(ni);
-                    if (ni_id == myid)
-                        continue;
-                    sockaddr_in sin { .sin_family = AF_INET };
-                    memcpy(&sin.sin_addr, ni + ni_id.size(), 4);
-                    memcpy(&sin.sin_port, ni + ni_id.size() + 4, 2);
-                    newNode(ni_id, (sockaddr*)&sin, sizeof(sin), 0);
-                    if (sr && sr->af == AF_INET) {
-                        sr->insertNode(ni_id, (sockaddr*)&sin, sizeof(sin), now.tv_sec);
-                    }
-                }
-                for (unsigned i = 0; i < nodes6_len / 38; i++) {
-                    uint8_t *ni = nodes6 + i * 38;
-                    InfoHash* ni_id = reinterpret_cast<InfoHash*>(ni);
-                    if (*ni_id == myid)
-                        continue;
-                    sockaddr_in6 sin6 {.sin6_family = AF_INET6};
-                    memcpy(&sin6.sin6_addr, ni + HASH_LEN, 16);
-                    memcpy(&sin6.sin6_port, ni + HASH_LEN + 16, 2);
-                    newNode(*ni_id, (sockaddr*)&sin6, sizeof(sin6), 0);
-                    if (sr && sr->af == AF_INET6) {
-                        sr->insertNode(*ni_id, (sockaddr*)&sin6, sizeof(sin6), now.tv_sec);
-                    }
-                }
-                if (sr) {
-                    /* Since we received a reply, the number of
-                       requests in flight has decreased.  Let's push
-                       another request. */
-                    /*if (sr->isSynced(now.tv_sec)) {
-                        DHT_DEBUG("Trying to accelerate search!");
-                        search_time = now.tv_sec;
-                        //sr->step_time = 0;
-                    } else {*/
-                        searchSendGetValues(*sr);
-                    //}
-                }
-            }
-            if (sr) {
-                sr->insertNode(id, from, fromlen, now.tv_sec, true, token);
-                if (!values.empty()) {
-                    DHT_DEBUG("Got %d values !", values.size());
-                    for (auto& cb : sr->callbacks) {
-                        if (!cb.second) continue;
-                        std::vector<std::shared_ptr<Value>> tmp;
-                        std::copy_if(values.begin(), values.end(), std::back_inserter(tmp), [&](const std::shared_ptr<Value>& v){
-                            return cb.first(*v);
-                        });
-                        if (cb.second and not tmp.empty())
-                            cb.second(tmp);
-                    }
-                }
-                if (sr->isSynced(now.tv_sec)) {
-                    search_time = now.tv_sec;
-                }
-            }
-        } else if (tid.matches(TransPrefix::ANNOUNCE_VALUES, &ttid)) {
-            DHT_DEBUG("Got reply to announce_values.");
-            Search *sr = findSearch(ttid, from->sa_family);
-            if (!sr || value_id == Value::INVALID_ID) {
-                DHT_DEBUG("Unknown search or announce!");
-                newNode(id, from, fromlen, 1);
-            } else {
-                newNode(id, from, fromlen, 2);
-                for (auto& sn : sr->nodes)
-                    if (sn.id == id) {
-                        auto it = sn.acked.insert({value_id, {}});
-                        it.first->second.request_time = 0;
-                        it.first->second.reply_time = now.tv_sec;
-                        sn.request_time = 0;
-                        //sn.reply_time = now.tv_sec;
-                        //sn.acked[value_id] = now.tv_sec;
-                        sn.pinged = 0;
-
-                        break;
-                    }
-                /* See comment for gp above. */
-                searchSendGetValues(*sr);
-            }
-        } else {
-            DHT_WARN("Unexpected reply: ");
-            DHT_WARN.logPrintable(buf, buflen);
-        }
-        break;
-    case MessageType::Ping:
-        DHT_DEBUG("Got ping (%d)!", tid.length);
-        newNode(id, from, fromlen, 1);
-        DHT_DEBUG("Sending pong.");
-        sendPong(from, fromlen, tid);
-        break;
-    case MessageType::FindNode:
-        DHT_DEBUG("Got \"find node\" request");
-        newNode(id, from, fromlen, 1);
-        DHT_DEBUG("Sending closest nodes (%d).", want);
-        sendClosestNodes(from, fromlen, tid, target, want);
-        break;
-    case MessageType::GetValues:
-        DHT_DEBUG("Got \"get values\" request");
-        newNode(id, from, fromlen, 1);
-        if (info_hash == zeroes) {
-            DHT_DEBUG("Eek!  Got get_values with no info_hash.");
-            sendError(from, fromlen, tid, 203, "Get_values with no info_hash");
-            break;
-        } else {
-            Storage* st = findStorage(info_hash);
-            Blob ntoken = makeToken(from, false);
-            if (st && st->values.size() > 0) {
-                 DHT_DEBUG("Sending found%s values.", from->sa_family == AF_INET6 ? " IPv6" : "");
-                 sendClosestNodes(from, fromlen, tid, info_hash, want, ntoken, st);
-            } else {
-                DHT_DEBUG("Sending nodes for get_values.");
-                sendClosestNodes(from, fromlen, tid, info_hash, want, ntoken);
-            }
-        }
-        break;
-    case MessageType::AnnounceValue:
-        DHT_DEBUG("Got \"announce value\" request!");
-        newNode(id, from, fromlen, 1);
-        if (info_hash == zeroes) {
-            DHT_DEBUG("Announce_value with no info_hash.");
-            sendError(from, fromlen, tid, 203, "Announce_value with no info_hash");
-            break;
-        }
-        if (!tokenMatch(token, from)) {
-            DHT_DEBUG("Incorrect token %s for announce_values.", to_hex(token.data(), token.size()).c_str());
-            sendError(from, fromlen, tid, 401, "Announce_value with wrong token");
-            break;
-        }
-        for (const auto& v : values) {
-            if (v->id == Value::INVALID_ID) {
-                DHT_DEBUG("Incorrect value id ");
-                sendError(from, fromlen, tid, 203, "Announce_value with invalid id");
-                continue;
-            }
-            auto lv = getLocal(info_hash, v->id);
-            std::shared_ptr<Value> vc = v;
-            if (lv) {
-                const auto& type = getType(lv->type);
-                if (type.editPolicy(info_hash, lv, vc, id, from, fromlen)) {
-                    DHT_DEBUG("Editing value of type %s belonging to %s at %s.", type.name.c_str(), v->owner.getId().toString().c_str(), info_hash.toString().c_str());
-                    storageStore(info_hash, vc);
-                } else {
-                    DHT_WARN("Rejecting edition of type %s belonging to %s at %s because of storage policy.", type.name.c_str(), v->owner.getId().toString().c_str(), info_hash.toString().c_str());
-                }
-            } else {
-                // Allow the value to be edited by the storage policy
-                const auto& type = getType(vc->type);
-                if (type.storePolicy(info_hash, vc, id, from, fromlen)) {
-                    DHT_DEBUG("Storing value of type %s belonging to %s at %s.", type.name.c_str(), v->owner.getId().toString().c_str(), info_hash.toString().c_str());
-                    storageStore(info_hash, vc);
-                } else {
-                    DHT_WARN("Rejecting storage of type %s belonging to %s at %s because of storage policy.", type.name.c_str(), v->owner.getId().toString().c_str(), info_hash.toString().c_str());
-                }
-            }
-
-            /* Note that if storage_store failed, we lie to the requestor.
-               This is to prevent them from backtracking, and hence
-               polluting the DHT. */
-            DHT_DEBUG("Sending announceValue confirmation.");
-            sendValueAnnounced(from, fromlen, tid, v->id);
-        }
-    }
-}
-
-void
-Dht::periodic(const uint8_t *buf, size_t buflen,
-             const sockaddr *from, socklen_t fromlen,
-             time_t *tosleep)
-{
-    gettimeofday(&now, nullptr);
-
-    processMessage(buf, buflen, from, fromlen);
-
-    if (now.tv_sec >= rotate_secrets_time)
-        rotateSecrets();
-
-    if (now.tv_sec >= expire_stuff_time) {
-        expireBuckets(buckets);
-        expireBuckets(buckets6);
-        expireStorage();
-        expireSearches();
-    }
-
-    if (search_time > 0 && now.tv_sec >= search_time) {
-        DHT_DEBUG("search_time");
-        search_time = 0;
-        for (auto& sr : searches) {
-            time_t tm = sr.getNextStepTime(types, now.tv_sec);
-            if (tm == 0) continue;
-            if (tm <= now.tv_sec) {
-                searchStep(sr);
-                tm = sr.getNextStepTime(types, now.tv_sec);
-            }
-            if (tm != 0 && (search_time == 0 || search_time > tm))
-                search_time = tm;
-        }
-        if (search_time == 0)
-            DHT_DEBUG("next search_time : (none)");
-        else if (search_time < now.tv_sec)
-            DHT_DEBUG("next search_time : %lu (ASAP)");
-        else
-            DHT_DEBUG("next search_time : %lu (in %lu s)", search_time, search_time-now.tv_sec);
-    }
-
-    if (now.tv_sec >= confirm_nodes_time) {
-        bool soon = false;
-
-        soon |= bucketMaintenance(buckets);
-        soon |= bucketMaintenance(buckets6);
-
-        if (!soon) {
-            if (mybucket_grow_time >= now.tv_sec - 150)
-                soon |= neighbourhoodMaintenance(buckets);
-            if (mybucket6_grow_time >= now.tv_sec - 150)
-                soon |= neighbourhoodMaintenance(buckets6);
-        }
-
-        /* In order to maintain all buckets' age within 600 seconds, worst
-           case is roughly 27 seconds, assuming the table is 22 bits deep.
-           We want to keep a margin for neighborhood maintenance, so keep
-           this within 25 seconds. */
-        auto time_dis = soon ?
-               std::uniform_int_distribution<time_t> {5 , 25}
-             : std::uniform_int_distribution<time_t> {60, 180};
-        confirm_nodes_time = now.tv_sec + time_dis(rd);
-
-        dumpTables();
-    }
-
-    if (confirm_nodes_time > now.tv_sec)
-        *tosleep = confirm_nodes_time - now.tv_sec;
-    else
-        *tosleep = 0;
-
-    if (search_time > 0) {
-        if (search_time <= now.tv_sec)
-            *tosleep = 0;
-        else if (*tosleep > search_time - now.tv_sec)
-            *tosleep = search_time - now.tv_sec;
-    }
-}
-
-std::vector<Dht::ValuesExport>
-Dht::exportValues() const
-{
-    std::vector<ValuesExport> e {};
-    e.reserve(store.size());
-    for (const auto& h : store) {
-        ValuesExport ve;
-        ve.first = h.id;
-        serialize<uint16_t>(h.values.size(), ve.second);
-        for (const auto& v : h.values) {
-            Blob vde;
-            serialize<time_t>(v.time, ve.second);
-            v.data->pack(ve.second);
-        }
-        e.push_back(std::move(ve));
-    }
-    return e;
-}
-
-void
-Dht::importValues(const std::vector<ValuesExport>& import)
-{
-    for (const auto& h : import) {
-        if (h.second.empty())
-            continue;
-        auto b = h.second.begin(),
-             e = h.second.end();
-        try {
-            const size_t n_vals = deserialize<uint16_t>(b, e);
-            for (unsigned i = 0; i < n_vals; i++) {
-                time_t val_time;
-                Value tmp_val;
-                try {
-                    val_time = deserialize<time_t>(b, e);
-                    tmp_val.unpack(b, e);
-                } catch (const std::exception&) {
-                    DHT_ERROR("Error reading value at %s", h.first.toString().c_str());
-                    continue;
-                }
-                auto st = storageStore(h.first, std::make_shared<Value>(std::move(tmp_val)));
-                st->time = val_time;
-            }
-        } catch (const std::exception&) {
-            DHT_ERROR("Error reading values at %s", h.first.toString().c_str());
-            continue;
-        }
-    }
-}
-
-
-std::vector<Dht::NodeExport>
-Dht::exportNodes()
-{
-    std::vector<NodeExport> nodes;
-    const auto b4 = buckets.findBucket(myid);
-    if (b4 != buckets.end()) {
-        for (auto& n : b4->nodes)
-            if (n.isGood(now.tv_sec))
-                nodes.push_back(n.exportNode());
-    }
-    const auto b6 = buckets6.findBucket(myid);
-    if (b6 != buckets6.end()) {
-        for (auto& n : b6->nodes)
-            if (n.isGood(now.tv_sec))
-                nodes.push_back(n.exportNode());
-    }
-    for (auto b = buckets.begin(); b != buckets.end(); ++b) {
-        if (b == b4) continue;
-        for (auto& n : b->nodes)
-            if (n.isGood(now.tv_sec))
-                nodes.push_back(n.exportNode());
-    }
-    for (auto b = buckets6.begin(); b != buckets6.end(); ++b) {
-        if (b == b6) continue;
-        for (auto& n : b->nodes)
-            if (n.isGood(now.tv_sec))
-                nodes.push_back(n.exportNode());
-    }
-    return nodes;
-}
-
-bool
-Dht::insertNode(const InfoHash& id, const sockaddr *sa, socklen_t salen)
-{
-    if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) {
-        errno = EAFNOSUPPORT;
-        return false;
-    }
-    Node *n = newNode(id, sa, salen, 0);
-    return !!n;
-}
-
-int
-Dht::pingNode(const sockaddr *sa, socklen_t salen)
-{
-    DHT_DEBUG("Sending ping.");
-    return sendPing(sa, salen, TransId {TransPrefix::PING});
-}
-
-/* We could use a proper bencoding printer and parser, but the format of
-   DHT messages is fairly stylised, so this seemed simpler. */
-
-#define CHECK(offset, delta, size)                  \
-    if (offset + delta > size) throw std::length_error("Provided buffer is not large enough.");
-
-#define INC(offset, delta, size)                    \
-    if (delta < 0) throw std::length_error("Provided buffer is not large enough."); \
-    CHECK(offset, (size_t)delta, size);             \
-    offset += delta
-
-#define COPY(buf, offset, src, delta, size)         \
-    CHECK(offset, delta, size);                     \
-    memcpy(buf + offset, src, delta);               \
-    offset += delta;
-
-#define ADD_V(buf, offset, size)                    \
-    COPY(buf, offset, my_v, sizeof(my_v), size);
-
-int
-Dht::send(const void *buf, size_t len, int flags, const sockaddr *sa, socklen_t salen)
-{
-    if (salen == 0)
-        return -1;
-
-    if (isNodeBlacklisted(sa, salen)) {
-        DHT_DEBUG("Attempting to send to blacklisted node.");
-        errno = EPERM;
-        return -1;
-    }
-
-    int s;
-    if (sa->sa_family == AF_INET)
-        s = dht_socket;
-    else if (sa->sa_family == AF_INET6)
-        s = dht_socket6;
-    else
-        s = -1;
-
-    if (s < 0) {
-        errno = EAFNOSUPPORT;
-        return -1;
-    }
-    return sendto(s, buf, len, flags, sa, salen);
-}
-
-int
-Dht::sendPing(const sockaddr *sa, socklen_t salen, TransId tid)
-{
-    char buf[512];
-    int i = 0, rc;
-    rc = snprintf(buf + i, 512 - i, "d1:ad2:id20:"); INC(i, rc, 512);
-    COPY(buf, i, myid.data(), myid.size(), 512);
-    rc = snprintf(buf + i, 512 - i, "e1:q4:ping1:t%d:", tid.length);
-    INC(i, rc, 512);
-    COPY(buf, i, tid.data(), tid.length, 512);
-    ADD_V(buf, i, 512);
-    rc = snprintf(buf + i, 512 - i, "1:y1:qe"); INC(i, rc, 512);
-    return send(buf, i, 0, sa, salen);
-}
-
-int
-Dht::sendPong(const sockaddr *sa, socklen_t salen, TransId tid)
-{
-    char buf[512];
-    int i = 0, rc;
-    rc = snprintf(buf + i, 512 - i, "d1:rd2:id20:"); INC(i, rc, 512);
-    COPY(buf, i, myid.data(), myid.size(), 512);
-    rc = snprintf(buf + i, 512 - i, "e1:t%d:", tid.length); INC(i, rc, 512);
-    COPY(buf, i, tid.data(), tid.length, 512);
-    ADD_V(buf, i, 512);
-    rc = snprintf(buf + i, 512 - i, "1:y1:re"); INC(i, rc, 512);
-    return send(buf, i, 0, sa, salen);
-}
-
-int
-Dht::sendFindNode(const sockaddr *sa, socklen_t salen, TransId tid,
-               const InfoHash& target, int want, int confirm)
-{
-    constexpr const size_t BUF_SZ = 512;
-    char buf[BUF_SZ];
-    int i = 0, rc;
-    rc = snprintf(buf + i, BUF_SZ - i, "d1:ad2:id20:"); INC(i, rc, BUF_SZ);
-    COPY(buf, i, myid.data(), myid.size(), BUF_SZ);
-    rc = snprintf(buf + i, BUF_SZ - i, "6:target20:"); INC(i, rc, BUF_SZ);
-    COPY(buf, i, target.data(), target.size(), BUF_SZ);
-    if (want > 0) {
-        rc = snprintf(buf + i, BUF_SZ - i, "4:wantl%s%se",
-                      (want & WANT4) ? "2:n4" : "",
-                      (want & WANT6) ? "2:n6" : "");
-        INC(i, rc, BUF_SZ);
-    }
-    rc = snprintf(buf + i, BUF_SZ - i, "e1:q9:find_node1:t%d:", tid.length);
-    INC(i, rc, BUF_SZ);
-    COPY(buf, i, tid.data(), tid.length, BUF_SZ);
-    ADD_V(buf, i, BUF_SZ);
-    rc = snprintf(buf + i, BUF_SZ - i, "1:y1:qe"); INC(i, rc, BUF_SZ);
-    return send(buf, i, confirm ? MSG_CONFIRM : 0, sa, salen);
-}
-
-int
-Dht::sendNodesValues(const sockaddr *sa, socklen_t salen, TransId tid,
-                 const uint8_t *nodes, unsigned nodes_len,
-                 const uint8_t *nodes6, unsigned nodes6_len,
-                 Storage *st, const Blob& token)
-{
-    constexpr const size_t BUF_SZ = 2048 * 64;
-    char buf[BUF_SZ];
-    int i = 0, rc;
-
-    rc = snprintf(buf + i, BUF_SZ - i, "d1:rd2:id20:"); INC(i, rc, BUF_SZ);
-    COPY(buf, i, myid.data(), myid.size(), BUF_SZ);
-    if (nodes_len > 0) {
-        rc = snprintf(buf + i, BUF_SZ - i, "5:nodes%u:", nodes_len);
-        INC(i, rc, BUF_SZ);
-        COPY(buf, i, nodes, nodes_len, BUF_SZ);
-    }
-    if (nodes6_len > 0) {
-         rc = snprintf(buf + i, BUF_SZ - i, "6:nodes6%u:", nodes6_len);
-         INC(i, rc, BUF_SZ);
-         COPY(buf, i, nodes6, nodes6_len, BUF_SZ);
-    }
-    if (not token.empty()) {
-        rc = snprintf(buf + i, BUF_SZ - i, "5:token%lu:", token.size());
-        INC(i, rc, BUF_SZ);
-        COPY(buf, i, token.data(), token.size(), BUF_SZ);
-    }
-
-    if (st && st->values.size() > 0) {
-        /* We treat the storage as a circular list, and serve a randomly
-           chosen slice.  In order to make sure we fit,
-           we limit ourselves to 50 values. */
-        std::uniform_int_distribution<> pos_dis(0, st->values.size()-1);
-        unsigned j0 = pos_dis(rd);
-        unsigned j = j0;
-        unsigned k = 0;
-
-        rc = snprintf(buf + i, BUF_SZ - i, "6:valuesl"); INC(i, rc, BUF_SZ);
-        do {
-            Blob packed_value;
-            st->values[j].data->pack(packed_value);
-            rc = snprintf(buf + i, BUF_SZ - i, "%lu:", packed_value.size()); INC(i, rc, BUF_SZ);
-            COPY(buf, i, packed_value.data(), packed_value.size(), BUF_SZ);
-            k++;
-            j = (j + 1) % st->values.size();
-        } while (j != j0 && k < 50);
-        rc = snprintf(buf + i, BUF_SZ - i, "e"); INC(i, rc, BUF_SZ);
-    }
-
-    rc = snprintf(buf + i, BUF_SZ - i, "e1:t%d:", tid.length); INC(i, rc, BUF_SZ);
-    COPY(buf, i, tid.data(), tid.length, BUF_SZ);
-    ADD_V(buf, i, BUF_SZ);
-    rc = snprintf(buf + i, BUF_SZ - i, "1:y1:re"); INC(i, rc, BUF_SZ);
-
-    return send(buf, i, 0, sa, salen);
-}
-
-unsigned
-Dht::insertClosestNode(uint8_t *nodes, unsigned numnodes, const InfoHash& id, const Node& n)
-{
-    unsigned i, size;
-
-    if (n.ss.ss_family == AF_INET)
-        size = HASH_LEN + sizeof(in_addr) + sizeof(in_port_t); // 26
-    else if (n.ss.ss_family == AF_INET6)
-        size = HASH_LEN + sizeof(in6_addr) + sizeof(in_port_t); // 38
-    else
-        return numnodes;
-
-    for (i = 0; i < numnodes; i++) {
-        const InfoHash* nid = reinterpret_cast<const InfoHash*>(nodes + size * i);
-        if (InfoHash::cmp(n.id, *nid) == 0)
-            return numnodes;
-        if (id.xorCmp(n.id, *nid) < 0)
-            break;
-    }
-
-    if (i >= 8)
-        return numnodes;
-
-    if (numnodes < 8)
-        numnodes++;
-
-    if (i < numnodes - 1)
-        memmove(nodes + size * (i + 1), nodes + size * i, size * (numnodes - i - 1));
-
-    if (n.ss.ss_family == AF_INET) {
-        sockaddr_in *sin = (sockaddr_in*)&n.ss;
-        memcpy(nodes + size * i, n.id.data(), HASH_LEN);
-        memcpy(nodes + size * i + HASH_LEN, &sin->sin_addr, sizeof(in_addr));
-        memcpy(nodes + size * i + HASH_LEN + sizeof(in_addr), &sin->sin_port, 2);
-    }
-    else if (n.ss.ss_family == AF_INET6) {
-        sockaddr_in6 *sin6 = (sockaddr_in6*)&n.ss;
-        memcpy(nodes + size * i, n.id.data(), HASH_LEN);
-        memcpy(nodes + size * i + HASH_LEN, &sin6->sin6_addr, sizeof(in6_addr));
-        memcpy(nodes + size * i + HASH_LEN + sizeof(in6_addr), &sin6->sin6_port, 2);
-    }
-
-    return numnodes;
-}
-
-unsigned
-Dht::bufferClosestNodes(uint8_t *nodes, unsigned numnodes, const InfoHash& id, const Bucket& b) const
-{
-    for (auto& n : b.nodes) {
-        if (n.isGood(now.tv_sec))
-            numnodes = insertClosestNode(nodes, numnodes, id, n);
-    }
-    return numnodes;
-}
-
-int
-Dht::sendClosestNodes(const sockaddr *sa, socklen_t salen, TransId tid,
-                    const InfoHash& id, int want, const Blob& token, Storage *st)
-{
-    uint8_t nodes[8 * 26];
-    uint8_t nodes6[8 * 38];
-    unsigned numnodes = 0, numnodes6 = 0;
-
-    if (want < 0)
-        want = sa->sa_family == AF_INET ? WANT4 : WANT6;
-
-    if ((want & WANT4)) {
-        auto b = buckets.findBucket(id);
-        if (b != buckets.end()) {
-            numnodes = bufferClosestNodes(nodes, numnodes, id, *b);
-            if (std::next(b) != buckets.end())
-                numnodes = bufferClosestNodes(nodes, numnodes, id, *std::next(b));
-            if (b != buckets.begin())
-                numnodes = bufferClosestNodes(nodes, numnodes, id, *std::prev(b));
-        }
-    }
-
-    if ((want & WANT6)) {
-        auto b = buckets6.findBucket(id);
-        if (b != buckets6.end()) {
-            numnodes6 = bufferClosestNodes(nodes6, numnodes6, id, *b);
-            if (std::next(b) != buckets6.end())
-                numnodes6 = bufferClosestNodes(nodes6, numnodes6, id, *std::next(b));
-            if (b != buckets6.begin())
-                numnodes6 = bufferClosestNodes(nodes6, numnodes6, id, *std::prev(b));
-        }
-    }
-    DHT_DEBUG("sending closest nodes (%d+%d nodes.)", numnodes, numnodes6);
-
-    try {
-        return sendNodesValues(sa, salen, tid,
-                                nodes, numnodes * 26,
-                                nodes6, numnodes6 * 38,
-                                st, token);
-    } catch (const std::overflow_error& e) {
-        DHT_ERROR("Can't send value: buffer not large enough !");
-        return -1;
-    }
-}
-
-int
-Dht::sendGetValues(const sockaddr *sa, socklen_t salen,
-               TransId tid, const InfoHash& infohash,
-               int want, int confirm)
-{
-    const size_t BUF_SZ = 2048 * 4;
-    char buf[BUF_SZ];
-    size_t i = 0;
-    int rc;
-
-    rc = snprintf(buf + i, BUF_SZ - i, "d1:ad2:id20:"); INC(i, rc, BUF_SZ);
-    COPY(buf, i, myid.data(), myid.size(), BUF_SZ);
-    rc = snprintf(buf + i, BUF_SZ - i, "9:info_hash20:"); INC(i, rc, BUF_SZ);
-    COPY(buf, i, infohash.data(), infohash.size(), BUF_SZ);
-    if (want > 0) {
-        rc = snprintf(buf + i, BUF_SZ - i, "4:wantl%s%se",
-                      (want & WANT4) ? "2:n4" : "",
-                      (want & WANT6) ? "2:n6" : "");
-        INC(i, rc, BUF_SZ);
-    }
-    rc = snprintf(buf + i, BUF_SZ - i, "e1:q9:get_peers1:t%d:", tid.length);
-    INC(i, rc, BUF_SZ);
-    COPY(buf, i, tid.data(), tid.length, BUF_SZ);
-    ADD_V(buf, i, BUF_SZ);
-    rc = snprintf(buf + i, BUF_SZ - i, "1:y1:qe"); INC(i, rc, BUF_SZ);
-    return send(buf, i, confirm ? MSG_CONFIRM : 0, sa, salen);
-}
-
-int
-Dht::sendAnnounceValue(const sockaddr *sa, socklen_t salen, TransId tid,
-                   const InfoHash& infohash, const Value& value,
-                   const Blob& token, int confirm)
-{
-    const size_t BUF_SZ = 2048 * 4;
-    char buf[BUF_SZ];
-    size_t i = 0;
-    int rc;
-
-    rc = snprintf(buf + i, BUF_SZ - i, "d1:ad2:id%lu:", myid.size()); INC(i, rc, BUF_SZ);
-    COPY(buf, i, myid.data(), myid.size(), BUF_SZ);
-    rc = snprintf(buf + i, BUF_SZ - i, "9:info_hash%lu:", infohash.size()); INC(i, rc, BUF_SZ);
-    COPY(buf, i, infohash.data(), infohash.size(), BUF_SZ);
-
-    Blob packed_value;
-    value.pack(packed_value);
-    rc = snprintf(buf + i, BUF_SZ - i, "6:valuesl%lu:", packed_value.size()); INC(i, rc, BUF_SZ);
-    COPY(buf, i, packed_value.data(), packed_value.size(), BUF_SZ);
-    rc = snprintf(buf + i, BUF_SZ - i, "e5:token%lu:", token.size()); INC(i, rc, BUF_SZ);
-    COPY(buf, i, token.data(), token.size(), BUF_SZ);
-    rc = snprintf(buf + i, BUF_SZ - i, "e1:q13:announce_peer1:t%u:", tid.length); INC(i, rc, BUF_SZ);
-    COPY(buf, i, tid.data(), tid.length, BUF_SZ);
-    ADD_V(buf, i, BUF_SZ);
-    rc = snprintf(buf + i, BUF_SZ - i, "1:y1:qe"); INC(i, rc, BUF_SZ);
-
-    return send(buf, i, confirm ? 0 : MSG_CONFIRM, sa, salen);
-}
-
-int
-Dht::sendValueAnnounced(const sockaddr *sa, socklen_t salen, TransId tid, Value::Id vid)
-{
-    char buf[512];
-    int i = 0, rc;
-
-    rc = snprintf(buf + i, 512 - i, "d1:rd2:id20:"); INC(i, rc, 512);
-    COPY(buf, i, myid.data(), myid.size(), 512);
-    rc = snprintf(buf + i, 512 - i, "3:vid%lu:", sizeof(Value::Id)); INC(i, rc, 512);
-    COPY(buf, i, &vid, sizeof(Value::Id), 512);
-    rc = snprintf(buf + i, 512 - i, "e1:t%u:", tid.length); INC(i, rc, 512);
-    COPY(buf, i, tid.data(), tid.length, 512);
-    ADD_V(buf, i, 512);
-    rc = snprintf(buf + i, 512 - i, "1:y1:re"); INC(i, rc, 512);
-    return send(buf, i, 0, sa, salen);
-}
-
-int
-Dht::sendError(const sockaddr *sa, socklen_t salen, TransId tid, int code, const char *message)
-{
-    char buf[512];
-    int i = 0, rc;
-
-    rc = snprintf(buf + i, 512 - i, "d1:eli%de%d:", code, (int)strlen(message));
-    INC(i, rc, 512);
-    COPY(buf, i, message, (int)strlen(message), 512);
-    rc = snprintf(buf + i, 512 - i, "e1:t%d:", tid.length); INC(i, rc, 512);
-    COPY(buf, i, tid.data(), tid.length, 512);
-    ADD_V(buf, i, 512);
-    rc = snprintf(buf + i, 512 - i, "1:y1:ee"); INC(i, rc, 512);
-    return send(buf, i, 0, sa, salen);
-}
-
-#undef CHECK
-#undef INC
-#undef COPY
-#undef ADD_V
-
-Dht::MessageType
-Dht::parseMessage(const uint8_t *buf, size_t buflen,
-              TransId& tid_return,
-              InfoHash& id_return, InfoHash& info_hash_return,
-              InfoHash& target_return, in_port_t& port_return,
-              Blob& token, Value::Id& value_id,
-              uint8_t *nodes_return, unsigned *nodes_len,
-              uint8_t *nodes6_return, unsigned *nodes6_len,
-              std::vector<std::shared_ptr<Value>>& values_return,
-              int *want_return, uint16_t& error_code)
-{
-    const uint8_t *p;
-
-    /* This code will happily crash if the buffer is not NUL-terminated. */
-    if (buf[buflen] != '\0')
-        throw DhtException("Eek!  parse_message with unterminated buffer.");
-
-#define CHECK(ptr, len) if (((uint8_t*)ptr) + (len) > (buf) + (buflen)) throw std::out_of_range("Truncated message.");
-
-    p = (uint8_t*)dht_memmem(buf, buflen, "1:t", 3);
-    if (p) {
-        char *q;
-        size_t l = strtoul((char*)p + 3, &q, 10);
-        if (q && *q == ':') {
-            CHECK(q + 1, l);
-            tid_return = {q+1, l};
-        } else
-            tid_return.length = 0;
-    }
-
-    p = (uint8_t*)dht_memmem(buf, buflen, "2:id20:", 7);
-    if (p) {
-        CHECK(p + 7, HASH_LEN);
-        memcpy(id_return.data(), p + 7, HASH_LEN);
-    } else {
-        id_return = {};
-    }
-
-    p = (uint8_t*)dht_memmem(buf, buflen, "9:info_hash20:", 14);
-    if (p) {
-        CHECK(p + 14, HASH_LEN);
-        memcpy(info_hash_return.data(), p + 14, HASH_LEN);
-    } else {
-        info_hash_return = {};
-    }
-
-    p = (uint8_t*)dht_memmem(buf, buflen, "porti", 5);
-    if (p) {
-        char *q;
-        unsigned long l = strtoul((char*)p + 5, &q, 10);
-        if (q && *q == 'e' && l < 0x10000)
-            port_return = l;
-        else
-            port_return = 0;
-    } else
-        port_return = 0;
-
-    p = (uint8_t*)dht_memmem(buf, buflen, "6:target20:", 11);
-    if (p) {
-        CHECK(p + 11, HASH_LEN);
-        memcpy(target_return.data(), p + 11, HASH_LEN);
-    } else {
-        target_return = {};
-    }
-
-    p = (uint8_t*)dht_memmem(buf, buflen, "5:token", 7);
-    if (p) {
-        char *q;
-        size_t l = strtoul((char*)p + 7, &q, 10);
-        if (q && *q == ':' && l > 0 && l <= 128) {
-            CHECK(q + 1, l);
-            token.clear();
-            token.insert(token.begin(), q + 1, q + 1 + l);
-        }
-    }
-
-    if (nodes_len) {
-        p = (uint8_t*)dht_memmem(buf, buflen, "5:nodes", 7);
-        if (p) {
-            char *q;
-            size_t l = strtoul((char*)p + 7, &q, 10);
-            if (q && *q == ':' && l > 0 && l < *nodes_len) {
-                CHECK(q + 1, l);
-                memcpy(nodes_return, q + 1, l);
-                *nodes_len = l;
-            } else
-                *nodes_len = 0;
-        } else
-            *nodes_len = 0;
-    }
-
-    if (nodes6_len) {
-        p = (uint8_t*)dht_memmem(buf, buflen, "6:nodes6", 8);
-        if (p) {
-            char *q;
-            size_t l = strtoul((char*)p + 8, &q, 10);
-            if (q && *q == ':' && l > 0 && l < *nodes6_len) {
-                CHECK(q + 1, l);
-                memcpy(nodes6_return, q + 1, l);
-                *nodes6_len = l;
-            } else
-                *nodes6_len = 0;
-        } else
-            *nodes6_len = 0;
-    }
-
-    p = (uint8_t*)dht_memmem(buf, buflen, "6:valuesl", 9);
-    if (p) {
-        unsigned i = p - buf + 9;
-        while (true) {
-            char *q;
-            size_t l = strtoul((char*)buf + i, &q, 10);
-            if (q && *q == ':' && l > 0) {
-                CHECK(q + 1, l);
-                i = q + 1 + l - (char*)buf;
-                Value v;
-                v.unpackBlob(Blob {q + 1, q + 1 + l});
-                values_return.push_back(std::make_shared<Value>(std::move(v)));
-            } else
-                break;
-        }
-        if (i >= buflen || buf[i] != 'e')
-            DHT_DEBUG("eek... unexpected end for values.");
-    }
-
-    p = (uint8_t*)dht_memmem(buf, buflen, "3:vid8:", 7);
-    if (p) {
-        CHECK(p + 7, sizeof(value_id));
-        memcpy(&value_id, p + 7, sizeof(value_id));
-    } else {
-        value_id = Value::INVALID_ID;
-    }
-
-    if (want_return) {
-        p = (uint8_t*)dht_memmem(buf, buflen, "4:wantl", 7);
-        if (p) {
-            unsigned i = p - buf + 7;
-            *want_return = 0;
-            while (buf[i] > '0' && buf[i] <= '9' && buf[i + 1] == ':' &&
-                  i + 2 + buf[i] - '0' < buflen) {
-                CHECK(buf + i + 2, buf[i] - '0');
-                if (buf[i] == '2' && memcmp(buf + i + 2, "n4", 2) == 0)
-                    *want_return |= WANT4;
-                else if (buf[i] == '2' && memcmp(buf + i + 2, "n6", 2) == 0)
-                    *want_return |= WANT6;
-                else
-                    DHT_DEBUG("eek... unexpected want flag (%c)", buf[i]);
-                i += 2 + buf[i] - '0';
-            }
-            if (i >= buflen || buf[i] != 'e')
-                DHT_DEBUG("eek... unexpected end for want.");
-        } else {
-            *want_return = -1;
-        }
-    }
-
-    p = (uint8_t*)dht_memmem(buf, buflen, "1:eli", 5);
-    if (p) {
-        CHECK(p + 5, sizeof(error_code));
-        memcpy(&error_code, p + 5, sizeof(error_code));
-    } else {
-        error_code = 0;
-    }
-
-#undef CHECK
-
-    if (dht_memmem(buf, buflen, "1:y1:r", 6))
-        return MessageType::Reply;
-    if (dht_memmem(buf, buflen, "1:y1:e", 6))
-        return MessageType::Error;
-    if (!dht_memmem(buf, buflen, "1:y1:q", 6))
-        throw DhtException("Parse error");
-    if (dht_memmem(buf, buflen, "1:q4:ping", 9))
-        return MessageType::Ping;
-    if (dht_memmem(buf, buflen, "1:q9:find_node", 14))
-       return MessageType::FindNode;
-    if (dht_memmem(buf, buflen, "1:q9:get_peers", 14))
-        return MessageType::GetValues;
-    if (dht_memmem(buf, buflen, "1:q13:announce_peer", 19))
-       return MessageType::AnnounceValue;
-    throw DhtException("Can't read message type.");
-}
-
-#ifdef HAVE_MEMMEM
-
-void *
-Dht::dht_memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen)
-{
-    return memmem(haystack, haystacklen, needle, needlelen);
-}
-
-#else
-
-void *
-Dht::dht_memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen)
-{
-    const char *h = (const char *)haystack;
-    const char *n = (const char *)needle;
-    size_t i;
-
-    /* size_t is unsigned */
-    if (needlelen > haystacklen)
-        return NULL;
-
-    for (i = 0; i <= haystacklen - needlelen; i++) {
-        if (memcmp(h + i, n, needlelen) == 0)
-            return (void*)(h + i);
-    }
-    return NULL;
-}
-
-#endif
-
-}
diff --git a/daemon/src/ringdht/dhtcpp/dht.h b/daemon/src/ringdht/dhtcpp/dht.h
deleted file mode 100644
index ecc3d2eb2e..0000000000
--- a/daemon/src/ringdht/dhtcpp/dht.h
+++ /dev/null
@@ -1,594 +0,0 @@
-/*
-Copyright (c) 2009-2014 Juliusz Chroboczek
-Copyright (c) 2014 Savoir-Faire Linux Inc.
-
-Authors : Adrien Béraud <adrien.beraud@savoirfairelinux.com>,
-          Juliusz Chroboczek <jch@pps.univ–paris–diderot.fr>
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-*/
-
-#pragma once
-
-#include "infohash.h"
-#include "value.h"
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-
-#include <string>
-#include <array>
-#include <vector>
-#include <map>
-#include <list>
-#include <functional>
-#include <algorithm>
-#include <memory>
-
-namespace dht {
-
-/**
- * Main Dht class.
- * Provides a Distributed Hash Table node.
- *
- * Must be given open UDP sockets and ::periodic must be
- * called regularly.
- */
-class Dht {
-public:
-
-    enum class Status {
-        Disconnected, // 0 nodes
-        Connecting,   // 1+ nodes
-        Connected     // 4+ good nodes
-    };
-
-    typedef std::function<bool(const std::vector<std::shared_ptr<Value>>& values)> GetCallback;
-    typedef std::function<void(bool success)> DoneCallback;
-
-    struct NodeExport {
-        InfoHash id;
-        sockaddr_storage ss;
-        socklen_t sslen;
-    };
-
-    Dht() {}
-
-    /**
-     * Initialise the Dht with two open sockets (for IPv4 and IP6)
-     * and an ID for the node.
-     */
-    Dht(int s, int s6, const InfoHash& id);
-    virtual ~Dht();
-
-    /**
-     * Get the ID of the node, which was provided in the constructor.
-     */
-    inline const InfoHash& getId() const { return myid; }
-
-    /**
-     * Get the current status of the node for the given family.
-     */
-    Status getStatus(sa_family_t af) const;
-
-    /**
-     * Returns true if the node have access to an open socket
-     * for the provided family.
-     */
-    bool isRunning(sa_family_t af) const;
-
-    /**
-     * Enable or disable logging of DHT internal messages
-     */
-    void setLoggers(LogMethod&& error = NOLOG, LogMethod&& warn = NOLOG, LogMethod&& debug = NOLOG);
-
-    virtual void registerType(const ValueType& type) {
-        types[type.id] = type;
-    }
-    const ValueType& getType(ValueType::Id type_id) const {
-        const auto& t_it = types.find(type_id);
-        return (t_it == types.end()) ? ValueType::USER_DATA : t_it->second;
-    }
-
-    /**
-     * Insert a node in the main routing table.
-     * The node is not pinged, so this should be
-     * used to bootstrap efficiently from previously known nodes.
-     */
-    bool insertNode(const InfoHash& id, const sockaddr*, socklen_t);
-    bool insertNode(const NodeExport& n) {
-        return insertNode(n.id, reinterpret_cast<const sockaddr*>(&n.ss), n.sslen);
-    }
-
-    int pingNode(const sockaddr*, socklen_t);
-
-    void periodic(const uint8_t *buf, size_t buflen, const sockaddr *from, socklen_t fromlen, time_t *tosleep);
-
-    /**
-     * Get a value by searching on all available protocols (IPv4, IPv6),
-     * and call the callback when some values are found.
-     * The operation will start as soon as the node is connected to the network.
-     * GetCallback will be called every time new values are found, until
-     * GetCallback returns false or the search completes.
-     * Then, DoneCallback is called.
-     */
-    void get(const InfoHash& id, GetCallback cb, DoneCallback donecb=nullptr, Value::Filter = Value::AllFilter());
-
-    /**
-     * Get locally stored data for the given hash.
-     */
-    std::vector<std::shared_ptr<Value>> getLocal(const InfoHash& id, Value::Filter f = Value::AllFilter()) const;
-
-    /**
-     * Get locally stored data for the given hash and value id.
-     */
-    std::shared_ptr<Value> getLocal(const InfoHash& id, const Value::Id& vid) const;
-
-    /**
-     * Announce a value on all available protocols (IPv4, IPv6), and
-     * automatically re-announce when it's about to expire.
-     * The operation will start as soon as the node is connected to the network.
-     * The done callback will be called once, when the first announce succeeds, or fails.
-     *
-     * A "put" operation will never end by itself because the value will need to be
-     * reannounced on a regular basis.
-     * User can call #cancelPut(InfoHash, Value::Id) to cancel a put operation.
-     */
-    void put(const InfoHash&, Value&&, DoneCallback cb=nullptr);
-
-    /**
-     * Get data currently being put at the given hash.
-     */
-    std::vector<std::shared_ptr<Value>> getPut(const InfoHash&);
-
-    /**
-     * Get data currently being put at the given hash with the given id.
-     */
-    std::shared_ptr<Value> getPut(const InfoHash&, const Value::Id&);
-
-    /**
-     * Stop any put/announce operation at the given location,
-     * for values with the given id.
-     */
-    bool cancelPut(const InfoHash&, const Value::Id&);
-
-    /**
-     * Get the list of good nodes for local storage saving purposes
-     * The list is ordered to minimize the back-to-work delay.
-     */
-    std::vector<NodeExport> exportNodes();
-
-    typedef std::pair<InfoHash, Blob> ValuesExport;
-    std::vector<ValuesExport> exportValues() const;
-    void importValues(const std::vector<ValuesExport>&);
-
-    int getNodesStats(sa_family_t af, unsigned *good_return, unsigned *dubious_return, unsigned *cached_return, unsigned *incoming_return) const;
-    void dumpTables() const;
-
-    /* This must be provided by the user. */
-    static bool isBlacklisted(const sockaddr*, socklen_t) { return false; }
-
-protected:
-    LogMethod DHT_DEBUG = NOLOG;
-    LogMethod DHT_WARN = NOLOG;
-    LogMethod DHT_ERROR = NOLOG;
-
-private:
-
-    /* When performing a search, we search for up to SEARCH_NODES closest nodes
-       to the destination, and use the additional ones to backtrack if any of
-       the target 8 turn out to be dead. */
-    static const unsigned SEARCH_NODES {14};
-
-    /* The maximum number of values we store for a given hash. */
-    static const unsigned MAX_VALUES {2048};
-
-    /* The maximum number of hashes we're willing to track. */
-    static const unsigned MAX_HASHES {16384};
-
-    /* The maximum number of searches we keep data about. */
-    static const unsigned MAX_SEARCHES {1024};
-
-    /* A search with no nodes will timeout after this time. */
-    static const time_t SEARCH_TIMEOUT {60};
-
-    /* The time after which we can send get requests for
-       a search in case of no answers. */
-    static const time_t SEARCH_GET_STEP {15};
-
-    /* The time after which we consider a search to be expirable. */
-    static const time_t SEARCH_EXPIRE_TIME {62 * 60};
-
-    /* The maximum number of nodes that we snub.  There is probably little
-        reason to increase this value. */
-    static const unsigned BLACKLISTED_MAX {10};
-
-    static const long unsigned MAX_REQUESTS_PER_SEC;
-
-    static const time_t TOKEN_EXPIRE_TIME {10 * 60};
-
-    static const unsigned TOKEN_SIZE {64};
-
-    struct Node {
-        InfoHash id {};
-        sockaddr_storage ss;
-        socklen_t sslen {0};
-        time_t time {0};            /* time of last message received */
-        time_t reply_time {0};      /* time of last correct reply received */
-        time_t pinged_time {0};     /* time of last request */
-        unsigned pinged {0};        /* how many requests we sent since last reply */
-
-        Node() {
-            std::fill_n((uint8_t*)&ss, sizeof(ss), 0);
-        }
-        Node(const InfoHash& id, const sockaddr* sa, socklen_t salen, time_t t, time_t reply_time)
-            : id(id), sslen(salen), time(t), reply_time(reply_time) {
-            std::copy_n((const uint8_t*)sa, salen, (uint8_t*)&ss);
-        }
-        bool isGood(time_t now) const;
-        NodeExport exportNode() const { return NodeExport {id, ss, sslen}; }
-    };
-
-    struct Bucket {
-        Bucket() {}
-        Bucket(sa_family_t af, const InfoHash& f = {}, time_t t = 0)
-            : af(af), first(f), time(t) {}
-        sa_family_t af {0};
-        InfoHash first {};
-        time_t time {0};             /* time of last reply in this bucket */
-        std::list<Node> nodes {};
-        sockaddr_storage cached {};  /* the address of a likely candidate */
-        socklen_t cachedlen {0};
-
-        /** Return a random node in a bucket. */
-        Node* randomNode();
-    };
-
-    class RoutingTable : public std::list<Bucket> {
-    public:
-        using std::list<Bucket>::list;
-
-        InfoHash middle(const RoutingTable::const_iterator&) const;
-
-        RoutingTable::iterator findBucket(const InfoHash& id);
-        RoutingTable::const_iterator findBucket(const InfoHash& id) const;
-
-        /**
-         * Returns true if the id is in the bucket's range.
-         */
-        inline bool contains(const RoutingTable::const_iterator& bucket, const InfoHash& id) const {
-            return InfoHash::cmp(bucket->first, id) <= 0
-                && (std::next(bucket) == end() || InfoHash::cmp(id, std::next(bucket)->first) < 0);
-        }
-
-        /**
-         * Returns a random id in the bucket's range.
-         */
-        InfoHash randomId(const RoutingTable::const_iterator& bucket) const;
-
-        /**
-         * Split a bucket in two equal parts.
-         */
-        bool split(const RoutingTable::iterator& b);
-    };
-
-    struct SearchNode {
-        SearchNode() {}
-        SearchNode(const InfoHash& id) : id(id) {}
-
-        struct AnnounceStatus {
-            time_t request_time;    /* the time of the last unanswered announce request */
-            time_t reply_time;      /* the time of the last announce confirmation */
-        };
-        typedef std::map<Value::Id, AnnounceStatus> AnnounceStatusMap;
-
-        /**
-         * Can we use this node to announce ?
-         */
-        bool isSynced(time_t now) const {
-            return /*pinged < 3 && replied &&*/ reply_time > now - 15 * 60;
-        }
-
-        time_t getAnnounceTime(AnnounceStatusMap::const_iterator ack, const ValueType& type) const {
-            if (ack == acked.end())
-                return request_time + 5;
-            return std::max<time_t>({ack->second.reply_time + type.expiration - 3, ack->second.request_time + 5, request_time + 5});
-        }
-        time_t getAnnounceTime(Value::Id vid, const ValueType& type) const {
-            return getAnnounceTime(acked.find(vid), type);
-        }
-
-        InfoHash id {};
-        sockaddr_storage ss {};
-        socklen_t sslen {0};
-        time_t request_time {0};    /* the time of the last unanswered request */
-        time_t reply_time {0};      /* the time of the last reply with a token */
-        unsigned pinged {0};
-        Blob token {};
-
-        AnnounceStatusMap acked {};  /* announcement status for a given value id */
-
-        // Generic temporary flag.
-        // Must be reset to false after use by the algorithm.
-        bool pending {false};
-    };
-
-    struct Announce {
-        std::shared_ptr<Value> value;
-        DoneCallback callback;
-    };
-
-    /**
-     * A search is a pointer to the nodes we think are responsible
-     * for storing values for a given hash.
-     *
-     * A Search has 3 states:
-     * - Idle (nothing to do)
-     * - Syncing (Some nodes not synced)
-     * - Announcing (Some announces not performed on all nodes)
-     */
-    struct Search {
-        uint16_t tid;
-        sa_family_t af;
-        time_t step_time {0};           /* the time of the last search_step */
-        InfoHash id {};
-        std::vector<Announce> announce {};
-        std::vector<std::pair<Value::Filter, GetCallback>> callbacks {};
-        DoneCallback done_callback {nullptr};
-        bool done {false};
-        std::vector<SearchNode> nodes {SEARCH_NODES+1};
-
-        bool insertNode(const InfoHash& id, const sockaddr*, socklen_t, time_t now, bool confirmed=false, const Blob& token={});
-        void insertBucket(const Bucket&, time_t now);
-
-        /**
-         * Can we use this search to announce ?
-         */
-        bool isSynced(time_t now) const;
-
-        /**
-         * Are all values that are registred for announcement announced ?
-         */
-        bool isAnnounced(const std::map<ValueType::Id, ValueType>& types, time_t now) const {
-            auto at = getAnnounceTime(types);
-            return at && at < now;
-        }
-
-        /**
-         * ret = 0 : no announce required.
-         * ret > 0 : (re-)announce required at time ret.
-         */
-        time_t getAnnounceTime(const std::map<ValueType::Id, ValueType>& types) const;
-
-        time_t getNextStepTime(const std::map<ValueType::Id, ValueType>& types, time_t now) const;
-    };
-
-    struct ValueStorage {
-        std::shared_ptr<Value> data {};
-        time_t time {0};
-
-        ValueStorage() {}
-        ValueStorage(const std::shared_ptr<Value>& v, time_t t) : data(v), time(t) {}
-    };
-
-    struct Storage {
-        InfoHash id;
-        std::vector<ValueStorage> values;
-    };
-
-    enum class MessageType {
-        Error = 0,
-        Reply,
-        Ping,
-        FindNode,
-        GetValues,
-        AnnounceValue
-    };
-
-    struct TransPrefix : public  std::array<uint8_t, 2>  {
-        TransPrefix(const std::string& str) : std::array<uint8_t, 2>({(uint8_t)str[0], (uint8_t)str[1]}) {}
-        static const TransPrefix PING;
-        static const TransPrefix FIND_NODE;
-        static const TransPrefix GET_VALUES;
-        static const TransPrefix ANNOUNCE_VALUES;
-    };
-
-    /* Transaction-ids are 4-bytes long, with the first two bytes identifying
-     * the kind of request, and the remaining two a sequence number in
-     * host order.
-     */
-    struct TransId final : public std::array<uint8_t, 4> {
-        TransId() {}
-        TransId(const TransPrefix prefix, uint16_t seqno = 0) {
-            std::copy_n(prefix.begin(), prefix.size(), begin());
-            *reinterpret_cast<uint16_t*>(data()+prefix.size()) = seqno;
-        }
-
-        TransId(const char* q, size_t l) : array<uint8_t, 4>() {
-            if (l > 4) {
-                length = 0;
-            } else {
-                std::copy_n(q, l, begin());
-                length = l;
-            }
-        }
-
-        bool matches(const TransPrefix prefix, uint16_t *seqno_return = nullptr) const {
-            if (std::equal(begin(), begin()+1, prefix.begin())) {
-                if (seqno_return)
-                    *seqno_return = *reinterpret_cast<const uint16_t*>(&(*this)[2]);
-                return true;
-            } else
-                return false;
-        }
-
-        unsigned length {4};
-    };
-
-    // prevent copy
-    Dht(const Dht&) = delete;
-    Dht& operator=(const Dht&) = delete;
-
-    int dht_socket {-1};
-    int dht_socket6 {-1};
-
-    time_t search_time {0};
-    time_t confirm_nodes_time {0};
-    time_t rotate_secrets_time {0};
-
-    InfoHash myid {};
-    static const uint8_t my_v[9];
-    std::array<uint8_t, 8> secret {};
-    std::array<uint8_t, 8> oldsecret {};
-
-    std::map<ValueType::Id, ValueType> types;
-
-    // the stuff
-    RoutingTable buckets {};
-    RoutingTable buckets6 {};
-    std::vector<Storage> store {};
-    std::list<Search> searches {};
-    uint16_t search_id {0};
-
-    sockaddr_storage blacklist[BLACKLISTED_MAX] {};
-    unsigned next_blacklisted = 0;
-
-    struct timeval now {};
-    time_t mybucket_grow_time {0}, mybucket6_grow_time {0};
-    time_t expire_stuff_time {0};
-    time_t rate_limit_time {0};
-
-    long unsigned rate_limit_tokens {MAX_REQUESTS_PER_SEC};
-
-    // Networking & packet handling
-    int send(const void* buf, size_t len, int flags, const sockaddr*, socklen_t);
-    int sendPing(const sockaddr*, socklen_t, TransId tid);
-    int sendPong(const sockaddr*, socklen_t, TransId tid);
-
-    int sendFindNode(const sockaddr*, socklen_t, TransId tid,
-                        const InfoHash& target, int want, int confirm);
-
-    int sendNodesValues(const sockaddr*, socklen_t, TransId tid,
-                              const uint8_t *nodes, unsigned nodes_len,
-                              const uint8_t *nodes6, unsigned nodes6_len,
-                              Storage *st, const Blob& token);
-
-    int sendClosestNodes(const sockaddr*, socklen_t, TransId tid,
-                               const InfoHash& id, int want, const Blob& token={},
-                               Storage *st=nullptr);
-
-    int sendGetValues(const sockaddr*, socklen_t, TransId tid,
-                            const InfoHash& infohash, int want, int confirm);
-
-    int sendAnnounceValue(const sockaddr*, socklen_t, TransId tid,
-                            const InfoHash& infohas, const Value& data,
-                            const Blob& token, int confirm);
-
-    int sendValueAnnounced(const sockaddr*, socklen_t, TransId, Value::Id);
-
-    int sendError(const sockaddr*, socklen_t, TransId tid, int code, const char *message);
-
-    void processMessage(const uint8_t *buf, size_t buflen, const sockaddr *from, socklen_t fromlen);
-    MessageType parseMessage(const uint8_t *buf, size_t buflen,
-                  TransId& tid,
-                  InfoHash& id_return, InfoHash& info_hash_return,
-                  InfoHash& target_return, in_port_t& port_return,
-                  Blob& token, Value::Id& value_id,
-                  uint8_t *nodes_return, unsigned *nodes_len,
-                  uint8_t *nodes6_return, unsigned *nodes6_len,
-                  std::vector<std::shared_ptr<Value>>& values_return,
-                  int *want_return, uint16_t& error_code);
-
-    void rotateSecrets();
-
-    Blob makeToken(const sockaddr *sa, bool old) const;
-    bool tokenMatch(const Blob& token, const sockaddr *sa) const;
-
-    // Storage
-    Storage* findStorage(const InfoHash& id);
-    const Storage* findStorage(const InfoHash& id) const {
-        return const_cast<Dht*>(this)->findStorage(id);
-    }
-
-    ValueStorage* storageStore(const InfoHash& id, const std::shared_ptr<Value>& value);
-    void expireStorage();
-
-    // Buckets
-    Bucket* findBucket(const InfoHash& id, sa_family_t af) {
-        RoutingTable::iterator b;
-        switch (af) {
-        case AF_INET:
-            b = buckets.findBucket(id);
-            return b == buckets.end() ? nullptr : &(*b);
-        case AF_INET6:
-            b = buckets6.findBucket(id);
-            return b == buckets6.end() ? nullptr : &(*b);
-        default:
-            return nullptr;
-        }
-    }
-    const Bucket* findBucket(const InfoHash& id, sa_family_t af) const {
-        return const_cast<Dht*>(this)->findBucket(id, af);
-    }
-
-    void expireBuckets(RoutingTable&);
-    int sendCachedPing(Bucket& b);
-    bool bucketMaintenance(RoutingTable&);
-    static unsigned insertClosestNode(uint8_t *nodes, unsigned numnodes, const InfoHash& id, const Node& n);
-    unsigned bufferClosestNodes(uint8_t *nodes, unsigned numnodes, const InfoHash& id, const Bucket& b) const;
-    void dumpBucket(const Bucket& b, std::ostream& out) const;
-
-    // Nodes
-    Node* newNode(const InfoHash& id, const sockaddr*, socklen_t, int confirm);
-    Node* findNode(const InfoHash& id, sa_family_t af);
-    const Node* findNode(const InfoHash& id, sa_family_t af) const;
-
-    void pinged(Node& n, Bucket *b = nullptr);
-
-    void blacklistNode(const InfoHash* id, const sockaddr*, socklen_t);
-    bool isNodeBlacklisted(const sockaddr*, socklen_t) const;
-    static bool isMartian(const sockaddr*, socklen_t);
-
-    // Searches
-
-    /**
-     * Low-level method that will perform a search on the DHT for the
-     * specified infohash (id), using the specified IP version (IPv4 or IPv6).
-     * The values can be filtered by an arbitrary provided filter.
-     */
-    Search* search(const InfoHash& id, sa_family_t af, GetCallback = nullptr, DoneCallback = nullptr, Value::Filter = Value::AllFilter());
-    void announce(const InfoHash& id, sa_family_t af, const std::shared_ptr<Value>& value, DoneCallback callback);
-
-    std::list<Search>::iterator newSearch();
-    void bootstrapSearch(Search& sr);
-    Search *findSearch(unsigned short tid, sa_family_t af);
-    void expireSearches();
-    bool searchSendGetValues(Search& sr, SearchNode *n = nullptr);
-    void searchStep(Search& sr);
-    void dumpSearch(const Search& sr, std::ostream& out) const;
-
-    bool rateLimit();
-    bool neighbourhoodMaintenance(RoutingTable&);
-
-    static void *dht_memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen);
-
-};
-
-}
diff --git a/daemon/src/ringdht/dhtcpp/dhtrunner.cpp b/daemon/src/ringdht/dhtcpp/dhtrunner.cpp
deleted file mode 100644
index d4293c99e4..0000000000
--- a/daemon/src/ringdht/dhtcpp/dhtrunner.cpp
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- *  Copyright (C) 2014 Savoir-Faire Linux Inc.
- *  Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  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.
- *
- *  Additional permission under GNU GPL version 3 section 7:
- *
- *  If you modify this program, or any covered work, by linking or
- *  combining it with the OpenSSL project's OpenSSL library (or a
- *  modified version of that library), containing parts covered by the
- *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
- *  grants you additional permission to convey the resulting work.
- *  Corresponding Source for a non-source form of such a combination
- *  shall include the source code for the parts of OpenSSL used as well
- *  as that of the covered work.
- */
-
-#include "dhtrunner.h"
-
-namespace dht {
-
-void
-DhtRunner::run(in_port_t port, const crypto::Identity identity, bool threaded, StatusCallback cb)
-{
-    if (running)
-        return;
-    if (rcv_thread.joinable())
-        rcv_thread.join();
-    statusCb = cb;
-    running = true;
-    doRun(port, identity);
-    if (!threaded)
-        return;
-    dht_thread = std::thread([this]() {
-        while (running) {
-            std::unique_lock<std::mutex> lk(dht_mtx);
-            loop_();
-            cv.wait_for(lk, std::chrono::seconds( tosleep ), [this]() {
-                if (!running) return true;
-                {
-                    std::unique_lock<std::mutex> lck(sock_mtx);
-                    if (!rcv.empty()) return true;
-                }
-                {
-                    std::unique_lock<std::mutex> lck(storage_mtx);
-                    if (!dht_gets.empty() || !dht_puts.empty() || !bootstrap_nodes.empty())
-                        return true;
-                }
-                return false;
-            });
-        }
-    });
-}
-
-void
-DhtRunner::join()
-{
-    running = false;
-    cv.notify_all();
-    if (dht_thread.joinable())
-        dht_thread.join();
-    if (rcv_thread.joinable())
-        rcv_thread.join();
-    {
-        std::unique_lock<std::mutex> lck(dht_mtx);
-        dht.reset();
-        status4 = Dht::Status::Disconnected;
-        status6 = Dht::Status::Disconnected;
-    }
-}
-
-void
-DhtRunner::loop_()
-{
-    if (!dht) return;
-    time_t tosl;
-    {
-        std::unique_lock<std::mutex> lck(sock_mtx);
-        if (!dht) return;
-        if (rcv.size()) {
-            for (const auto& pck : rcv) {
-                auto& buf = pck.first;
-                auto& from = pck.second;
-                dht->periodic(buf.data(), buf.size()-1, (sockaddr*)&from, from.ss_family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6), &tosl);
-            }
-            rcv.clear();
-        } else {
-            dht->periodic(nullptr, 0, nullptr, 0, &tosl);
-        }
-    }
-    tosleep = tosl;
-    {
-        std::unique_lock<std::mutex> lck(storage_mtx);
-
-        for (auto& get : dht_gets) {
-            std::cout << "Processing get (" <<  std::get<0>(get) << ")" << std::endl;
-            dht->get(std::get<0>(get), std::get<1>(get), std::get<2>(get), std::move(std::get<3>(get)));
-        }
-        dht_gets.clear();
-
-        for (auto& put : dht_eputs) {
-            auto& id = std::get<0>(put);
-            auto& val = std::get<2>(put);
-            std::cout << "Processing encrypted put at " << id << " for " << std::get<1>(put) << " -> " << val << std::endl;
-            dht->putEncrypted(id, std::get<1>(put), std::move(val), std::get<3>(put));
-        }
-        dht_eputs.clear();
-
-        for (auto& put : dht_puts) {
-            auto& id = std::get<0>(put);
-            auto& val = std::get<1>(put);
-            std::cout << "Processing put " << id << " -> " << val << std::endl;
-            dht->put(id, std::move(val), std::get<2>(put));
-        }
-        dht_puts.clear();
-
-        for (auto& put : dht_sputs) {
-            auto& id = std::get<0>(put);
-            auto& val = std::get<1>(put);
-            std::cout << "Processing signed put " << id << " -> " << val << std::endl;
-            dht->putSigned(id, std::move(val), std::get<2>(put));
-        }
-        dht_sputs.clear();
-
-        for (auto& node : bootstrap_nodes)
-            dht->insertNode(node);
-        bootstrap_nodes.clear();
-
-        for (auto& node : bootstrap_ips) {
-            dht->pingNode((sockaddr*)&node, sizeof(node));
-            //std::this_thread::sleep_for( std::chrono::microseconds(/*rand_delay()*/ 10) );
-        }
-        bootstrap_ips.clear();
-    }
-
-    if (statusCb) {
-        Dht::Status nstatus4 = dht->getStatus(AF_INET);
-        Dht::Status nstatus6 = dht->getStatus(AF_INET6);
-        if (nstatus4 != status4 || nstatus6 != status6) {
-            status4 = nstatus4;
-            status6 = nstatus6;
-            statusCb(status4, status6);
-        }
-    }
-}
-
-void
-DhtRunner::doRun(in_port_t port, const crypto::Identity identity)
-{
-    dht.reset();
-
-    int s = socket(PF_INET, SOCK_DGRAM, 0);
-    int s6 = socket(PF_INET6, SOCK_DGRAM, 0);
-    if(s >= 0) {
-        sockaddr_in sin {
-            .sin_family = AF_INET,
-            .sin_port = htons(port)
-        };
-        int rc = bind(s, (sockaddr*)&sin, sizeof(sin));
-        if(rc < 0)
-            throw DhtException("Can't bind IPv4 socket");
-    }
-    if(s6 >= 0) {
-        int val = 1;
-        int rc = setsockopt(s6, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&val, sizeof(val));
-        if(rc < 0) {
-            throw DhtException("setsockopt(IPV6_V6ONLY)");
-        }
-
-        /* BEP-32 mandates that we should bind this socket to one of our
-           global IPv6 addresses.  In this simple example, this only
-           happens if the user used the -b flag. */
-        sockaddr_in6 sin6 {
-            .sin6_family = AF_INET6,
-            .sin6_port = htons(port)
-        };
-        rc = bind(s6, (sockaddr*)&sin6, sizeof(sin6));
-        if(rc < 0)
-            throw DhtException("Can't bind IPv6 socket");
-    }
-
-    dht = std::unique_ptr<SecureDht>(new SecureDht {s, s6, identity});
-
-    rcv_thread = std::thread([this,s,s6]() {
-        std::mt19937 engine(std::random_device{}());
-        auto rand_delay = std::bind(std::uniform_int_distribution<uint32_t>(0, 1000000), engine);
-        try {
-            while (true) {
-                uint8_t buf[4096 * 64];
-                sockaddr_storage from;
-                socklen_t fromlen;
-
-                struct timeval tv;
-                fd_set readfds;
-                tv.tv_sec = tosleep / 5;
-                tv.tv_usec = rand_delay();
-                //std::cout << "Dht::rcv_thread loop " << tv.tv_sec << "." << tv.tv_usec << std::endl;
-
-                FD_ZERO(&readfds);
-                if(s >= 0)
-                    FD_SET(s, &readfds);
-                if(s6 >= 0)
-                    FD_SET(s6, &readfds);
-                int rc = select(s > s6 ? s + 1 : s6 + 1, &readfds, NULL, NULL, &tv);
-                if(rc < 0) {
-                    if(errno != EINTR) {
-                        perror("select");
-                        std::this_thread::sleep_for( std::chrono::seconds(1) );
-                    }
-                }
-
-                if(!running)
-                    break;
-
-                if(rc > 0) {
-                    fromlen = sizeof(from);
-                    if(s >= 0 && FD_ISSET(s, &readfds))
-                        rc = recvfrom(s, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&from, &fromlen);
-                    else if(s6 >= 0 && FD_ISSET(s6, &readfds))
-                        rc = recvfrom(s6, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&from, &fromlen);
-                    else
-                        break;
-                    if (rc > 0) {
-                        buf[rc] = 0;
-                        {
-                            std::unique_lock<std::mutex> lck(sock_mtx);
-                            rcv.emplace_back(Blob {buf, buf+rc+1}, from);
-                        }
-                        cv.notify_all();
-                    }
-                }
-            }
-        } catch (const std::exception& e) {
-            std::cerr << "Error int DHT networking thread: " << e.what() << std::endl;
-        }
-        if (s >= 0)
-            close(s);
-        if (s6 >= 0)
-            close(s6);
-    });
-}
-
-void
-DhtRunner::get(InfoHash hash, Dht::GetCallback vcb, Dht::DoneCallback dcb, Value::Filter f)
-{
-    std::unique_lock<std::mutex> lck(storage_mtx);
-    dht_gets.emplace_back(hash, vcb, dcb, f);
-    cv.notify_all();
-}
-
-void
-DhtRunner::get(const std::string& key, Dht::GetCallback vcb, Dht::DoneCallback dcb, Value::Filter f)
-{
-    get(InfoHash::get(key), vcb, dcb, f);
-}
-
-void
-DhtRunner::put(InfoHash hash, Value&& value, Dht::DoneCallback cb)
-{
-    std::unique_lock<std::mutex> lck(storage_mtx);
-    dht_puts.emplace_back(hash, std::move(value), cb);
-    cv.notify_all();
-}
-
-void
-DhtRunner::put(const std::string& key, Value&& value, Dht::DoneCallback cb)
-{
-    put(InfoHash::get(key), std::forward<Value>(value), cb);
-}
-
-void
-DhtRunner::putSigned(InfoHash hash, Value&& value, Dht::DoneCallback cb)
-{
-    std::unique_lock<std::mutex> lck(storage_mtx);
-    dht_sputs.emplace_back(hash, std::move(value), cb);
-    cv.notify_all();
-}
-
-void
-DhtRunner::putSigned(const std::string& key, Value&& value, Dht::DoneCallback cb)
-{
-    putSigned(InfoHash::get(key), std::forward<Value>(value), cb);
-}
-
-void
-DhtRunner::putEncrypted(InfoHash hash, InfoHash to, Value&& value, Dht::DoneCallback cb)
-{
-    std::unique_lock<std::mutex> lck(storage_mtx);
-    dht_eputs.emplace_back(hash, to, std::move(value), cb);
-    cv.notify_all();
-}
-
-void
-DhtRunner::putEncrypted(const std::string& key, InfoHash to, Value&& value, Dht::DoneCallback cb)
-{
-    putEncrypted(key, to, std::forward<Value>(value), cb);
-}
-
-void
-DhtRunner::bootstrap(const std::vector<sockaddr_storage>& nodes)
-{
-    std::unique_lock<std::mutex> lck(storage_mtx);
-    bootstrap_ips.insert(bootstrap_ips.end(), nodes.begin(), nodes.end());
-    cv.notify_all();
-}
-
-void
-DhtRunner::bootstrap(const std::vector<Dht::NodeExport>& nodes)
-{
-    std::unique_lock<std::mutex> lck(storage_mtx);
-    bootstrap_nodes.insert(bootstrap_nodes.end(), nodes.begin(), nodes.end());
-    cv.notify_all();
-}
-
-
-}
diff --git a/daemon/src/ringdht/dhtcpp/dhtrunner.h b/daemon/src/ringdht/dhtcpp/dhtrunner.h
deleted file mode 100644
index 09b883edc9..0000000000
--- a/daemon/src/ringdht/dhtcpp/dhtrunner.h
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- *  Copyright (C) 2014 Savoir-Faire Linux Inc.
- *  Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  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.
- *
- *  Additional permission under GNU GPL version 3 section 7:
- *
- *  If you modify this program, or any covered work, by linking or
- *  combining it with the OpenSSL project's OpenSSL library (or a
- *  modified version of that library), containing parts covered by the
- *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
- *  grants you additional permission to convey the resulting work.
- *  Corresponding Source for a non-source form of such a combination
- *  shall include the source code for the parts of OpenSSL used as well
- *  as that of the covered work.
- */
-
-#pragma once
-
-#include "securedht.h"
-
-#include <thread>
-#include <random>
-#include <mutex>
-#include <atomic>
-#include <condition_variable>
-#include <exception>
-
-#include <unistd.h> // close(fd)
-
-namespace dht {
-
-/**
- * Provides a thread-safe interface to run the (secure) DHT.
- * The class will open sockets on the provided port and will
- * either wait for (expectedly frequent) calls to loop() or start an internal
- * thread that will update the DHT when appropriate.
- */
-class DhtRunner {
-
-public:
-    typedef std::function<void(Dht::Status, Dht::Status)> StatusCallback;
-
-    DhtRunner() {}
-    virtual ~DhtRunner() {
-        join();
-    }
-
-    void get(InfoHash hash, Dht::GetCallback vcb, Dht::DoneCallback dcb=nullptr, Value::Filter f = Value::AllFilter());
-    void get(const std::string& key, Dht::GetCallback vcb, Dht::DoneCallback dcb=nullptr, Value::Filter f = Value::AllFilter());
-
-    void put(InfoHash hash, Value&& value, Dht::DoneCallback cb=nullptr);
-    void put(const std::string& key, Value&& value, Dht::DoneCallback cb=nullptr);
-
-    void putSigned(InfoHash hash, Value&& value, Dht::DoneCallback cb=nullptr);
-    void putSigned(const std::string& key, Value&& value, Dht::DoneCallback cb=nullptr);
-
-    void putEncrypted(InfoHash hash, InfoHash to, Value&& value, Dht::DoneCallback cb=nullptr);
-    void putEncrypted(const std::string& key, InfoHash to, Value&& value, Dht::DoneCallback cb=nullptr);
-
-    void bootstrap(const std::vector<sockaddr_storage>& nodes);
-    void bootstrap(const std::vector<Dht::NodeExport>& nodes);
-
-    void dumpTables() const
-    {
-        std::unique_lock<std::mutex> lck(dht_mtx);
-        dht->dumpTables();
-    }
-
-    InfoHash getId() const {
-        if (!dht)
-            return {};
-        return dht->getId();
-    }
-
-    std::vector<Dht::NodeExport> exportNodes() const {
-        std::unique_lock<std::mutex> lck(dht_mtx);
-        if (!dht)
-            return {};
-        return dht->exportNodes();
-    }
-
-    std::vector<Dht::ValuesExport> exportValues() const {
-        std::unique_lock<std::mutex> lck(dht_mtx);
-        if (!dht)
-            return {};
-        return dht->exportValues();
-    }
-
-    void setLoggers(LogMethod&& error = NOLOG, LogMethod&& warn = NOLOG, LogMethod&& debug = NOLOG) {
-        std::unique_lock<std::mutex> lck(dht_mtx);
-        dht->setLoggers(std::forward<LogMethod>(error), std::forward<LogMethod>(warn), std::forward<LogMethod>(debug));
-    }
-
-    void registerType(const ValueType& type) {
-        std::unique_lock<std::mutex> lck(dht_mtx);
-        dht->registerType(type);
-    }
-
-    void importValues(const std::vector<Dht::ValuesExport>& values) {
-        std::unique_lock<std::mutex> lck(dht_mtx);
-        dht->importValues(values);
-    }
-
-    bool isRunning() const {
-        return running;
-    }
-
-    /**
-     * If threaded is false, loop() must be called periodically.
-     */
-    void run(in_port_t port, const crypto::Identity identity, bool threaded = false, StatusCallback cb = nullptr);
-
-    void loop() {
-        std::unique_lock<std::mutex> lck(dht_mtx);
-        loop_();
-    }
-
-    void join();
-
-private:
-
-    void doRun(in_port_t port, const crypto::Identity identity);
-    void loop_();
-
-    std::unique_ptr<SecureDht> dht {};
-    mutable std::mutex dht_mtx {};
-    std::thread dht_thread {};
-    std::condition_variable cv {};
-
-    std::thread rcv_thread {};
-    std::mutex sock_mtx {};
-    std::vector<std::pair<Blob, sockaddr_storage>> rcv {};
-    std::atomic<time_t> tosleep {0};
-
-    // IPC temporary storage
-    std::vector<std::tuple<InfoHash, Dht::GetCallback, Dht::DoneCallback, Value::Filter>> dht_gets {};
-    std::vector<std::tuple<InfoHash, Value, Dht::DoneCallback>> dht_puts {};
-    std::vector<std::tuple<InfoHash, Value, Dht::DoneCallback>> dht_sputs {};
-    std::vector<std::tuple<InfoHash, InfoHash, Value, Dht::DoneCallback>> dht_eputs {};
-    std::vector<sockaddr_storage> bootstrap_ips {};
-    std::vector<Dht::NodeExport> bootstrap_nodes {};
-    std::mutex storage_mtx {};
-
-    std::atomic<bool> running {false};
-
-    Dht::Status status4 {Dht::Status::Disconnected},
-                status6 {Dht::Status::Disconnected};
-    StatusCallback statusCb {nullptr};
-};
-
-}
diff --git a/daemon/src/ringdht/dhtcpp/infohash.cpp b/daemon/src/ringdht/dhtcpp/infohash.cpp
deleted file mode 100644
index aabfdc76b6..0000000000
--- a/daemon/src/ringdht/dhtcpp/infohash.cpp
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- *  Copyright (C) 2014 Savoir-Faire Linux Inc.
- *  Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  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.
- *
- *  Additional permission under GNU GPL version 3 section 7:
- *
- *  If you modify this program, or any covered work, by linking or
- *  combining it with the OpenSSL project's OpenSSL library (or a
- *  modified version of that library), containing parts covered by the
- *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
- *  grants you additional permission to convey the resulting work.
- *  Corresponding Source for a non-source form of such a combination
- *  shall include the source code for the parts of OpenSSL used as well
- *  as that of the covered work.
- */
-
-#include "infohash.h"
-
-extern "C" {
-#include <gnutls/gnutls.h>
-}
-
-#include <sstream>
-
-namespace dht {
-
-InfoHash::InfoHash(const std::string& hex) {
-    unsigned in = std::min((size_t)HASH_LEN, hex.size()/2);
-    for (unsigned i = 0; i < in; i++) {
-        sscanf(hex.data() + 2*i, "%02x", (unsigned*)(&((*this)[i])));
-    }
-}
-
-InfoHash
-InfoHash::get(const uint8_t* data, size_t data_len)
-{
-    InfoHash h;
-    size_t s = h.size();
-    const gnutls_datum_t gnudata = {(uint8_t*)data, (unsigned)data_len};
-    const gnutls_digest_algorithm_t algo =  (HASH_LEN == 64) ? GNUTLS_DIG_SHA512 : (
-                                            (HASH_LEN == 32) ? GNUTLS_DIG_SHA256 : (
-                                            (HASH_LEN == 20) ? GNUTLS_DIG_SHA1   :
-                                            GNUTLS_DIG_NULL ));
-    static_assert(algo != GNUTLS_DIG_NULL, "Can't find hash function to use.");
-    int rc = gnutls_fingerprint(algo, &gnudata, h.data(), &s);
-    if (rc == 0 && s == HASH_LEN)
-        return h;
-    throw std::string("Error while hashing");
-}
-
-std::string
-InfoHash::toString() const
-{
-    std::stringstream ss;
-    ss << *this;
-    return ss.str();
-}
-
-std::ostream& operator<< (std::ostream& s, const InfoHash& h)
-{
-    s << std::hex;
-    for (unsigned i=0; i<HASH_LEN; i++)
-        s << std::setfill('0') << std::setw(2) << (unsigned)h[i];
-    s << std::dec;
-    return s;
-}
-
-}
diff --git a/daemon/src/ringdht/dhtcpp/infohash.h b/daemon/src/ringdht/dhtcpp/infohash.h
deleted file mode 100644
index 31b82128e1..0000000000
--- a/daemon/src/ringdht/dhtcpp/infohash.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- *  Copyright (C) 2014 Savoir-Faire Linux Inc.
- *  Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  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.
- *
- *  Additional permission under GNU GPL version 3 section 7:
- *
- *  If you modify this program, or any covered work, by linking or
- *  combining it with the OpenSSL project's OpenSSL library (or a
- *  modified version of that library), containing parts covered by the
- *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
- *  grants you additional permission to convey the resulting work.
- *  Corresponding Source for a non-source form of such a combination
- *  shall include the source code for the parts of OpenSSL used as well
- *  as that of the covered work.
- */
-
-#pragma once
-
-#include <iostream>
-#include <iomanip>
-#include <array>
-#include <vector>
-
-#include <cstring>
-
-// bytes
-#define HASH_LEN 20
-
-namespace dht {
-
-class DhtException : public std::runtime_error {
-    public:
-        DhtException(const std::string &str = "") :
-            std::runtime_error("DhtException occured: " + str) {}
-};
-
-
-/**
- * Represents an InfoHash.
- * An InfoHash is a byte array of HASH_LEN bytes.
- * InfoHashes identify nodes and values in the Dht.
- */
-class InfoHash final : public std::array<uint8_t, HASH_LEN> {
-public:
-    constexpr InfoHash() : std::array<uint8_t, HASH_LEN>() {}
-    constexpr InfoHash(const std::array<uint8_t, HASH_LEN>& h) : std::array<uint8_t, HASH_LEN>(h) {}
-    InfoHash(const uint8_t* h, size_t h_len=HASH_LEN) : std::array<uint8_t, HASH_LEN>() {
-        memcpy(data(), h, std::min((size_t)HASH_LEN, h_len));
-    }
-
-    /**
-     * Constructor from an hexadecimal string (without "0x").
-     * hex must be at least 2.HASH_LEN characters long.
-     * If too long, only the first 2.HASH_LEN characters are read.
-     */
-    InfoHash(const std::string& hex);
-
-    /**
-     * Find the lowest 1 bit in an id.
-     * Result will allways be lower than 8*HASH_LEN
-     */
-    inline unsigned lowbit() const {
-        int i, j;
-        for(i = HASH_LEN-1; i >= 0; i--)
-            if((*this)[i] != 0)
-                break;
-        if(i < 0)
-            return -1;
-        for(j = 7; j >= 0; j--)
-            if(((*this)[i] & (0x80 >> j)) != 0)
-                break;
-        return 8 * i + j;
-    }
-
-    /**
-     * Forget about the ``XOR-metric''.  An id is just a path from the
-     * root of the tree, so bits are numbered from the start.
-     */
-    static inline int cmp(const InfoHash& __restrict__ id1, const InfoHash& __restrict__ id2) {
-        return std::memcmp(id1.data(), id2.data(), HASH_LEN);
-    }
-
-    /** Find how many bits two ids have in common. */
-    static inline unsigned
-    commonBits(const InfoHash& id1, const InfoHash& id2)
-    {
-        unsigned i, j;
-        uint8_t x;
-        for(i = 0; i < HASH_LEN; i++) {
-            if(id1[i] != id2[i])
-                break;
-        }
-
-        if(i == HASH_LEN)
-            return 8*HASH_LEN;
-
-        x = id1[i] ^ id2[i];
-
-        j = 0;
-        while((x & 0x80) == 0) {
-            x <<= 1;
-            j++;
-        }
-
-        return 8 * i + j;
-    }
-
-    /** Determine whether id1 or id2 is closer to this */
-    int
-    xorCmp(const InfoHash& id1, const InfoHash& id2) const
-    {
-        unsigned i;
-        for(i = 0; i < HASH_LEN; i++) {
-            uint8_t xor1, xor2;
-            if(id1[i] == id2[i])
-                continue;
-            xor1 = id1[i] ^ (*this)[i];
-            xor2 = id2[i] ^ (*this)[i];
-            if(xor1 < xor2)
-                return -1;
-            else
-                return 1;
-        }
-        return 0;
-    }
-
-    static inline InfoHash get(const std::string& data) {
-        return get((const uint8_t*)data.data(), data.size());
-    }
-
-    static inline InfoHash get(const std::vector<uint8_t>& data) {
-        return get(data.data(), data.size());
-    }
-
-    /**
-     * Computes the hash from a given data buffer of size data_len.
-     */
-    static InfoHash get(const uint8_t* data, size_t data_len);
-
-    friend std::ostream& operator<< (std::ostream& s, const InfoHash& h);
-
-    std::string toString() const;
-};
-
-}
diff --git a/daemon/src/ringdht/dhtcpp/securedht.cpp b/daemon/src/ringdht/dhtcpp/securedht.cpp
deleted file mode 100644
index d6e5745ad7..0000000000
--- a/daemon/src/ringdht/dhtcpp/securedht.cpp
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- *  Copyright (C) 2014 Savoir-Faire Linux Inc.
- *  Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  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.
- *
- *  Additional permission under GNU GPL version 3 section 7:
- *
- *  If you modify this program, or any covered work, by linking or
- *  combining it with the OpenSSL project's OpenSSL library (or a
- *  modified version of that library), containing parts covered by the
- *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
- *  grants you additional permission to convey the resulting work.
- *  Corresponding Source for a non-source form of such a combination
- *  shall include the source code for the parts of OpenSSL used as well
- *  as that of the covered work.
- */
-
-#include "securedht.h"
-
-extern "C" {
-#include <gnutls/gnutls.h>
-#include <gnutls/abstract.h>
-#include <gnutls/x509.h>
-}
-
-#include <random>
-
-namespace dht {
-
-SecureDht::SecureDht(int s, int s6, crypto::Identity id)
-: Dht(s, s6, InfoHash::get("node:"+id.second->getPublicKey().getId().toString())), key_(id.first), certificate_(id.second)
-{
-    if (s < 0 && s6 < 0)
-        return;
-
-    int rc = gnutls_global_init();
-    if (rc != GNUTLS_E_SUCCESS)
-        throw DhtException(std::string("Error initializing GnuTLS: ")+gnutls_strerror(rc));
-
-    auto certId = certificate_->getPublicKey().getId();
-    if (certId != key_->getPublicKey().getId())
-        throw DhtException("SecureDht: provided certificate doesn't match private key.");
-
-    registerType(ValueType::USER_DATA);
-    registerInsecureType(ServiceAnnouncement::TYPE);
-    registerInsecureType(CERTIFICATE_TYPE);
-
-    Dht::put(certId, Value {
-        CERTIFICATE_TYPE,
-        *certificate_
-    }, [this](bool ok) {
-        if (ok)
-            DHT_DEBUG("SecureDht: public key announced successfully");
-        else
-            DHT_ERROR("SecureDht: error while announcing public key!");
-    });
-}
-
-SecureDht::~SecureDht()
-{
-    gnutls_global_deinit();
-}
-
-ValueType
-SecureDht::secureType(ValueType&& type)
-{
-    type.storePolicy = [this,type](InfoHash id, std::shared_ptr<Value>& v, InfoHash nid, const sockaddr* a, socklen_t al) {
-        if (v->isSigned()) {
-            if (!v->owner.checkSignature(v->getToSign(), v->signature)) {
-                DHT_WARN("Signature verification failed");
-                return false;
-            }
-            else
-                DHT_WARN("Signature verification succeded");
-        }
-        return type.storePolicy(id, v, nid, a, al);
-    };
-    type.editPolicy = [this,type](InfoHash id, const std::shared_ptr<Value>& o, std::shared_ptr<Value>& n, InfoHash nid, const sockaddr* a, socklen_t al) {
-        if (!o->isSigned())
-            return type.editPolicy(id, o, n, nid, a, al);
-        if (o->owner != n->owner) {
-            DHT_WARN("Edition forbidden: owner changed.");
-            return false;
-        }
-        if (!o->owner.checkSignature(n->getToSign(), n->signature)) {
-            DHT_WARN("Edition forbidden: signature verification failed.");
-            return false;
-        }
-        DHT_WARN("Edition old seq: %d, new seq: %d.", o->seq, n->seq);
-        if (o->seq == n->seq) {
-            // If the data is exactly the same,
-            // it can be reannounced, possibly by someone else.
-            if (o->getToSign() != n->getToSign())
-                return false;
-        }
-        else if (n->seq < o->seq)
-            return false;
-        return true;
-    };
-    return type;
-}
-
-const std::shared_ptr<crypto::Certificate>
-SecureDht::getCertificate(const InfoHash& node) const
-{
-    if (node == getId())
-        return certificate_;
-    auto it = nodesCertificates_.find(node);
-    if (it == nodesCertificates_.end())
-        return nullptr;
-    else
-        return it->second;
-}
-
-const std::shared_ptr<crypto::Certificate>
-SecureDht::registerCertificate(const InfoHash& node, const Blob& data)
-{
-    std::shared_ptr<crypto::Certificate> crt;
-    try {
-        crt = std::make_shared<crypto::Certificate>(data);
-    } catch (const std::exception& e) {
-        return nullptr;
-    }
-    InfoHash h = crt->getPublicKey().getId();
-    if (node == h) {
-        DHT_DEBUG("Registering public key for %s", h.toString().c_str());
-        nodesCertificates_[h] = crt;
-    } else {
-        DHT_DEBUG("Certificate %s for node %s does not match node id !", h.toString().c_str(), node.toString().c_str());
-        return nullptr;
-    }
-    auto it = nodesCertificates_.find(h);
-    if (it == nodesCertificates_.end()) {
-        return nullptr;
-    }
-    return it->second;
-}
-
-void
-SecureDht::findCertificate(const InfoHash& node, std::function<void(const std::shared_ptr<crypto::Certificate>)> cb)
-{
-    std::shared_ptr<crypto::Certificate> b = getCertificate(node);
-    if (b && *b) {
-        std::cout << "Using public key from cache for " << node << std::endl;
-        cb(b);
-        return;
-    }
-    auto found = std::make_shared<bool>(false);
-    Dht::get(node, [cb,node,found,this](const std::vector<std::shared_ptr<Value>>& vals) {
-        if (*found)
-            return false;
-        for (const auto& v : vals) {
-            if (auto cert = registerCertificate(node, v->data)) {
-                *found = true;
-                std::cout << "Found public key for " << node << std::endl;
-                cb(cert);
-                return false;
-            }
-        }
-        return true;
-    }, [cb,found](bool) {
-        if (!*found)
-            cb(nullptr);
-    }, Value::TypeFilter(CERTIFICATE_TYPE));
-}
-
-
-void
-SecureDht::get(const InfoHash& id, GetCallback cb, DoneCallback donecb, Value::Filter filter)
-{
-    Dht::get(id,
-        [=](const std::vector<std::shared_ptr<Value>>& values) {
-            std::vector<std::shared_ptr<Value>> tmpvals {};
-            for (const auto& v : values) {
-                if (v->isEncrypted()) {
-                    try {
-                        Value decrypted_val = std::move(decrypt(*v));
-                        if (decrypted_val.recipient == getId()) {
-                            auto dv = std::make_shared<Value>(std::move(decrypted_val));
-                            if (dv->owner.checkSignature(dv->getToSign(), dv->signature))
-                                tmpvals.push_back(v);
-                            else
-                                DHT_WARN("Signature verification failed for %s", id.toString().c_str());
-                        }
-                    } catch (const std::exception& e) {
-                        DHT_WARN("Could not decrypt value %s at infohash %s", v->toString().c_str(), id.toString().c_str());
-                        continue;
-                    }
-                } else if (v->isSigned()) {
-                    if (v->owner.checkSignature(v->getToSign(), v->signature))
-                        tmpvals.push_back(v);
-                    else
-                        DHT_WARN("Signature verification failed for %s", id.toString().c_str());
-                } else {
-                    tmpvals.push_back(v);
-                }
-            }
-            if (not tmpvals.empty())
-                cb(tmpvals);
-            return true;
-        },
-        donecb,
-        filter);
-}
-
-void
-SecureDht::putSigned(const InfoHash& hash, Value&& val, DoneCallback callback)
-{
-    if (val.id == Value::INVALID_ID) {
-        auto id = getId();
-        static_assert(sizeof(Value::Id) <= sizeof(InfoHash), "Value::Id can't be larger than InfoHash");
-        val.id = *reinterpret_cast<Value::Id*>(id.data());
-    }
-    // TODO search the DHT instead of using the local value
-    auto p = getPut(hash, val.id);
-    if (p) {
-        DHT_WARN("Found previous value being announced.");
-        val.seq = p->seq + 1;
-    }
-    sign(val);
-    put(hash, std::move(val), callback);
-}
-
-void
-SecureDht::putEncrypted(const InfoHash& hash, const InfoHash& to, const std::shared_ptr<Value>& val, DoneCallback callback)
-{
-    findCertificate(to, [=](const std::shared_ptr<crypto::Certificate> crt) {
-        if(!crt || !*crt) {
-            if (callback)
-                callback(false);
-            return;
-        }
-        DHT_WARN("Encrypting data for PK: %s", crt->getPublicKey().getId().toString().c_str());
-        try {
-            put(hash, encrypt(*val, crt->getPublicKey()), callback);
-        } catch (const std::exception& e) {
-            DHT_WARN("Error putting encrypted data: %s", e.what());
-            if (callback)
-                callback(false);
-        }
-    });
-}
-
-void
-SecureDht::sign(Value& v) const
-{
-    if (v.flags.isEncrypted())
-        throw DhtException("Can't sign encrypted data.");
-    v.owner = key_->getPublicKey();
-    v.flags = Value::ValueFlags(true, false);
-    v.signature = key_->sign(v.getToSign());
-}
-
-Value
-SecureDht::encrypt(Value& v, const crypto::PublicKey& to) const
-{
-    if (v.flags.isEncrypted()) {
-        throw DhtException("Data is already encrypted.");
-    }
-    v.setRecipient(to.getId());
-    sign(v);
-    Value nv {v.id};
-    nv.setCypher(to.encrypt(v.getToEncrypt()));
-    return nv;
-}
-
-Value
-SecureDht::decrypt(const Value& v)
-{
-    if (not v.flags.isEncrypted())
-        throw DhtException("Data is not encrypted.");
-    auto decrypted = key_->decrypt(v.cypher);
-    Value ret {v.id};
-    auto pb = decrypted.cbegin(), pe = decrypted.cend();
-    ret.unpackBody(pb, pe);
-    return ret;
-}
-
-}
diff --git a/daemon/src/ringdht/dhtcpp/securedht.h b/daemon/src/ringdht/dhtcpp/securedht.h
deleted file mode 100644
index d44dee0607..0000000000
--- a/daemon/src/ringdht/dhtcpp/securedht.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- *  Copyright (C) 2014 Savoir-Faire Linux Inc.
- *  Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  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.
- *
- *  Additional permission under GNU GPL version 3 section 7:
- *
- *  If you modify this program, or any covered work, by linking or
- *  combining it with the OpenSSL project's OpenSSL library (or a
- *  modified version of that library), containing parts covered by the
- *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
- *  grants you additional permission to convey the resulting work.
- *  Corresponding Source for a non-source form of such a combination
- *  shall include the source code for the parts of OpenSSL used as well
- *  as that of the covered work.
- */
-
-#pragma once
-
-#include "dht.h"
-#include "crypto.h"
-
-#include <map>
-#include <vector>
-#include <memory>
-
-namespace dht {
-
-class SecureDht : private Dht {
-public:
-
-    typedef std::function<void(bool)> SignatureCheckCallback;
-
-    SecureDht() {}
-
-    /**
-     * s, s6: bound socket descriptors for IPv4 and IPv6, respectively.
-     *        For the Dht to be initialised, at least one of them must be >= 0.
-     * id:    the identity to use for the crypto layer and to compute
-     *        our own hash on the Dht.
-     */
-    SecureDht(int s, int s6, crypto::Identity id);
-
-    virtual ~SecureDht();
-
-    using Dht::periodic;
-    using Dht::pingNode;
-    using Dht::insertNode;
-    using Dht::exportNodes;
-    using Dht::exportValues;
-    using Dht::importValues;
-    using Dht::getStatus;
-    using Dht::dumpTables;
-    using Dht::put;
-    using Dht::setLoggers;
-
-    InfoHash getId() const {
-        return key_->getPublicKey().getId();
-    }
-
-    ValueType secureType(ValueType&& type);
-
-    ValueType secureType(const ValueType& type) {
-        ValueType tmp_type = type;
-        return secureType(std::move(tmp_type));
-    }
-
-    virtual void registerType(const ValueType& type) {
-        Dht::registerType(secureType(type));
-    }
-    virtual void registerType(ValueType&& type) {
-        Dht::registerType(secureType(std::forward<ValueType>(type)));
-    }
-    virtual void registerInsecureType(const ValueType& type) {
-        Dht::registerType(type);
-    }
-
-    /**
-     * "Secure" get(), that will check the signature of signed data, and decrypt encrypted data.
-     * If the signature can't be checked, or if the data can't be decrypted, it is not returned.
-     * Public, non-signed & non-encrypted data is retransmitted as-is.
-     */
-    void get(const InfoHash& id, GetCallback cb, DoneCallback donecb, Value::Filter = Value::AllFilter());
-
-    /**
-     * Will take ownership of the value, sign it using our private key and put it in the DHT.
-     */
-    void putSigned(const InfoHash& hash, Value&& data, DoneCallback callback);
-
-    /**
-     * Will sign the data using our private key, encrypt it using the recipient' public key,
-     * and put it in the DHT.
-     * The operation will be immediate if the recipient' public key is known (otherwise it will be retrived first).
-     */
-    void putEncrypted(const InfoHash& hash, const InfoHash& to, const std::shared_ptr<Value>& val, DoneCallback callback);
-    void putEncrypted(const InfoHash& hash, const InfoHash& to, Value&& v, DoneCallback callback) {
-        putEncrypted(hash, to, std::make_shared<Value>(std::move(v)), callback);
-    }
-
-    /**
-     * Take ownership of the value and sign it using our private key.
-     */
-    void sign(Value& v) const;
-
-    Value encrypt(Value& v, const crypto::PublicKey& to) const;
-
-    Value decrypt(const Value& v);
-
-    void findCertificate(const InfoHash& node, std::function<void(const std::shared_ptr<crypto::Certificate>)> cb);
-
-    const std::shared_ptr<crypto::Certificate> registerCertificate(const InfoHash& node, const Blob& publicKey);
-    const std::shared_ptr<crypto::Certificate> getCertificate(const InfoHash& node) const;
-
-private:
-    // prevent copy
-    SecureDht(const SecureDht&) = delete;
-    SecureDht& operator=(const SecureDht&) = delete;
-
-    std::shared_ptr<crypto::PrivateKey> key_ {};
-    std::shared_ptr<crypto::Certificate> certificate_ {};
-
-    std::map<InfoHash, std::shared_ptr<crypto::Certificate>> nodesCertificates_ {};
-};
-
-const ValueType CERTIFICATE_TYPE = {8, "Certificate", 60 * 60 * 24 * 7,
-    // A certificate can only be stored at it's public key ID.
-    [](InfoHash id, std::shared_ptr<Value>& v, InfoHash, const sockaddr*, socklen_t) {
-        try {
-            crypto::Certificate crt(v->data);
-            // TODO check certificate signature
-            return crt.getPublicKey().getId() == id;
-        } catch (const std::exception& e) {}
-        return false;
-    },
-    [](InfoHash id, const std::shared_ptr<Value>& o, std::shared_ptr<Value>& n, InfoHash, const sockaddr*, socklen_t) {
-        try {
-            crypto::Certificate crt_old(o->data);
-            crypto::Certificate crt_new(n->data);
-            return crt_old.getPublicKey().getId() == crt_new.getPublicKey().getId();
-        } catch (const std::exception& e) {}
-        return false;
-    }
-};
-
-}
diff --git a/daemon/src/ringdht/dhtcpp/serialize.h b/daemon/src/ringdht/dhtcpp/serialize.h
deleted file mode 100644
index 7b0fc75c85..0000000000
--- a/daemon/src/ringdht/dhtcpp/serialize.h
+++ /dev/null
@@ -1,316 +0,0 @@
-/**
- * Copyright (c) 2013, Simone Pellegrini All rights reserved.
- * Copyright (c) 2014 Savoir-Faire Linux. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  - Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- *
- *  - Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#pragma once
-
-#include <vector>
-#include <string>
-#include <tuple>
-#include <numeric>
-#include <limits>
-
-typedef std::vector<uint8_t> Blob;
-
-template <class T>
-inline void serialize(const T&, Blob&);
-
-namespace detail {
-
-    template<std::size_t> struct int_{};
-
-}
-
-// get_size
-template <class T>
-size_t get_size(const T& obj);
-
-namespace detail {
-
-    typedef uint16_t serialized_size_t;
-
-    template <class T>
-    struct get_size_helper;
-
-    template <class T>
-    struct get_size_helper<std::vector<T>> {
-        static size_t value(const std::vector<T>& obj) {
-        return std::accumulate(obj.begin(), obj.end(), sizeof(serialized_size_t),
-                [](const size_t& acc, const T& cur) { return acc+get_size(cur); });
-        }
-    };
-
-    template <>
-    struct get_size_helper<std::string> {
-        static size_t value(const std::string& obj) {
-            return sizeof(serialized_size_t) + obj.length()*sizeof(uint8_t);
-        }
-    };
-
-    template <class tuple_type>
-    inline size_t get_tuple_size(const tuple_type& obj, int_<0>) {
-        constexpr size_t idx = std::tuple_size<tuple_type>::value-1;
-        return get_size(std::get<idx>(obj));
-    }
-
-    template <class tuple_type, size_t pos>
-    inline size_t get_tuple_size(const tuple_type& obj, int_<pos>) {
-        constexpr size_t idx = std::tuple_size<tuple_type>::value-pos-1;
-        size_t acc = get_size(std::get<idx>(obj));
-
-        // recur
-        return acc+get_tuple_size(obj, int_<pos-1>());
-    }
-
-    template <class ...T>
-    struct get_size_helper<std::tuple<T...>> {
-        static size_t value(const std::tuple<T...>& obj) {
-            return get_tuple_size(obj, int_<sizeof...(T)-1>());
-        }
-    };
-
-    template <class T>
-    struct get_size_helper {
-        static size_t value(const T&) { return sizeof(T); }
-    };
-
-}
-
-template <class T>
-inline size_t get_size(const T& obj) {
-    return detail::get_size_helper<T>::value(obj);
-}
-
-namespace detail {
-
-    template <class T>
-    class serialize_helper;
-
-    template <class T>
-    void serializer(const T& obj, Blob::iterator&);
-
-    template <class tuple_type>
-    inline void serialize_tuple(const tuple_type& obj, Blob::iterator& res, int_<0>) {
-        constexpr size_t idx = std::tuple_size<tuple_type>::value-1;
-        serializer(std::get<idx>(obj), res);
-    }
-
-    template <class tuple_type, size_t pos>
-    inline void serialize_tuple(const tuple_type& obj, Blob::iterator& res, int_<pos>) {
-        constexpr size_t idx = std::tuple_size<tuple_type>::value-pos-1;
-        serializer(std::get<idx>(obj), res);
-
-        // recur
-        serialize_tuple(obj, res, int_<pos-1>());
-    }
-
-    template <class... T>
-    struct serialize_helper<std::tuple<T...>> {
-        static void apply(const std::tuple<T...>& obj, Blob::iterator& res) {
-            detail::serialize_tuple(obj, res, detail::int_<sizeof...(T)-1>());
-        }
-
-    };
-
-    template <>
-    struct serialize_helper<std::string> {
-        static void apply(const std::string& obj, Blob::iterator& res) {
-            // store the number of elements of this vector at the beginning
-            if (obj.length() > std::numeric_limits<serialized_size_t>::max())
-                throw std::length_error("string is too long");
-            serializer(static_cast<serialized_size_t>(obj.length()), res);
-            for(const auto& cur : obj) { serializer(cur, res); }
-        }
-
-    };
-
-    template <class T>
-    struct serialize_helper<std::vector<T>> {
-        static void apply(const std::vector<T>& obj, Blob::iterator& res) {
-            // store the number of elements of this vector at the beginning
-            if (obj.size() > std::numeric_limits<serialized_size_t>::max())
-                throw std::length_error("vector is too large");
-            serializer(static_cast<serialized_size_t>(obj.size()), res);
-            for(const auto& cur : obj) { serializer(cur, res); }
-        }
-
-    };
-
-    template <class T>
-    struct serialize_helper {
-        static void apply(const T& obj, Blob::iterator& res) {
-            const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&obj);
-            std::copy(ptr,ptr+sizeof(T),res);
-            res+=sizeof(T);
-        }
-
-    };
-
-    template <class T>
-    inline void serializer(const T& obj, Blob::iterator& res) {
-        serialize_helper<T>::apply(obj,res);
-    }
-
-} // end detail namespace
-
-template <class T>
-inline void serialize(const T& obj, Blob& res) {
-
-    size_t offset = res.size();
-    size_t size = get_size(obj);
-    res.resize(res.size() + size);
-
-    Blob::iterator it = res.begin()+offset;
-    detail::serializer(obj,it);
-    if (res.begin() + offset + size != it)
-        throw std::logic_error("error serializing object");
-}
-
-namespace detail {
-
-    template <class T>
-    struct deserialize_helper;
-
-    template <class T>
-    struct deserialize_helper {
-        static T apply(Blob::const_iterator& begin,
-                            Blob::const_iterator end) {
-            if (begin+sizeof(T)>end)
-                throw std::length_error("error deserializing object");
-            T val;
-            std::copy(begin, begin+sizeof(T), reinterpret_cast<uint8_t*>(&val));
-            begin+=sizeof(T);
-            return val;
-        }
-    };
-
-    template <class T>
-    struct deserialize_helper<std::vector<T>> {
-        static std::vector<T> apply(Blob::const_iterator& begin,
-                                             Blob::const_iterator end)
-        {
-            // retrieve the number of elements
-            serialized_size_t size = deserialize_helper<serialized_size_t>::apply(begin,end);
-
-            std::vector<T> vect(size);
-            for(size_t i=0; i<size; ++i) {
-                vect[i] = std::move(deserialize_helper<T>::apply(begin,end));
-            }
-            return vect;
-        }
-    };
-
-    template <>
-    struct deserialize_helper<std::string> {
-        static std::string apply(Blob::const_iterator& begin,
-                                         Blob::const_iterator end)
-        {
-            // retrieve the number of elements
-            serialized_size_t size = deserialize_helper<serialized_size_t>::apply(begin,end);
-
-            if (size == 0u) return std::string();
-            std::string str(size,'\0');
-            for(size_t i=0; i<size; ++i) {
-                str.at(i) = deserialize_helper<uint8_t>::apply(begin,end);
-            }
-            return str;
-        }
-    };
-
-    template <class tuple_type>
-    inline void deserialize_tuple(tuple_type& obj,
-                                  Blob::const_iterator& begin,
-                                  Blob::const_iterator end, int_<0>) {
-        constexpr size_t idx = std::tuple_size<tuple_type>::value-1;
-        typedef typename std::tuple_element<idx,tuple_type>::type T;
-
-        std::get<idx>(obj) = std::move(deserialize_helper<T>::apply(begin, end));
-    }
-
-    template <class tuple_type, size_t pos>
-    inline void deserialize_tuple(tuple_type& obj,
-                                  Blob::const_iterator& begin,
-                                  Blob::const_iterator end, int_<pos>) {
-        constexpr size_t idx = std::tuple_size<tuple_type>::value-pos-1;
-        typedef typename std::tuple_element<idx,tuple_type>::type T;
-        std::get<idx>(obj) = std::move(deserialize_helper<T>::apply(begin, end));
-
-        // recur
-        deserialize_tuple(obj, begin, end, int_<pos-1>());
-    }
-
-    template <class... T>
-    struct deserialize_helper<std::tuple<T...>> {
-        static std::tuple<T...> apply(Blob::const_iterator& begin,
-                                                Blob::const_iterator end)
-        {
-            //return std::make_tuple(deserialize(begin,begin+sizeof(T),T())...);
-            std::tuple<T...> ret;
-            deserialize_tuple(ret, begin, end, int_<sizeof...(T)-1>());
-            return ret;
-        }
-
-    };
-
-}
-
-template <class T>
-inline T deserialize(Blob::const_iterator& begin, const Blob::const_iterator& end) {
-    return detail::deserialize_helper<T>::apply(begin, end);
-}
-
-template <class T>
-inline T deserialize(const Blob& res) {
-    Blob::const_iterator it = res.begin();
-    return deserialize<T>(it, res.end());
-}
-
-namespace dht {
-
-    struct Serializable {
-    /**
-     * Append serialized object to res.
-     */
-    virtual void pack(Blob& res) const = 0;
-    Blob getPacked() const {
-        Blob ret;
-        pack(ret);
-        return ret;
-    }
-
-    /**
-     * Read serialized object from {begin, end}.
-     */
-    virtual void unpack(Blob::const_iterator& begin, Blob::const_iterator& end) = 0;
-    void unpackBlob(const Blob& data) {
-        auto cib = data.cbegin(), cie = data.cend();
-        unpack(cib, cie);
-    }
-
-    virtual ~Serializable() = default;
-};
-
-}
diff --git a/daemon/src/ringdht/dhtcpp/value.cpp b/daemon/src/ringdht/dhtcpp/value.cpp
deleted file mode 100644
index 0263d72cde..0000000000
--- a/daemon/src/ringdht/dhtcpp/value.cpp
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- *  Copyright (C) 2014 Savoir-Faire Linux Inc.
- *  Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  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.
- *
- *  Additional permission under GNU GPL version 3 section 7:
- *
- *  If you modify this program, or any covered work, by linking or
- *  combining it with the OpenSSL project's OpenSSL library (or a
- *  modified version of that library), containing parts covered by the
- *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
- *  grants you additional permission to convey the resulting work.
- *  Corresponding Source for a non-source form of such a combination
- *  shall include the source code for the parts of OpenSSL used as well
- *  as that of the covered work.
- */
-
-#include "value.h"
-#include "securedht.h" // print certificate ID
-
-namespace dht {
-
-std::ostream& operator<< (std::ostream& s, const Value& v)
-{
-    s << "Value[id:" << std::hex << v.id << std::dec << " ";
-    if (v.flags.isSigned())
-        s << "signed (v" << v.seq << ") ";
-    if (v.flags.isEncrypted())
-        s << "encrypted ";
-    else {
-        if (v.type == ServiceAnnouncement::TYPE.id) {
-            s << ServiceAnnouncement(v.data);
-        } else if (v.type == CERTIFICATE_TYPE.id) {
-            s << "Certificate";
-            try {
-                InfoHash h = crypto::Certificate(v.data).getPublicKey().getId();
-                s << " with ID " << h;
-            } catch (const std::exception& e) {
-                s << " (invalid)";
-            }
-        } else {
-            s << "Data (type: " << v.type << " ): ";
-            s << std::hex;
-            for (size_t i=0; i<v.data.size(); i++)
-                s << std::setfill('0') << std::setw(2) << (unsigned)v.data[i];
-            s << std::dec;
-        }
-    }
-    s << "]";
-    return s;
-}
-
-const ValueType ValueType::USER_DATA = {0, "User Data"};
-
-bool
-ServiceAnnouncement::storePolicy(InfoHash, std::shared_ptr<Value>& v, InfoHash, const sockaddr* from, socklen_t fromlen)
-{
-    ServiceAnnouncement request {};
-    request.unpackBlob(v->data);
-    if (request.getPort() == 0)
-        return false;
-    ServiceAnnouncement sa_addr {from, fromlen};
-    sa_addr.setPort(request.getPort());
-    // argument v is modified (not the value).
-    v = std::make_shared<Value>(ServiceAnnouncement::TYPE, sa_addr, v->id);
-    return true;
-}
-
-const ValueType ServiceAnnouncement::TYPE = {1, "Service Announcement", 15 * 60, ServiceAnnouncement::storePolicy, ValueType::DEFAULT_EDIT_POLICY};
-
-void
-Value::packToSign(Blob& res) const
-{
-    res.push_back(flags.to_ulong());
-    if (flags.isEncrypted()) {
-        res.insert(res.end(), cypher.begin(), cypher.end());
-    } else {
-        if (flags.isSigned()) {
-            serialize<decltype(seq)>(seq, res);
-            owner.pack(res);
-            //res.insert(res.end(), owner.begin(), owner.end());
-            if (flags.haveRecipient())
-                res.insert(res.end(), recipient.begin(), recipient.end());
-        }
-        serialize<ValueType::Id>(type, res);
-        serialize<Blob>(data, res);
-    }
-}
-
-Blob
-Value::getToSign() const
-{
-    Blob ret;
-    packToSign(ret);
-    return ret;
-}
-
-/**
- * Pack part of the data to be encrypted
- */
-void
-Value::packToEncrypt(Blob& res) const
-{
-    packToSign(res);
-    if (!flags.isEncrypted() && flags.isSigned())
-        serialize<Blob>(signature, res);
-}
-
-Blob
-Value::getToEncrypt() const
-{
-    Blob ret;
-    packToEncrypt(ret);
-    return ret;
-}
-
-void
-Value::pack(Blob& res) const
-{
-    serialize<Id>(id, res);
-    packToEncrypt(res);
-}
-
-void
-Value::unpackBody(Blob::const_iterator& begin, Blob::const_iterator& end)
-{
-    // clear optional fields
-    owner = {};
-    recipient = {};
-    cypher.clear();
-    signature.clear();
-    data.clear();
-    type = 0;
-
-    flags = {deserialize<uint8_t>(begin, end)};
-    if (flags.isEncrypted()) {
-        cypher = {begin, end};
-        begin = end;
-    } else {
-        if(flags.isSigned()) {
-            seq = deserialize<decltype(seq)>(begin, end);
-            owner.unpack(begin, end);
-            if (flags.haveRecipient())
-               recipient = deserialize<InfoHash>(begin, end);
-        }
-        type = deserialize<ValueType::Id>(begin, end);
-        data = deserialize<Blob>(begin, end);
-        if (flags.isSigned())
-            signature = deserialize<Blob>(begin, end);
-    }
-}
-
-void
-Value::unpack(Blob::const_iterator& begin, Blob::const_iterator& end)
-{
-    id = deserialize<Id>(begin, end);
-    unpackBody(begin, end);
-}
-
-std::ostream& operator<< (std::ostream& s, const ServiceAnnouncement& v)
-{
-    s << "Peer: ";
-    s << "port " << v.getPort();
-
-    if (v.ss.ss_family == AF_INET || v.ss.ss_family == AF_INET6) {
-        char hbuf[NI_MAXHOST];
-        if (getnameinfo((sockaddr*)&v.ss, sizeof(v.ss), hbuf, sizeof(hbuf), nullptr, 0, NI_NUMERICHOST) == 0) {
-            s << " addr " << std::string(hbuf, strlen(hbuf));
-        }
-    }
-    return s;
-}
-
-void
-ServiceAnnouncement::pack(Blob& res) const
-{
-    serialize<in_port_t>(getPort(), res);
-    if (ss.ss_family == AF_INET) {
-        auto sa4 = reinterpret_cast<const sockaddr_in*>(&ss);
-        serialize<in_addr>(sa4->sin_addr, res);
-    } else if (ss.ss_family == AF_INET6) {
-        auto sa6 = reinterpret_cast<const sockaddr_in6*>(&ss);
-        serialize<in6_addr>(sa6->sin6_addr, res);
-    }
-}
-
-void
-ServiceAnnouncement::unpack(Blob::const_iterator& begin, Blob::const_iterator& end)
-{
-    setPort(deserialize<in_port_t>(begin, end));
-    size_t addr_size = end - begin;
-    if (addr_size < sizeof(in_addr)) {
-        ss.ss_family = 0;
-    } else if (addr_size == sizeof(in_addr)) {
-        auto sa4 = reinterpret_cast<sockaddr_in*>(&ss);
-        sa4->sin_family = AF_INET;
-        sa4->sin_addr = deserialize<in_addr>(begin, end);
-    } else if (addr_size == sizeof(in6_addr)) {
-        auto sa6 = reinterpret_cast<sockaddr_in6*>(&ss);
-        sa6->sin6_family = AF_INET6;
-        sa6->sin6_addr = deserialize<in6_addr>(begin, end);
-    } else {
-        throw std::runtime_error("ServiceAnnouncement parse error.");
-    }
-}
-
-
-}
diff --git a/daemon/src/ringdht/dhtcpp/value.h b/daemon/src/ringdht/dhtcpp/value.h
deleted file mode 100644
index e10ce05693..0000000000
--- a/daemon/src/ringdht/dhtcpp/value.h
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- *  Copyright (C) 2014 Savoir-Faire Linux Inc.
- *  Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  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.
- *
- *  Additional permission under GNU GPL version 3 section 7:
- *
- *  If you modify this program, or any covered work, by linking or
- *  combining it with the OpenSSL project's OpenSSL library (or a
- *  modified version of that library), containing parts covered by the
- *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
- *  grants you additional permission to convey the resulting work.
- *  Corresponding Source for a non-source form of such a combination
- *  shall include the source code for the parts of OpenSSL used as well
- *  as that of the covered work.
- */
-
-#pragma once
-
-#include "infohash.h"
-#include "crypto.h"
-#include "serialize.h"
-
-#include <sys/socket.h>
-#include <netdb.h>
-
-#include <string>
-#include <sstream>
-#include <bitset>
-#include <vector>
-#include <iostream>
-#include <algorithm>
-#include <functional>
-#include <memory>
-
-#include <cstdarg>
-
-namespace dht {
-
-/**
- * Wrapper for logging methods
- */
-struct LogMethod {
-    LogMethod() = default;
-
-    template<typename T>
-    LogMethod( T&& t) : func(std::forward<T>(t)) {}
-
-    void operator()(char const* format, ...) const {
-        va_list args;
-        va_start(args, format);
-        func(format, args);
-        va_end(args);
-    }
-
-    void logPrintable(const uint8_t *buf, size_t buflen) const {
-        std::string buf_clean(buflen, '\0');
-        for (size_t i=0; i<buflen; i++)
-            buf_clean[i] = buf[i] >= 32 && buf[i] <= 126 ? buf[i] : '.';
-        (*this)("%s", buf_clean.c_str());
-    }
-private:
-    std::function<void(char const*, va_list)> func;
-};
-
-/**
- * Dummy function used to disable logging
- */
-constexpr int NOLOG(char const*, va_list) { return 0; }
-
-
-class Value;
-
-typedef std::function<bool(InfoHash, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t)> StorePolicy;
-typedef std::function<bool(InfoHash, const std::shared_ptr<Value>&, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t)> EditPolicy;
-
-struct ValueType {
-    typedef uint16_t Id;
-    ValueType () {}
-
-    ValueType (Id id, std::string name, time_t e = 60 * 60)
-    : id(id), name(name), expiration(e) {}
-
-    ValueType (Id id, std::string name, time_t e, StorePolicy&& sp, EditPolicy&& ep)
-     : id(id), name(name), expiration(e), storePolicy(std::move(sp)), editPolicy(std::move(ep)) {}
-
-    virtual ~ValueType() {}
-
-    bool operator==(const ValueType& o) {
-       return id == o.id;
-    }
-
-    // Generic value type
-    static const ValueType USER_DATA;
-
-    static bool DEFAULT_STORE_POLICY(InfoHash, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t) {
-        return true;
-    }
-    static bool DEFAULT_EDIT_POLICY(InfoHash, const std::shared_ptr<Value>&, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t) {
-        return false;
-    }
-
-    Id id {0};
-    std::string name {};
-    time_t expiration {60 * 60};
-    StorePolicy storePolicy {DEFAULT_STORE_POLICY};
-    EditPolicy editPolicy {DEFAULT_EDIT_POLICY};
-};
-
-/**
- * A "value" is data potentially stored on the Dht, with some metadata.
- *
- * It can be an IP:port announced for a service, a public key, or any kind of
- * light user-defined data (recommended: less than 512 bytes).
- *
- * Values are stored at a given InfoHash in the Dht, but also have a
- * unique ID to distinguish between values stored at the same location.
- */
-struct Value : public Serializable
-{
-    typedef uint64_t Id;
-    static const Id INVALID_ID {0};
-
-    typedef std::function<bool(const Value&)> Filter;
-
-    static const Filter AllFilter() {
-        return [](const Value&){return true;};
-    }
-
-    static Filter TypeFilter(const ValueType& t) {
-        const auto tid = t.id;
-        return [tid](const Value& v) {
-            return v.type == tid;
-        };
-    }
-
-    static Filter chainFilters(Filter& f1, Filter& f2) {
-        return [f1,f2](const Value& v){
-            return f1(v) && f2(v);
-        };
-    }
-
-    /**
-     * Hold information about how the data is signed/encrypted.
-     * Class is final because bitset have no virtual destructor.
-     */
-    class ValueFlags final : public std::bitset<3> {
-    public:
-        using std::bitset<3>::bitset;
-        ValueFlags() {}
-        ValueFlags(bool sign, bool encrypted, bool have_recipient = false) : bitset<3>((sign ? 1:0) | (encrypted ? 2:0) | (have_recipient ? 4:0)) {}
-        bool isSigned() const {
-            return (*this)[0];
-        }
-        bool isEncrypted() const {
-            return (*this)[1];
-        }
-        bool haveRecipient() const {
-            return (*this)[2];
-        }
-    };
-
-    bool isEncrypted() const {
-        return flags.isEncrypted();
-    }
-    bool isSigned() const {
-        return flags.isSigned();
-    }
-
-    Value() {}
-
-    Value (Id id) : id(id) {}
-
-    /** Generic constructor */
-    Value(ValueType::Id t, const Blob& data, Id id = INVALID_ID)
-     : id(id), type(t), data(data) {}
-    Value(ValueType::Id t, const Serializable& d, Id id = INVALID_ID)
-     : id(id), type(t), data(d.getPacked()) {}
-    Value(const ValueType& t, const Serializable& d, Id id = INVALID_ID)
-     : id(id), type(t.id), data(d.getPacked()) {}
-
-    /** Custom user data constructor */
-    Value(const Blob& userdata) : data(userdata) {}
-    Value(Blob&& userdata) : data(std::move(userdata)) {}
-
-    Value(Value&& o) noexcept
-     : id(o.id), flags(o.flags), owner(std::move(o.owner)), recipient(o.recipient),
-     type(o.type), data(std::move(o.data)), seq(o.seq), signature(std::move(o.signature)), cypher(std::move(o.cypher)) {}
-
-    inline bool operator== (const Value& o) {
-        return id == o.id &&
-        (flags.isEncrypted() ? cypher == o.cypher :
-        (owner == o.owner && type == o.type && data == o.data && signature == o.signature));
-    }
-
-    void setRecipient(const InfoHash& r) {
-        recipient = r;
-        flags[2] = true;
-    }
-
-    void setCypher(Blob&& c) {
-        cypher = std::move(c);
-        flags = {true, true, true};
-    }
-
-    /**
-     * Pack part of the data to be signed
-     */
-    void packToSign(Blob& res) const;
-    Blob getToSign() const;
-
-    /**
-     * Pack part of the data to be encrypted
-     */
-    void packToEncrypt(Blob& res) const;
-    Blob getToEncrypt() const;
-
-    void pack(Blob& res) const;
-
-    void unpackBody(Blob::const_iterator& begin, Blob::const_iterator& end);
-    virtual void unpack(Blob::const_iterator& begin, Blob::const_iterator& end);
-
-    /** print value for debugging */
-    friend std::ostream& operator<< (std::ostream& s, const Value& v);
-
-    std::string toString() const {
-        std::stringstream ss;
-        ss << *this;
-        return ss.str();
-    }
-
-    Id id {INVALID_ID};
-
-    // data (part that is signed / encrypted)
-
-    ValueFlags flags {};
-
-    /**
-     * Public key of the signer.
-     */
-    crypto::PublicKey owner {};
-
-    /**
-     * Hash of the recipient (optional).
-     * Should only be present for encrypted values.
-     * Can optionally be present for signed values.
-     */
-    InfoHash recipient {};
-
-    /**
-     * Type of data.
-     */
-    ValueType::Id type {ValueType::USER_DATA.id};
-    Blob data {};
-
-    /**
-     * Sequence number to avoid replay attacks
-     */
-    uint16_t seq {0};
-
-    /**
-     * Optional signature.
-     */
-    Blob signature {};
-
-    /**
-     * Hold encrypted version of the data.
-     */
-    Blob cypher {};
-};
-
-
-/* "Peer" announcement
- */
-struct ServiceAnnouncement : public Serializable
-{
-    ServiceAnnouncement(in_port_t p = 0) {
-        ss.ss_family = 0;
-        setPort(p);
-    }
-
-    ServiceAnnouncement(const sockaddr* sa, socklen_t sa_len) {
-        if (sa)
-            std::copy_n((const uint8_t*)sa, sa_len, (uint8_t*)&ss);
-    }
-
-    ServiceAnnouncement(const Blob& b) {
-        unpackBlob(b);
-    }
-
-    virtual void pack(Blob& res) const;
-    virtual void unpack(Blob::const_iterator& begin, Blob::const_iterator& end);
-
-    in_port_t getPort() const {
-        return ntohs(reinterpret_cast<const sockaddr_in*>(&ss)->sin_port);
-    }
-    void setPort(in_port_t p) {
-        reinterpret_cast<sockaddr_in*>(&ss)->sin_port = htons(p);
-    }
-
-    sockaddr_storage getPeerAddr() const {
-        return ss;
-    }
-
-    static const ValueType TYPE;
-    static bool storePolicy(InfoHash, std::shared_ptr<Value>&, InfoHash, const sockaddr*, socklen_t);
-
-    /** print value for debugging */
-    friend std::ostream& operator<< (std::ostream&, const ServiceAnnouncement&);
-
-private:
-    sockaddr_storage ss;
-};
-
-}
diff --git a/daemon/src/ringdht/ringaccount.cpp b/daemon/src/ringdht/ringaccount.cpp
index 75f206e49d..bedbfa02ac 100644
--- a/daemon/src/ringdht/ringaccount.cpp
+++ b/daemon/src/ringdht/ringaccount.cpp
@@ -40,7 +40,7 @@
 #include "sip/sipcall.h"
 #include "sip/sip_utils.h"
 
-#include "dhtcpp/securedht.h"
+#include <opendht/securedht.h>
 
 #include "array_size.h"
 
diff --git a/daemon/src/ringdht/ringaccount.h b/daemon/src/ringdht/ringaccount.h
index a3d6daa469..2822412dbf 100644
--- a/daemon/src/ringdht/ringaccount.h
+++ b/daemon/src/ringdht/ringaccount.h
@@ -41,7 +41,8 @@
 #include "noncopyable.h"
 #include "ip_utils.h"
 #include "sfl_types.h" // enable_if_base_of
-#include "dhtcpp/dhtrunner.h"
+
+#include <opendht/dhtrunner.h>
 
 #include <pjsip/sip_transport_tls.h>
 #include <pjsip/sip_types.h>
diff --git a/daemon/tools/Makefile.am b/daemon/tools/Makefile.am
index 0447a71cdf..d469d4732b 100644
--- a/daemon/tools/Makefile.am
+++ b/daemon/tools/Makefile.am
@@ -5,8 +5,6 @@ libexec_PROGRAMS = dhtnode
 
 dhtnode_SOURCES = dhtnode.cpp
 
-dhtnode_CXXFLAGS=-I$(top_srcdir)/src/ringdht/dhtcpp
+dhtnode_CXXFLAGS= @OPENDHT_CFLAGS@
 
-dhtnode_LDFLAGS = @GNUTLS_LIBS@ -lpthread
-
-dhtnode_LDADD = $(top_builddir)/src/ringdht/dhtcpp/libdhtcpp.la
+dhtnode_LDFLAGS = @GNUTLS_LIBS@ -lpthread @OPENDHT_LIBS@
diff --git a/daemon/tools/dhtnode.cpp b/daemon/tools/dhtnode.cpp
index d185427c9e..65c7b3b0b3 100644
--- a/daemon/tools/dhtnode.cpp
+++ b/daemon/tools/dhtnode.cpp
@@ -29,8 +29,7 @@
  *  as that of the covered work.
  */
 
-#include "dhtrunner.h"
-#include "dht.h"
+#include <opendht.h>
 
 extern "C" {
 #include <gnutls/gnutls.h>
-- 
GitLab