diff --git a/daemon/src/Makefile.am b/daemon/src/Makefile.am
index 1b71b13fb4628c96f68264fa6ddaa8e15104da0b..0a87e45cbb713365dc93ab24daeaa821427daf58 100644
--- a/daemon/src/Makefile.am
+++ b/daemon/src/Makefile.am
@@ -20,6 +20,12 @@ IAX_LIBA=./iax/libiaxlink.la
 IAX_LIB=-liax
 endif
 
+if USE_DHT
+DHT_SUBDIR=dht
+DHT_CXXFLAG=-DUSE_DHT
+DHT_LIBA=./dht/libdht.la
+endif
+
 if USE_NETWORKMANAGER
 NETWORKMANAGER=-DUSE_NETWORKMANAGER
 endif
@@ -29,11 +35,7 @@ TLS_LIB = @GNUTLS_LIBS@
 TLS_CFLAGS = @GNUTLS_CFLAGS@
 endif
 
-SUBDIRS = client audio config hooks history sip $(IAX_SUBDIR) $(INSTANT_MESSAGING_SUBDIR) $(SFL_VIDEO_SUBDIR)
-
-if USE_DHT
-SUBDIRS += dht
-endif
+SUBDIRS = client audio config hooks history sip $(IAX_SUBDIR) $(DHT_SUBDIR) $(INSTANT_MESSAGING_SUBDIR) $(SFL_VIDEO_SUBDIR)
 
 # libsflphone
 
@@ -46,6 +48,7 @@ libsflphone_la_LIBADD = \
 	./config/libconfig.la \
 	./hooks/libhooks.la \
 	./history/libhistory.la \
+	$(DHT_LIBA) \
 	$(IAX_LIBA) \
 	$(IM_LIBA) \
 	$(SFL_VIDEO_LIBS)
diff --git a/daemon/src/account_factory.cpp b/daemon/src/account_factory.cpp
index 73bf24cba682c2bfcca4c0a3fff88f0024fff171..53dedb4759cb8291ad8812f0e907e0341fc4b3e4 100644
--- a/daemon/src/account_factory.cpp
+++ b/daemon/src/account_factory.cpp
@@ -38,6 +38,9 @@
 #if HAVE_IAX
 #include "iax/iaxaccount.h"
 #endif
+#if HAVE_DHT
+#include "dht/dhtaccount.h"
+#endif
 
 #include "sip/sipvoiplink.h" // for SIPVoIPLink::loadIP2IPSettings
 
@@ -55,6 +58,11 @@ AccountFactory::AccountFactory()
     generators_.insert(std::make_pair(IAXAccount::ACCOUNT_TYPE, iaxfunc));
     DEBUG("registered %s account", IAXAccount::ACCOUNT_TYPE);
 #endif
+#if HAVE_DHT
+    auto dhtfunc = [](const std::string& id){ return std::make_shared<DHTAccount>(id, false); };
+    generators_.insert(std::make_pair(DHTAccount::ACCOUNT_TYPE, dhtfunc));
+    DEBUG("registered %s account", DHTAccount::ACCOUNT_TYPE);
+#endif
 }
 
 std::shared_ptr<Account>
diff --git a/daemon/src/dht/Makefile.am b/daemon/src/dht/Makefile.am
index 8def4e2fc28c720e83045eb059bc5b734dfcf745..b748335c8e0e3aa45975db983daa6b3e3316f93d 100644
--- a/daemon/src/dht/Makefile.am
+++ b/daemon/src/dht/Makefile.am
@@ -1 +1,17 @@
+include $(top_srcdir)/globals.mak
+
+if USE_DHT
+
+noinst_LTLIBRARIES = libdht.la
+libdht_la_CXXFLAGS = @CXXFLAGS@
+
 SUBDIRS = dhtcpp
+
+libdht_la_LIBADD = \
+        dhtcpp/libdhtcpp.la
+
+libdht_la_SOURCES = \
+        dhtaccount.cpp \
+        dhtaccount.h
+
+endif
diff --git a/daemon/src/dht/dhtaccount.cpp b/daemon/src/dht/dhtaccount.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5ecdc79c0f8f3c7ead83a523ba7f9b9fb3579e61
--- /dev/null
+++ b/daemon/src/dht/dhtaccount.cpp
@@ -0,0 +1,668 @@
+/*
+ *  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 "dhtaccount.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sip/sdp.h"
+#include "sip/sipvoiplink.h"
+#include "sip/sipcall.h"
+#include "sip/sip_utils.h"
+
+#include "dhtcpp/securedht.h"
+
+#include "array_size.h"
+
+#include "call_factory.h"
+
+#ifdef SFL_PRESENCE
+//#include "sippresence.h"
+#include "client/configurationmanager.h"
+#endif
+
+#include "account_schema.h"
+#include "logger.h"
+#include "manager.h"
+
+#ifdef SFL_VIDEO
+#include "video/libav_utils.h"
+#endif
+#include "fileutils.h"
+
+#include "config/yamlparser.h"
+#include <yaml-cpp/yaml.h>
+
+#include <unistd.h>
+#include <pwd.h>
+
+#include <algorithm>
+#include <array>
+#include <memory>
+#include <sstream>
+#include <cstdlib>
+
+constexpr const char * const DHTAccount::ACCOUNT_TYPE;
+
+DHTAccount::DHTAccount(const std::string& accountID, bool /* presenceEnabled */)
+    : SIPAccountBase(accountID)
+{
+    fileutils::check_dir(fileutils::get_cache_dir().c_str());
+    nodePath_ = fileutils::get_cache_dir()+DIR_SEPARATOR_STR+getAccountID();
+    fileutils::check_dir(nodePath_.c_str());
+    WARN("node cache path: %s", nodePath_.c_str());
+    if (privkeyPath_.empty()) {
+        fileutils::check_dir(fileutils::get_data_dir().c_str());
+        const auto idPath = fileutils::get_data_dir()+DIR_SEPARATOR_STR+getAccountID();
+        fileutils::check_dir(idPath.c_str());
+        privkeyPath_ = idPath+DIR_SEPARATOR_STR+"id_rsa";
+        WARN("privkeyPath : %s", privkeyPath_.c_str());
+    }
+    if (certPath_.empty()) {
+        certPath_ = privkeyPath_+".pub";
+        WARN("certPath : %s", certPath_.c_str());
+    }
+    int rc = gnutls_global_init();
+    if (rc != GNUTLS_E_SUCCESS) {
+        ERROR("Error initializing GnuTLS : %s", gnutls_strerror(rc));
+        throw VoipLinkException("Can't initialize GnuTLS.");
+    }
+}
+
+DHTAccount::~DHTAccount()
+{
+    setTransport();
+    dht_.join();
+    gnutls_global_deinit();
+}
+
+std::shared_ptr<SIPCall>
+DHTAccount::newIncomingCall(const std::string& id)
+{
+    return Manager::instance().callFactory.newCall<SIPCall, DHTAccount>(*this, id, Call::INCOMING);
+}
+
+template <>
+std::shared_ptr<SIPCall>
+DHTAccount::newOutgoingCall(const std::string& id, const std::string& toUrl)
+{
+    auto call = Manager::instance().callFactory.newCall<SIPCall, DHTAccount>(*this, id, Call::OUTGOING);
+    auto dhtf = toUrl.find("dht:");
+    dhtf = (dhtf == std::string::npos) ? 0 : dhtf+4;
+    const std::string toUri = toUrl.substr(dhtf, 40);
+    DEBUG("Calling DHT peer %s", toUri.c_str());
+    call->setIPToIP(true);
+
+    auto resolved = std::make_shared<bool>(false);
+    dht_.get(dht::InfoHash(toUri),
+        [this,call,toUri,resolved](const std::vector<std::shared_ptr<dht::Value>>& values) {
+            if (*resolved)
+                return false;
+            for (const auto& v : values) {
+                DEBUG("Resolved value : %s", v->toString().c_str());
+                IpAddr peer;
+                try {
+                    peer = IpAddr{ dht::ServiceAnnouncement(v->data).getPeerAddr() };
+                } catch (const std::exception& e) {
+                    ERROR("Exception while reading value : %s", e.what());
+                    continue;
+                }
+                *resolved = true;
+                std::string toip = getToUri(toUri+"@"+peer.toString(true, true));
+                DEBUG("Got DHT peer IP: %s", toip.c_str());
+                createOutgoingCall(call, toUri, toip, peer);
+                return false;
+            }
+            return true;
+        },
+        [call,resolved] (bool ok){
+            if (not *resolved) {
+                call->setConnectionState(Call::DISCONNECTED);
+                call->setState(Call::ERROR);
+            }
+        },
+        dht::Value::TypeFilter(dht::ServiceAnnouncement::TYPE));
+    return call;
+}
+
+void
+DHTAccount::createOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::string& to, const std::string& toUri, const IpAddr& peer)
+{
+    WARN("DHTAccount::createOutgoingCall to: %s toUri: %s tlsListener: %d", to.c_str(), toUri.c_str(), tlsListener_?1:0);
+    std::shared_ptr<SipTransport> t = link_->sipTransport->getTlsTransport(tlsListener_, getToUri(peer.toString(true, true)));
+    setTransport(t);
+    call->setTransport(t);
+    call->setIPToIP(true);
+    call->setPeerNumber(toUri);
+    call->initRecFilename(to);
+
+    const auto localAddress = ip_utils::getInterfaceAddr(getLocalInterface(), peer.getFamily());
+    call->setCallMediaLocal(localAddress);
+
+    // May use the published address as well
+    const auto addrSdp = isStunEnabled() or (not getPublishedSameasLocal()) ?
+        getPublishedIpAddress() : localAddress;
+
+    // Initialize the session using ULAW as default codec in case of early media
+    // The session should be ready to receive media once the first INVITE is sent, before
+    // the session initialization is completed
+    sfl::AudioCodec* ac = Manager::instance().audioCodecFactory.instantiateCodec(PAYLOAD_CODEC_ULAW);
+    if (!ac)
+        throw VoipLinkException("Could not instantiate codec for early media");
+
+    std::vector<sfl::AudioCodec *> audioCodecs;
+    audioCodecs.push_back(ac);
+
+    try {
+        call->getAudioRtp().initConfig();
+        call->getAudioRtp().initSession();
+
+        if (isStunEnabled())
+            call->updateSDPFromSTUN();
+
+        call->getAudioRtp().initLocalCryptoInfo();
+        call->getAudioRtp().start(audioCodecs);
+    } catch (...) {
+        throw VoipLinkException("Could not start rtp session for early media");
+    }
+
+    // Building the local SDP offer
+    auto& localSDP = call->getLocalSDP();
+
+    if (getPublishedSameasLocal())
+        localSDP.setPublishedIP(addrSdp);
+    else
+        localSDP.setPublishedIP(getPublishedAddress());
+
+    const bool created = localSDP.createOffer(getActiveAudioCodecs(), getActiveVideoCodecs());
+
+    if (not created or not SIPStartCall(call))
+        throw VoipLinkException("Could not send outgoing INVITE request for new call");
+}
+
+std::shared_ptr<Call>
+DHTAccount::newOutgoingCall(const std::string& id, const std::string& toUrl)
+{
+    return newOutgoingCall<SIPCall>(id, toUrl);
+}
+
+bool
+DHTAccount::SIPStartCall(const std::shared_ptr<SIPCall>& call)
+{
+    std::string toUri(call->getPeerNumber()); // expecting a fully well formed sip uri
+
+    pj_str_t pjTo = pj_str((char*) toUri.c_str());
+
+    // Create the from header
+    std::string from(getFromUri());
+    pj_str_t pjFrom = pj_str((char*) from.c_str());
+
+    pj_str_t pjContact(getContactHeader());
+
+    const std::string debugContactHeader(pj_strbuf(&pjContact), pj_strlen(&pjContact));
+    DEBUG("contact header: %s / %s -> %s",
+          debugContactHeader.c_str(), from.c_str(), toUri.c_str());
+
+    pjsip_dialog *dialog = NULL;
+
+    if (pjsip_dlg_create_uac(pjsip_ua_instance(), &pjFrom, &pjContact, &pjTo, NULL, &dialog) != PJ_SUCCESS) {
+        ERROR("Unable to create SIP dialogs for user agent client when "
+              "calling %s", toUri.c_str());
+        return false;
+    }
+
+    pj_str_t subj_hdr_name = CONST_PJ_STR("Subject");
+    pjsip_hdr* subj_hdr = (pjsip_hdr*) pjsip_parse_hdr(dialog->pool, &subj_hdr_name, (char *) "Phone call", 10, NULL);
+
+    pj_list_push_back(&dialog->inv_hdr, subj_hdr);
+
+    pjsip_inv_session* inv = nullptr;
+    if (pjsip_inv_create_uac(dialog, call->getLocalSDP().getLocalSdpSession(), 0, &inv) != PJ_SUCCESS) {
+        ERROR("Unable to create invite session for user agent client");
+        return false;
+    }
+
+    if (!inv) {
+        ERROR("Call invite is not initialized");
+        return PJ_FALSE;
+    }
+
+    call->inv.reset(inv);
+
+/*
+    updateDialogViaSentBy(dialog);
+    if (hasServiceRoute())
+        pjsip_dlg_set_route_set(dialog, sip_utils::createRouteSet(getServiceRoute(), call->inv->pool));
+*/
+    call->inv->mod_data[link_->getModId()] = (void*)call.get();
+
+    pjsip_tx_data *tdata;
+
+    if (pjsip_inv_invite(call->inv.get(), &tdata) != PJ_SUCCESS) {
+        ERROR("Could not initialize invite messager for this call");
+        return false;
+    }
+
+    const pjsip_tpselector tp_sel = getTransportSelector();
+    if (pjsip_dlg_set_transport(dialog, &tp_sel) != PJ_SUCCESS) {
+        ERROR("Unable to associate transport for invite session dialog");
+        return false;
+    }
+
+    if (pjsip_inv_send_msg(call->inv.get(), tdata) != PJ_SUCCESS) {
+        call->inv.reset();
+        ERROR("Unable to send invite message for this call");
+        return false;
+    }
+
+    call->setConnectionState(Call::PROGRESSING);
+    call->setState(Call::ACTIVE);
+
+    return true;
+}
+
+void DHTAccount::serialize(YAML::Emitter &out)
+{
+    using namespace Conf;
+
+    out << YAML::BeginMap;
+    SIPAccountBase::serialize(out);
+    out << YAML::Key << DHT_PRIVKEY_PATH_KEY << YAML::Value << privkeyPath_;
+    out << YAML::Key << DHT_CERT_PATH_KEY << YAML::Value << certPath_;
+
+    // tls submap
+    out << YAML::Key << TLS_KEY << YAML::Value << YAML::BeginMap;
+    SIPAccountBase::serializeTls(out);
+    out << YAML::EndMap;
+
+    out << YAML::EndMap;
+}
+
+void DHTAccount::unserialize(const YAML::Node &node)
+{
+    using namespace yaml_utils;
+    SIPAccountBase::unserialize(node);
+    parseValue(node, Conf::DHT_PRIVKEY_PATH_KEY, privkeyPath_);
+    parseValue(node, Conf::DHT_CERT_PATH_KEY, certPath_);
+}
+
+dht::crypto::Identity
+DHTAccount::loadIdentity() const
+{
+    std::vector<char> buffer;
+    std::vector<char> buffer_crt;
+    try {
+        {
+            std::ifstream file(privkeyPath_, std::ios::binary);
+            if (!file)
+                throw std::runtime_error("Can't read private key file.");
+            file.seekg(0, std::ios::end);
+            std::streamsize size = file.tellg();
+            if (size > std::numeric_limits<unsigned>::max())
+                throw std::runtime_error("Can't read private key file.");
+            buffer.resize(size);
+            file.seekg(0, std::ios::beg);
+            if (!file.read(buffer.data(), size))
+                throw std::runtime_error("Can't load private key.");
+        }
+        {
+            std::ifstream file(certPath_, std::ios::binary);
+            if (!file)
+                throw std::runtime_error("Can't read certificate file.");
+            file.seekg(0, std::ios::end);
+            std::streamsize size = file.tellg();
+            if (size > std::numeric_limits<unsigned>::max())
+                throw std::runtime_error("Can't read certificate file.");
+            buffer_crt.resize(size);
+            file.seekg(0, std::ios::beg);
+            if (!file.read(buffer_crt.data(), size))
+                throw std::runtime_error("Can't load certificate.");
+        }
+    }
+    catch (const std::exception& e) {
+        ERROR("Error loading identity: %s", e.what());
+        auto id = dht::crypto::generateIdentity();
+        if (!id.first || !id.second) {
+            throw VoipLinkException("Can't generate identity for this account.");
+        }
+        saveIdentity(id);
+        return id;
+    }
+
+    gnutls_datum_t dt {reinterpret_cast<uint8_t*>(buffer.data()), static_cast<unsigned>(buffer.size())};
+    gnutls_x509_privkey_t x509_key;
+    gnutls_x509_privkey_init(&x509_key);
+    int err = gnutls_x509_privkey_import(x509_key, &dt, GNUTLS_X509_FMT_PEM);
+    if (err != GNUTLS_E_SUCCESS) {
+        ERROR("Could not read PEM key - %s", gnutls_strerror(err));
+        err = gnutls_x509_privkey_import(x509_key, &dt, GNUTLS_X509_FMT_DER);
+    }
+    if (err != GNUTLS_E_SUCCESS) {
+        gnutls_x509_privkey_deinit(x509_key);
+        ERROR("Could not read key - %s", gnutls_strerror(err));
+        return {};
+    }
+
+    gnutls_x509_crt_t certificate;
+    gnutls_x509_crt_init(&certificate);
+
+    gnutls_datum_t crt_dt {reinterpret_cast<uint8_t*>(buffer_crt.data()), static_cast<unsigned>(buffer_crt.size())};
+    err = gnutls_x509_crt_import(certificate, &crt_dt, GNUTLS_X509_FMT_PEM);
+    if (err != GNUTLS_E_SUCCESS) {
+        ERROR("Could not read PEM certificate - %s", gnutls_strerror(err));
+        err = gnutls_x509_crt_import(certificate, &crt_dt, GNUTLS_X509_FMT_DER);
+    }
+    if (err != GNUTLS_E_SUCCESS) {
+        gnutls_x509_privkey_deinit(x509_key);
+        gnutls_x509_crt_deinit(certificate);
+        ERROR("Could not read key - %s", gnutls_strerror(err));
+        return {};
+    }
+
+    return {std::make_shared<dht::crypto::PrivateKey>(x509_key), std::make_shared<dht::crypto::Certificate>(certificate)};
+}
+
+void
+DHTAccount::saveIdentity(const dht::crypto::Identity id) const
+{
+    if (id.first) {
+        auto buffer = id.first->serialize();
+        std::ofstream file(privkeyPath_, std::ios::trunc | std::ios::binary);
+        if (!file.is_open()) {
+            ERROR("Could not write key to %s", privkeyPath_.c_str());
+            return;
+        }
+        file.write((char*)buffer.data(), buffer.size());
+    }
+
+    if (id.second) {
+        auto buffer = id.second->getPacked();
+        std::ofstream file(certPath_, std::ios::trunc | std::ios::binary);
+        if (!file.is_open()) {
+            ERROR("Could not write key to %s", certPath_.c_str());
+            return;
+        }
+        file.write((char*)buffer.data(), buffer.size());
+    }
+}
+
+void DHTAccount::setAccountDetails(const std::map<std::string, std::string> &details)
+{
+    SIPAccountBase::setAccountDetails(details);
+}
+
+std::map<std::string, std::string> DHTAccount::getAccountDetails() const
+{
+    std::map<std::string, std::string> a = SIPAccountBase::getAccountDetails();
+    return a;
+}
+
+void DHTAccount::doRegister()
+{
+    if (not isEnabled()) {
+        WARN("Account must be enabled to register, ignoring");
+        return;
+    }
+
+    DEBUG("doRegister");
+    try {
+        if (dht_.isRunning()) {
+            ERROR("DHT already running");
+        }
+        dht_.run(getLocalPort()+5, loadIdentity(), [=](dht::Dht::Status s4, dht::Dht::Status s6) {
+            WARN("Dht status : %d %d", (int)s4, (int)s6);
+            auto status = std::max(s4, s6);
+            switch(status) {
+            case dht::Dht::Status::Connecting:
+            case dht::Dht::Status::Connected:
+                setRegistrationState(status == dht::Dht::Status::Connected ? RegistrationState::REGISTERED : RegistrationState::TRYING);
+                if (!tlsListener_) {
+                    initTlsConfiguration();
+                    tlsListener_ = link_->sipTransport->getTlsListener(
+                        SipTransportDescr {getTransportType(), getTlsListenerPort(), getLocalInterface()},
+                        getTlsSetting());
+                    if (!tlsListener_) {
+                        setRegistrationState(RegistrationState::ERROR_GENERIC);
+                        ERROR("Error creating TLS listener.");
+                        return;
+                    }
+                }
+                break;
+            case dht::Dht::Status::Disconnected:
+            default:
+                setRegistrationState(status == dht::Dht::Status::Disconnected ? RegistrationState::UNREGISTERED : RegistrationState::ERROR_GENERIC);
+                tlsListener_.reset();
+                break;
+            }
+        });
+        dht_.put(dht_.getId(), dht::Value{dht::ServiceAnnouncement::TYPE.id, dht::ServiceAnnouncement(getTlsListenerPort())}, [](bool ok) {
+            DEBUG("Peer announce callback ! %d", ok);
+        });
+
+        username_ = dht_.getId().toString();
+
+        Manager::instance().registerEventHandler((uintptr_t)this, std::bind(&dht::DhtRunner::loop, &dht_));
+        setRegistrationState(RegistrationState::TRYING);
+
+        dht_.bootstrap(loadNodes());
+        if (!hostname_.empty()) {
+            std::stringstream ss(hostname_);
+            std::vector<sockaddr_storage> bootstrap;
+            std::string node_addr;
+            while (std::getline(ss, node_addr, ';')) {
+                auto ips = ip_utils::getAddrList(node_addr);
+                bootstrap.insert(bootstrap.end(), ips.begin(), ips.end());
+            }
+            for (auto ip : bootstrap)
+                DEBUG("Bootstrap node: %s", IpAddr(ip).toString(true).c_str());
+            dht_.bootstrap(bootstrap);
+        }
+    }
+    catch (const std::exception& e) {
+        ERROR("Error registering DHT account: %s", e.what());
+        setRegistrationState(RegistrationState::ERROR_GENERIC);
+    }
+}
+
+
+void DHTAccount::doUnregister(std::function<void(bool)> released_cb)
+{
+    Manager::instance().unregisterEventHandler((uintptr_t)this);
+    saveNodes(dht_.getNodes());
+    dht_.join();
+    tlsListener_.reset();
+    setRegistrationState(RegistrationState::UNREGISTERED);
+    if (released_cb)
+        released_cb(false);
+}
+
+void DHTAccount::saveNodes(const std::vector<dht::Dht::NodeExport>& nodes) const
+{
+    if (nodes.empty())
+        return;
+    std::string nodesPath = nodePath_+DIR_SEPARATOR_STR "nodes";
+    {
+        std::ofstream file(nodesPath, std::ios::trunc);
+        if (!file.is_open()) {
+            ERROR("Could not save nodes to %s", nodesPath.c_str());
+            return;
+        }
+        for (auto& n : nodes)
+            file << n.id << " " << IpAddr(n.ss).toString(true) << "\n";
+    }
+}
+
+std::vector<dht::Dht::NodeExport>
+DHTAccount::loadNodes() const
+{
+    std::vector<dht::Dht::NodeExport> nodes;
+    std::string nodesPath = nodePath_+DIR_SEPARATOR_STR "nodes";
+    {
+        std::ifstream file(nodesPath);
+        if (!file.is_open()) {
+            ERROR("Could not load nodes from %s", nodesPath.c_str());
+            return nodes;
+        }
+        std::string line;
+        while (std::getline(file, line))
+        {
+            std::istringstream iss(line);
+            std::string id, ipstr;
+            if (!(iss >> id >> ipstr)) { break; }
+            IpAddr ip {ipstr};
+            dht::Dht::NodeExport e {{id}, ip, ip.getLength()};
+            nodes.push_back(e);
+        }
+    }
+}
+
+void DHTAccount::initTlsConfiguration()
+{
+    ciphers_ = {PJ_TLS_RSA_WITH_AES_256_CBC_SHA256};
+
+    // TLS listener is unique and should be only modified through IP2IP_PROFILE
+    pjsip_tls_setting_default(&tlsSetting_);
+
+    pj_cstr(&tlsSetting_.ca_list_file, "");
+    pj_cstr(&tlsSetting_.cert_file, certPath_.c_str());
+    pj_cstr(&tlsSetting_.privkey_file, privkeyPath_.c_str());
+    pj_cstr(&tlsSetting_.password, "");
+    tlsSetting_.method = PJSIP_TLSV1_METHOD;
+    tlsSetting_.ciphers_num = ciphers_.size();
+    tlsSetting_.ciphers = &ciphers_.front();
+    tlsSetting_.verify_server = false;
+    tlsSetting_.verify_client = false;
+    tlsSetting_.require_client_cert = false;
+    tlsSetting_.timeout.sec = 2;
+    tlsSetting_.qos_type = PJ_QOS_TYPE_BEST_EFFORT;
+    tlsSetting_.qos_ignore_error = PJ_TRUE;
+}
+
+void DHTAccount::loadConfig()
+{
+    WARN("DHTAccount::loadConfig()");
+    initTlsConfiguration();
+    transportType_ = PJSIP_TRANSPORT_TLS;
+}
+
+bool DHTAccount::userMatch(const std::string& username) const
+{
+    return !username.empty() and username == dht_.getId().toString();
+}
+
+MatchRank
+DHTAccount::matches(const std::string &userName, const std::string &server) const
+{
+    if (userMatch(userName)) {
+        DEBUG("Matching account id in request with username %s", userName.c_str());
+        return MatchRank::FULL;
+    } else {
+        return MatchRank::NONE;
+    }
+}
+
+std::string DHTAccount::getFromUri() const
+{
+    std::string username(username_);
+    std::string transport {pjsip_transport_get_type_name(transportType_)};
+    return username + "<dht:" + dht_.getId().toString() + "@dht.invalid;transport=" + transport + ">";
+}
+
+std::string DHTAccount::getToUri(const std::string& to) const
+{
+    const std::string transport {pjsip_transport_get_type_name(transportType_)};
+    return "<sips:" + to + ";transport=" + transport + ">";
+}
+
+pj_str_t
+DHTAccount::getContactHeader()
+{
+    if (transport_ == nullptr)
+        ERROR("Transport not created yet");
+
+    // The transport type must be specified, in our case START_OTHER refers to stun transport
+    pjsip_transport_type_e transportType = transportType_;
+
+    if (transportType == PJSIP_TRANSPORT_START_OTHER)
+        transportType = PJSIP_TRANSPORT_UDP;
+
+    // Else we determine this infor based on transport information
+    std::string address = "dht.invalid";
+    pj_uint16_t port = getTlsListenerPort();
+
+    link_->sipTransport->findLocalAddressFromTransport(transport_->get(), transportType, hostname_, address, port);
+
+#if HAVE_IPV6
+    /* Enclose IPv6 address in square brackets */
+    if (IpAddr::isIpv6(address)) {
+        address = IpAddr(address).toString(false, true);
+    }
+#endif
+
+    WARN("getContactHeader %s@%s:%d", username_.c_str(), address.c_str(), port);
+    contact_.slen = pj_ansi_snprintf(contact_.ptr, PJSIP_MAX_URL_SIZE,
+                                     "<sips:%s%s%s:%d;transport=%s>",
+                                     username_.c_str(),
+                                     (username_.empty() ? "" : "@"),
+                                     address.c_str(),
+                                     port,
+                                     pjsip_transport_get_type_name(transportType));
+    return contact_;
+}
+
+#ifdef SFL_PRESENCE
+/**
+ *  Enable the presence module
+ */
+void
+DHTAccount::enablePresence(const bool& /* enabled */)
+{
+}
+
+/**
+ *  Set the presence (PUBLISH/SUBSCRIBE) support flags
+ *  and process the change.
+ */
+void
+DHTAccount::supportPresence(int /* function */, bool /* enabled*/)
+{
+}
+#endif
+
+/*
+void DHTAccount::updateDialogViaSentBy(pjsip_dialog *dlg)
+{
+    if (allowViaRewrite_ && via_addr_.host.slen > 0)
+        pjsip_dlg_set_via_sent_by(dlg, &via_addr_, via_tp_);
+}
+*/
\ No newline at end of file
diff --git a/daemon/src/dht/dhtaccount.h b/daemon/src/dht/dhtaccount.h
new file mode 100644
index 0000000000000000000000000000000000000000..65d0601ac05be59afe34da2a683310aeaa781035
--- /dev/null
+++ b/daemon/src/dht/dhtaccount.h
@@ -0,0 +1,335 @@
+/*
+ *  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.
+ */
+
+#ifndef DHTACCOUNT_H
+#define DHTACCOUNT_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sip/siptransport.h"
+#include "sip/sipaccountbase.h"
+#include "noncopyable.h"
+#include "ip_utils.h"
+#include "sfl_types.h" // enable_if_base_of
+#include "dhtcpp/dhtrunner.h"
+
+#include <pjsip/sip_transport_tls.h>
+#include <pjsip/sip_types.h>
+#include <pjsip-ua/sip_regc.h>
+
+#include <vector>
+#include <map>
+
+namespace Conf {
+    const char *const DHT_PRIVKEY_PATH_KEY = "dhtPrivkeyPath";
+    const char *const DHT_CERT_PATH_KEY = "dhtPubkeyPath";
+}
+
+namespace YAML {
+    class Node;
+    class Emitter;
+}
+
+/**
+ * @file sipaccount.h
+ * @brief A SIP Account specify SIP specific functions and object = SIPCall/SIPVoIPLink)
+ */
+class DHTAccount : public SIPAccountBase {
+    public:
+        constexpr static const char * const ACCOUNT_TYPE = "DHT";
+
+        const char* getAccountType() const {
+            return ACCOUNT_TYPE;
+        }
+
+        /**
+         * Constructor
+         * @param accountID The account identifier
+         */
+        DHTAccount(const std::string& accountID, bool presenceEnabled);
+
+        ~DHTAccount();
+
+        /**
+         * Serialize internal state of this account for configuration
+         * @param YamlEmitter the configuration engine which generate the configuration file
+         */
+        virtual void serialize(YAML::Emitter &out);
+
+        /**
+         * Populate the internal state for this account based on info stored in the configuration file
+         * @param The configuration node for this account
+         */
+        virtual void unserialize(const YAML::Node &node);
+
+        /**
+         * Return an map containing the internal state of this account. Client application can use this method to manage
+         * account info.
+         * @return A map containing the account information.
+         */
+        virtual std::map<std::string, std::string> getAccountDetails() const;
+
+        /**
+         * Actually useless, since config loading is done in init()
+         */
+        void loadConfig();
+
+        /**
+         * Connect to the DHT.
+         */
+        void doRegister();
+
+        /**
+         * Disconnect from the DHT.
+         */
+        void doUnregister(std::function<void(bool)> cb = std::function<void(bool)>());
+
+        /**
+         * @return pj_str_t "From" uri based on account information.
+         * From RFC3261: "The To header field first and foremost specifies the desired
+         * logical" recipient of the request, or the address-of-record of the
+         * user or resource that is the target of this request. [...]  As such, it is
+         * very important that the From URI not contain IP addresses or the FQDN
+         * of the host on which the UA is running, since these are not logical
+         * names."
+         */
+        std::string getFromUri() const;
+
+        /**
+         * This method adds the correct scheme, hostname and append
+         * the ;transport= parameter at the end of the uri, in accordance with RFC3261.
+         * It is expected that "port" is present in the internal hostname_.
+         *
+         * @return pj_str_t "To" uri based on @param username
+         * @param username A string formatted as : "username"
+         */
+        std::string getToUri(const std::string& username) const;
+
+        /**
+         * In the current version of SFLPhone, "srv" uri is obtained in the preformated
+         * way: hostname:port. This method adds the correct scheme and append
+         * the ;transport= parameter at the end of the uri, in accordance with RFC3261.
+         *
+         * @return pj_str_t "server" uri based on @param hostPort
+         * @param hostPort A string formatted as : "hostname:port"
+         */
+        std::string getServerUri() const { return ""; };
+
+        /**
+         * Get the contact header for
+         * @return pj_str_t The contact header based on account information
+         */
+        pj_str_t getContactHeader();
+
+        void setReceivedParameter(const std::string &received) {
+            receivedParameter_ = received;
+            via_addr_.host.ptr = (char *) receivedParameter_.c_str();
+            via_addr_.host.slen = receivedParameter_.size();
+        }
+
+        std::string getReceivedParameter() const {
+            return receivedParameter_;
+        }
+
+        pjsip_host_port *
+        getViaAddr() {
+            return &via_addr_;
+        }
+
+        int getRPort() const {
+            if (rPort_ == -1)
+                return localPort_;
+            else
+                return rPort_;
+        }
+
+        void setRPort(int rPort) {
+            rPort_ = rPort;
+            via_addr_.port = rPort;
+        }
+
+        /* Returns true if the username and/or hostname match this account */
+        MatchRank matches(const std::string &username, const std::string &hostname) const;
+
+#ifdef SFL_PRESENCE
+        /**
+         * Presence management
+         */
+        //SIPPresence * getPresence() const;
+
+        /**
+         * Activate the module.
+         * @param function Publish or subscribe to enable
+         * @param enable Flag
+         */
+        void enablePresence(const bool& enable);
+        /**
+         * Activate the publish/subscribe.
+         * @param enable Flag
+         */
+        void supportPresence(int function, bool enable);
+#endif
+
+        /**
+         * Implementation of Account::newOutgoingCall()
+         * Note: keep declaration before newOutgoingCall template.
+         */
+        std::shared_ptr<Call> newOutgoingCall(const std::string& id, const std::string& toUrl);
+
+        /**
+         * Create outgoing SIPCall.
+         * @param[in] id The ID of the call
+         * @param[in] toUrl The address to call
+         * @return std::shared_ptr<T> A shared pointer on the created call.
+         *      The type of this instance is given in template argument.
+         *      This type can be any base class of SIPCall class (included).
+         */
+        template <class T=SIPCall>
+        std::shared_ptr<enable_if_base_of<T, SIPCall> >
+        newOutgoingCall(const std::string& id, const std::string& toUrl);
+
+        /**
+         * Create incoming SIPCall.
+         * @param[in] id The ID of the call
+         * @return std::shared_ptr<T> A shared pointer on the created call.
+         *      The type of this instance is given in template argument.
+         *      This type can be any base class of SIPCall class (included).
+         */
+        virtual std::shared_ptr<SIPCall>
+        newIncomingCall(const std::string& id);
+
+        virtual bool isTlsEnabled() const {
+            return true;
+        }
+
+        virtual bool getSrtpEnabled() const {
+            return true;
+        }
+
+        virtual std::string getSrtpKeyExchange() const {
+            return "sdes";
+        }
+
+        virtual bool getSrtpFallback() const {
+            return false;
+        }
+
+    private:
+        void createOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::string& to, const std::string& toUrl, const IpAddr& peer);
+
+        /**
+         * Set the internal state for this account, mainly used to manage account details from the client application.
+         * @param The map containing the account information.
+         */
+        virtual void setAccountDetails(const std::map<std::string, std::string> &details);
+
+        NON_COPYABLE(DHTAccount);
+
+        /**
+         * Start a SIP Call
+         * @param call  The current call
+         * @return true if all is correct
+         */
+        bool SIPStartCall(const std::shared_ptr<SIPCall>& call);
+
+        bool userMatch(const std::string &username) const;
+
+        /**
+         * @return pjsip_tls_setting structure, filled from the configuration
+         * file, that can be used directly by PJSIP to initialize
+         * TLS transport.
+         */
+        pjsip_tls_setting * getTlsSetting() {
+            return &tlsSetting_;
+        }
+
+        dht::DhtRunner dht_ {};
+
+        std::string nodePath_ {};
+        std::string privkeyPath_ {};
+        std::string certPath_ {};
+
+        /**
+         * If identityPath_ is a valid private key file (PEM or DER),
+         * load and returns it. Otherwise, generate one.
+         * Check if the given path contains a valid private key.
+         * @return the key if a valid private key exists there, nullptr otherwise.
+         */
+        dht::crypto::Identity loadIdentity() const;
+        void saveIdentity(const dht::crypto::Identity id) const;
+        void saveNodes(const std::vector<dht::Dht::NodeExport>& nodes) const;
+        std::vector<dht::Dht::NodeExport> loadNodes() const;
+
+        /**
+         * Initializes tls settings from configuration file.
+         */
+        void initTlsConfiguration();
+
+        /**
+         * PJSIP aborts if the string length of our cipher list is too
+         * great, so this function forces our cipher list to fit this constraint.
+         */
+        void trimCiphers();
+
+        /**
+         * The TLS settings, used only if tls is chosen as a sip transport.
+         */
+        pjsip_tls_setting tlsSetting_ {};
+
+        /**
+         * Allocate a vector to be used by pjsip to store the supported ciphers on this system.
+         */
+        CipherArray ciphers_ {};
+
+        /**
+         * Optional: "received" parameter from VIA header
+         */
+        std::string receivedParameter_ {};
+
+        /**
+         * Optional: "rport" parameter from VIA header
+         */
+        int rPort_ {-1};
+
+        /**
+         * Optional: via_addr construct from received parameters
+         */
+        pjsip_host_port via_addr_ {};
+
+        char contactBuffer_[PJSIP_MAX_URL_SIZE] {};
+        pj_str_t contact_ {contactBuffer_, 0};
+        pjsip_transport *via_tp_ {nullptr};
+
+};
+
+#endif
diff --git a/daemon/src/managerimpl.cpp b/daemon/src/managerimpl.cpp
index 9599a73ec8d0c0d4899aaa7ed350b8ca9e42308d..9296aa49c4dfe682121c6d231db95df2cde9039d 100644
--- a/daemon/src/managerimpl.cpp
+++ b/daemon/src/managerimpl.cpp
@@ -45,6 +45,9 @@
 #include "fileutils.h"
 #include "map_utils.h"
 #include "account.h"
+#if HAVE_DHT
+#include "dht/dhtaccount.h"
+#endif
 
 #include "call_factory.h"
 
@@ -2757,6 +2760,15 @@ ManagerImpl::newOutgoingCall(const std::string& id,
     std::shared_ptr<Account> account = Manager::instance().getIP2IPAccount();
     std::string finalToUrl = toUrl;
 
+#if HAVE_DHT
+    if (toUrl.find("dht:") != std::string::npos) {
+        WARN("DHT call detected");
+        auto dhtAcc = getAllAccounts<DHTAccount>();
+        if (not dhtAcc.empty())
+            return dhtAcc.front()->newOutgoingCall(id, finalToUrl);
+    }
+#endif
+
     // FIXME: have a generic version to remove sip dependency
     sip_utils::stripSipUriPrefix(finalToUrl);
 
diff --git a/daemon/src/sip/sipaccount.cpp b/daemon/src/sip/sipaccount.cpp
index 4f46cca231bc746f39076755d9b4cc2bea9400c3..b4baf150a7400d26d075de2a15bdb8b14dfa0762 100644
--- a/daemon/src/sip/sipaccount.cpp
+++ b/daemon/src/sip/sipaccount.cpp
@@ -75,6 +75,8 @@
 static const int MIN_REGISTRATION_TIME = 60;
 static const int DEFAULT_REGISTRATION_TIME = 3600;
 static const char *const VALID_TLS_METHODS[] = {"Default", "TLSv1", "SSLv3", "SSLv23"};
+static const char *const VALID_SRTP_KEY_EXCHANGES[] = {"", "sdes", "zrtp"};
+
 constexpr const char * const SIPAccount::ACCOUNT_TYPE;
 
 #if HAVE_TLS
@@ -407,19 +409,26 @@ void SIPAccount::serialize(YAML::Emitter &out)
 
     // tls submap
     out << YAML::Key << TLS_KEY << YAML::Value << YAML::BeginMap;
+    SIPAccountBase::serializeTls(out);
+    out << YAML::Key << TLS_ENABLE_KEY << YAML::Value << tlsEnable_;
+    out << YAML::Key << VERIFY_CLIENT_KEY << YAML::Value << tlsVerifyClient_;
+    out << YAML::Key << VERIFY_SERVER_KEY << YAML::Value << tlsVerifyServer_;
+    out << YAML::Key << REQUIRE_CERTIF_KEY << YAML::Value << tlsRequireClientCertificate_;
+    out << YAML::Key << TIMEOUT_KEY << YAML::Value << tlsNegotiationTimeoutSec_;
     out << YAML::Key << CALIST_KEY << YAML::Value << tlsCaListFile_;
     out << YAML::Key << CERTIFICATE_KEY << YAML::Value << tlsCertificateFile_;
     out << YAML::Key << CIPHERS_KEY << YAML::Value << tlsCiphers_;
-    out << YAML::Key << TLS_ENABLE_KEY << YAML::Value << tlsEnable_;
     out << YAML::Key << METHOD_KEY << YAML::Value << tlsMethod_;
     out << YAML::Key << TLS_PASSWORD_KEY << YAML::Value << tlsPassword_;
     out << YAML::Key << PRIVATE_KEY_KEY << YAML::Value << tlsPrivateKeyFile_;
-    out << YAML::Key << REQUIRE_CERTIF_KEY << YAML::Value << tlsRequireClientCertificate_;
     out << YAML::Key << SERVER_KEY << YAML::Value << tlsServerName_;
-    out << YAML::Key << TIMEOUT_KEY << YAML::Value << tlsNegotiationTimeoutSec_;
-    out << YAML::Key << TLS_PORT_KEY << YAML::Value << tlsListenerPort_;
-    out << YAML::Key << VERIFY_CLIENT_KEY << YAML::Value << tlsVerifyClient_;
-    out << YAML::Key << VERIFY_SERVER_KEY << YAML::Value << tlsVerifyServer_;
+    out << YAML::EndMap;
+
+    // srtp submap
+    out << YAML::Key << SRTP_KEY << YAML::Value << YAML::BeginMap;
+    out << YAML::Key << SRTP_ENABLE_KEY << YAML::Value << srtpEnabled_;
+    out << YAML::Key << KEY_EXCHANGE_KEY << YAML::Value << srtpKeyExchange_;
+    out << YAML::Key << RTP_FALLBACK_KEY << YAML::Value << srtpFallback_;
     out << YAML::EndMap;
 
     // zrtp submap
@@ -505,7 +514,6 @@ void SIPAccount::unserialize(const YAML::Node &node)
     const auto &tlsMap = node[TLS_KEY];
 
     parseValue(tlsMap, TLS_ENABLE_KEY, tlsEnable_);
-    parseValue(tlsMap, TLS_PORT_KEY, tlsListenerPort_);
     parseValue(tlsMap, CERTIFICATE_KEY, tlsCertificateFile_);
     parseValue(tlsMap, CALIST_KEY, tlsCaListFile_);
     parseValue(tlsMap, CIPHERS_KEY, tlsCiphers_);
@@ -516,13 +524,21 @@ void SIPAccount::unserialize(const YAML::Node &node)
 
     parseValue(tlsMap, TLS_PASSWORD_KEY, tlsPassword_);
     parseValue(tlsMap, PRIVATE_KEY_KEY, tlsPrivateKeyFile_);
-    parseValue(tlsMap, REQUIRE_CERTIF_KEY, tlsRequireClientCertificate_);
     parseValue(tlsMap, SERVER_KEY, tlsServerName_);
+    parseValue(tlsMap, REQUIRE_CERTIF_KEY, tlsRequireClientCertificate_);
     parseValue(tlsMap, VERIFY_CLIENT_KEY, tlsVerifyClient_);
     parseValue(tlsMap, VERIFY_SERVER_KEY, tlsVerifyServer_);
     // FIXME
     parseValue(tlsMap, TIMEOUT_KEY, tlsNegotiationTimeoutSec_);
 
+    // get srtp submap
+    const auto &srtpMap = node[SRTP_KEY];
+    parseValue(srtpMap, SRTP_ENABLE_KEY, srtpEnabled_);
+
+    std::string tmpKey;
+    parseValue(srtpMap, KEY_EXCHANGE_KEY, tmpKey);
+    validate(srtpKeyExchange_, tmpKey, VALID_SRTP_KEY_EXCHANGES);
+    parseValue(srtpMap, RTP_FALLBACK_KEY, srtpFallback_);
 }
 
 template <typename T>
@@ -584,6 +600,17 @@ void SIPAccount::setAccountDetails(const std::map<std::string, std::string> &det
     parseBool(details, CONFIG_TLS_VERIFY_CLIENT, tlsVerifyClient_);
     parseBool(details, CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, tlsRequireClientCertificate_);
     parseString(details, CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, tlsNegotiationTimeoutSec_);
+    parseBool(details, CONFIG_TLS_VERIFY_SERVER, tlsVerifyServer_);
+    parseBool(details, CONFIG_TLS_VERIFY_CLIENT, tlsVerifyClient_);
+    parseBool(details, CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, tlsRequireClientCertificate_);
+    parseString(details, CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, tlsNegotiationTimeoutSec_);
+
+    // srtp settings
+    parseBool(details, CONFIG_SRTP_ENABLE, srtpEnabled_);
+    parseBool(details, CONFIG_SRTP_RTP_FALLBACK, srtpFallback_);
+    iter = details.find(CONFIG_SRTP_KEY_EXCHANGE);
+    if (iter != details.end())
+        validate(srtpKeyExchange_, iter->second, VALID_SRTP_KEY_EXCHANGES);
 
     if (credentials_.empty()) { // credentials not set, construct 1 entry
         WARN("No credentials set, inferring them...");
@@ -664,15 +691,7 @@ std::map<std::string, std::string> SIPAccount::getAccountDetails() const
     a[CONFIG_STUN_SERVER] = stunServer_;
     a[CONFIG_KEEP_ALIVE_ENABLED] = keepAliveEnabled_ ? TRUE_STR : FALSE_STR;
 
-    a[CONFIG_ZRTP_DISPLAY_SAS] = zrtpDisplaySas_ ? TRUE_STR : FALSE_STR;
-    a[CONFIG_ZRTP_DISPLAY_SAS_ONCE] = zrtpDisplaySasOnce_ ? TRUE_STR : FALSE_STR;
-    a[CONFIG_ZRTP_HELLO_HASH] = zrtpHelloHash_ ? TRUE_STR : FALSE_STR;
-    a[CONFIG_ZRTP_NOT_SUPP_WARNING] = zrtpNotSuppWarning_ ? TRUE_STR : FALSE_STR;
-
     // TLS listener is unique and parameters are modified through IP2IP_PROFILE
-    std::stringstream tlslistenerport;
-    tlslistenerport << tlsListenerPort_;
-    a[CONFIG_TLS_LISTENER_PORT] = tlslistenerport.str();
     a[CONFIG_TLS_ENABLE] = tlsEnable_ ? TRUE_STR : FALSE_STR;
     a[CONFIG_TLS_CA_LIST_FILE] = tlsCaListFile_;
     a[CONFIG_TLS_CERTIFICATE_FILE] = tlsCertificateFile_;
@@ -686,6 +705,15 @@ std::map<std::string, std::string> SIPAccount::getAccountDetails() const
     a[CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE] = tlsRequireClientCertificate_ ? TRUE_STR : FALSE_STR;
     a[CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC] = tlsNegotiationTimeoutSec_;
 
+    a[CONFIG_SRTP_KEY_EXCHANGE] = srtpKeyExchange_;
+    a[CONFIG_SRTP_ENABLE] = srtpEnabled_ ? TRUE_STR : FALSE_STR;
+    a[CONFIG_SRTP_RTP_FALLBACK] = srtpFallback_ ? TRUE_STR : FALSE_STR;
+
+    a[CONFIG_ZRTP_DISPLAY_SAS] = zrtpDisplaySas_ ? TRUE_STR : FALSE_STR;
+    a[CONFIG_ZRTP_DISPLAY_SAS_ONCE] = zrtpDisplaySasOnce_ ? TRUE_STR : FALSE_STR;
+    a[CONFIG_ZRTP_HELLO_HASH] = zrtpHelloHash_ ? TRUE_STR : FALSE_STR;
+    a[CONFIG_ZRTP_NOT_SUPP_WARNING] = zrtpNotSuppWarning_ ? TRUE_STR : FALSE_STR;
+
     return a;
 }
 
diff --git a/daemon/src/sip/sipaccount.h b/daemon/src/sip/sipaccount.h
index 32650ca23901459282c2dca5ecf5ec3d9c88b085..b2eeb414e37fb188ca052cba8c2596144486e6ff 100644
--- a/daemon/src/sip/sipaccount.h
+++ b/daemon/src/sip/sipaccount.h
@@ -55,6 +55,19 @@ typedef std::vector<pj_ssl_cipher> CipherArray;
 
 namespace Conf {
     const char *const KEEP_ALIVE_ENABLED = "keepAlive";
+
+    // TODO: write an object to store credential which implement serializable
+    const char *const SRTP_KEY = "srtp";
+    const char *const SRTP_ENABLE_KEY = "enable";
+    const char *const KEY_EXCHANGE_KEY = "keyExchange";
+    const char *const RTP_FALLBACK_KEY = "rtpFallback";
+
+    // TODO: wirte an object to store zrtp params wich implement serializable
+    const char *const ZRTP_KEY = "zrtp";
+    const char *const DISPLAY_SAS_KEY = "displaySas";
+    const char *const DISPLAY_SAS_ONCE_KEY = "displaySasOnce";
+    const char *const HELLO_HASH_ENABLED_KEY = "helloHashEnabled";
+    const char *const NOT_SUPP_WARNING_KEY = "notSuppWarning";
 }
 
 namespace YAML {
@@ -309,14 +322,6 @@ class SIPAccount : public SIPAccountBase {
             return stunPort_;
         }
 
-        /**
-         * @return bool Tells if current transport for that
-         * account is set to TLS.
-         */
-        bool isTlsEnabled() const {
-            return tlsEnable_;
-        }
-
         /**
          * @return bool Tells if current transport for that
          * account is set to OTHER.
@@ -369,6 +374,21 @@ class SIPAccount : public SIPAccountBase {
 
         bool hasServiceRoute() const { return not serviceRoute_.empty(); }
 
+        virtual bool isTlsEnabled() const {
+            return tlsEnable_;
+        }
+
+        virtual bool getSrtpEnabled() const {
+            return srtpEnabled_;
+        }
+
+        virtual std::string getSrtpKeyExchange() const {
+            return srtpKeyExchange_;
+        }
+
+        virtual bool getSrtpFallback() const {
+            return srtpFallback_;
+        }
 
         bool getZrtpHelloHash() const {
             return zrtpHelloHash_;
@@ -605,11 +625,7 @@ class SIPAccount : public SIPAccountBase {
          */
         pj_uint16_t stunPort_ {PJ_STUN_PORT};
 
-
-
-        /**
-         * Certificate autority file
-         */
+        bool tlsEnable_ {false};
         std::string tlsCaListFile_;
         std::string tlsCertificateFile_;
         std::string tlsPrivateKeyFile_;
@@ -622,6 +638,25 @@ class SIPAccount : public SIPAccountBase {
         bool tlsRequireClientCertificate_;
         std::string tlsNegotiationTimeoutSec_;
 
+        /**
+         * Determine if SRTP is enabled for this account, SRTP and ZRTP are mutually exclusive
+         * This only determine if the media channel is secured. One could only enable TLS
+         * with no secured media channel.
+         */
+        bool srtpEnabled_ {false};
+
+        /**
+         * Specifies the type of key exchange usd for SRTP (sdes/zrtp)
+         */
+        std::string srtpKeyExchange_ {""};
+
+        /**
+         * Determine if the softphone should fallback on non secured media channel if SRTP negotiation fails.
+         * Make sure other SIP endpoints share the same behavior since it could result in encrypted data to be
+         * played through the audio device.
+         */
+        bool srtpFallback_ {};
+
         /**
          * Determine if the SAS sould be displayed on client side. SAS is a 4-charcter string
          * that end users should verbaly validate to ensure the channel is secured. Used especially
diff --git a/daemon/src/sip/sipaccountbase.cpp b/daemon/src/sip/sipaccountbase.cpp
index df82054e1392cb1a2ed124016c36aaace86c3ec3..ed763f537165ff2f8411db38f2a580498c7b8969 100644
--- a/daemon/src/sip/sipaccountbase.cpp
+++ b/daemon/src/sip/sipaccountbase.cpp
@@ -44,8 +44,6 @@
 
 bool SIPAccountBase::portsInUse_[HALF_MAX_PORT];
 
-static const char *const VALID_SRTP_KEY_EXCHANGES[] = {"", "sdes", "zrtp"};
-
 SIPAccountBase::SIPAccountBase(const std::string& accountID)
     : Account(accountID), link_(getSIPVoIPLink())
 {}
@@ -108,19 +106,17 @@ void SIPAccountBase::serialize(YAML::Emitter &out)
     out << YAML::Key << PUBLISH_PORT_KEY << YAML::Value << publishedPort_;
     out << YAML::Key << SAME_AS_LOCAL_KEY << YAML::Value << publishedSameasLocal_;
 
-    // srtp submap
-    out << YAML::Key << SRTP_KEY << YAML::Value << YAML::BeginMap;
-    out << YAML::Key << SRTP_ENABLE_KEY << YAML::Value << srtpEnabled_;
-    out << YAML::Key << KEY_EXCHANGE_KEY << YAML::Value << srtpKeyExchange_;
-    out << YAML::Key << RTP_FALLBACK_KEY << YAML::Value << srtpFallback_;
-    out << YAML::EndMap;
-
     out << YAML::Key << VIDEO_CODECS_KEY << YAML::Value << videoCodecList_;
     out << YAML::Key << VIDEO_ENABLED_KEY << YAML::Value << videoEnabled_;
     out << YAML::Key << VIDEO_PORT_MAX_KEY << YAML::Value << videoPortRange_.second;
     out << YAML::Key << VIDEO_PORT_MIN_KEY << YAML::Value << videoPortRange_.first;
 }
 
+void SIPAccountBase::serializeTls(YAML::Emitter &out)
+{
+    using namespace Conf;
+    out << YAML::Key << TLS_PORT_KEY << YAML::Value << tlsListenerPort_;
+}
 
 void SIPAccountBase::unserialize(const YAML::Node &node)
 {
@@ -153,14 +149,9 @@ void SIPAccountBase::unserialize(const YAML::Node &node)
 
     parseValue(node, DTMF_TYPE_KEY, dtmfType_);
 
-    // get srtp submap
-    const auto &srtpMap = node[SRTP_KEY];
-    parseValue(srtpMap, SRTP_ENABLE_KEY, srtpEnabled_);
-
-    std::string tmpKey;
-    parseValue(srtpMap, KEY_EXCHANGE_KEY, tmpKey);
-    validate(srtpKeyExchange_, tmpKey, VALID_SRTP_KEY_EXCHANGES);
-    parseValue(srtpMap, RTP_FALLBACK_KEY, srtpFallback_);
+    // get tls submap
+    const auto &tlsMap = node[TLS_KEY];
+    parseValue(tlsMap, TLS_PORT_KEY, tlsListenerPort_);
 
     unserializeRange(node, AUDIO_PORT_MIN_KEY, AUDIO_PORT_MAX_KEY, audioPortRange_);
     unserializeRange(node, VIDEO_PORT_MIN_KEY, VIDEO_PORT_MAX_KEY, videoPortRange_);
@@ -195,12 +186,8 @@ void SIPAccountBase::setAccountDetails(const std::map<std::string, std::string>
     updateRange(tmpMin, tmpMax, videoPortRange_);
 #endif
 
-    // srtp settings
-    parseBool(details, CONFIG_SRTP_ENABLE, srtpEnabled_);
-    parseBool(details, CONFIG_SRTP_RTP_FALLBACK, srtpFallback_);
-    auto iter = details.find(CONFIG_SRTP_KEY_EXCHANGE);
-    if (iter != details.end())
-        validate(srtpKeyExchange_, iter->second, VALID_SRTP_KEY_EXCHANGES);
+    // TLS
+    parseInt(details, CONFIG_TLS_LISTENER_PORT, tlsListenerPort_);
 }
 
 std::map<std::string, std::string>
@@ -231,9 +218,9 @@ SIPAccountBase::getAccountDetails() const
     publishedport << publishedPort_;
     a[CONFIG_PUBLISHED_PORT] = publishedport.str();
 
-    a[CONFIG_SRTP_KEY_EXCHANGE] = srtpKeyExchange_;
-    a[CONFIG_SRTP_ENABLE] = srtpEnabled_ ? TRUE_STR : FALSE_STR;
-    a[CONFIG_SRTP_RTP_FALLBACK] = srtpFallback_ ? TRUE_STR : FALSE_STR;
+    std::stringstream tlslistenerport;
+    tlslistenerport << tlsListenerPort_;
+    a[CONFIG_TLS_LISTENER_PORT] = tlslistenerport.str();
     return a;
 }
 
diff --git a/daemon/src/sip/sipaccountbase.h b/daemon/src/sip/sipaccountbase.h
index 50993be61ad1b94402a9d834dd6214a2ac3d33c6..c279a57f1d22966c0c390ccbd4423a9cd1bc69b9 100644
--- a/daemon/src/sip/sipaccountbase.h
+++ b/daemon/src/sip/sipaccountbase.h
@@ -66,19 +66,6 @@ namespace Conf {
     const char *const PRESENCE_STATUS_KEY = "presenceStatus";
     const char *const PRESENCE_NOTE_KEY = "presenceNote";
 
-    // TODO: write an object to store credential which implement serializable
-    const char *const SRTP_KEY = "srtp";
-    const char *const SRTP_ENABLE_KEY = "enable";
-    const char *const KEY_EXCHANGE_KEY = "keyExchange";
-    const char *const RTP_FALLBACK_KEY = "rtpFallback";
-
-    // TODO: wirte an object to store zrtp params wich implement serializable
-    const char *const ZRTP_KEY = "zrtp";
-    const char *const DISPLAY_SAS_KEY = "displaySas";
-    const char *const DISPLAY_SAS_ONCE_KEY = "displaySasOnce";
-    const char *const HELLO_HASH_ENABLED_KEY = "helloHashEnabled";
-    const char *const NOT_SUPP_WARNING_KEY = "notSuppWarning";
-
     // TODO: write an object to store tls params which implement serializable
     const char *const TLS_KEY = "tls";
     const char *const TLS_PORT_KEY = "tlsPort";
@@ -152,8 +139,12 @@ public:
         return dtmfType_;
     }
 
-    bool isTlsEnabled() const {
-        return tlsEnable_;
+    /**
+     * Determine if TLS is enabled for this account. TLS provides a secured channel for
+     * SIP signalization. It is independant than the media encription provided by SRTP or ZRTP.
+     */
+    virtual bool isTlsEnabled() const {
+        return false;
     }
 
     virtual pjsip_tls_setting * getTlsSetting() {
@@ -238,17 +229,13 @@ public:
         publishedPort_ = port;
     }
 
-    bool getSrtpEnabled() const {
-        return srtpEnabled_;
+    virtual bool getSrtpEnabled() const {
+        return false;
     }
 
-    std::string getSrtpKeyExchange() const {
-        return srtpKeyExchange_;
-    }
+    virtual std::string getSrtpKeyExchange() const = 0;
 
-    bool getSrtpFallback() const {
-        return srtpFallback_;
-    }
+    virtual bool getSrtpFallback() const = 0;
 
     /**
      * Get the contact header for
@@ -287,6 +274,7 @@ public:
 
 protected:
     virtual void serialize(YAML::Emitter &out);
+    virtual void serializeTls(YAML::Emitter &out);
     virtual void unserialize(const YAML::Node &node);
 
     virtual void setAccountDetails(const std::map<std::string, std::string> &details);
@@ -360,31 +348,6 @@ protected:
      */
     std::string dtmfType_ {OVERRTP_STR};
 
-    /**
-     * Determine if TLS is enabled for this account. TLS provides a secured channel for
-     * SIP signalization. It is independant than the media encription provided by SRTP or ZRTP.
-     */
-    bool tlsEnable_ {false};
-
-    /**
-     * Determine if SRTP is enabled for this account, SRTP and ZRTP are mutually exclusive
-     * This only determine if the media channel is secured. One could only enable TLS
-     * with no secured media channel.
-     */
-    bool srtpEnabled_ {false};
-
-    /**
-     * Specifies the type of key exchange usd for SRTP (sdes/zrtp)
-     */
-    std::string srtpKeyExchange_ {""};
-
-    /**
-     * Determine if the softphone should fallback on non secured media channel if SRTP negotiation fails.
-     * Make sure other SIP endpoints share the same behavior since it could result in encrypted data to be
-     * played through the audio device.
-     */
-    bool srtpFallback_ {};
-
     pj_status_t transportStatus_ {PJSIP_SC_TRYING};
     std::string transportError_ {};
 
diff --git a/daemon/src/sip/sipvoiplink.cpp b/daemon/src/sip/sipvoiplink.cpp
index 2e1e39284a9842c83b59d0b557aa3d1edfdabf11..d40792131d4a5e4f65ad27950e919f4b73036eef 100644
--- a/daemon/src/sip/sipvoiplink.cpp
+++ b/daemon/src/sip/sipvoiplink.cpp
@@ -43,6 +43,8 @@
 #include "sipaccount.h"
 #include "sip_utils.h"
 
+#include "dht/dhtaccount.h"
+
 #include "call_factory.h"
 
 #include "manager.h"
@@ -231,13 +233,13 @@ transaction_request_cb(pjsip_rx_data *rdata)
     const std::string remote_user(sip_from_uri->user.ptr, sip_from_uri->user.slen);
     const std::string remote_hostname(sip_from_uri->host.ptr, sip_from_uri->host.slen);
 
-    auto sipaccount(getSIPVoIPLink()->guessAccount(toUsername, viaHostname, remote_hostname));
-    if (!sipaccount) {
+    auto account(getSIPVoIPLink()->guessAccount(toUsername, viaHostname, remote_hostname));
+    if (!account) {
         ERROR("NULL account");
         return PJ_FALSE;
     }
 
-    const auto& account_id = sipaccount->getAccountID();
+    const auto& account_id = account->getAccountID();
     std::string displayName(sip_utils::parseDisplayName(rdata->msg_info.msg_buf));
     pjsip_msg_body *body = rdata->msg_info.msg->body;
 
@@ -271,7 +273,7 @@ transaction_request_cb(pjsip_rx_data *rdata)
     if (!body || pjmedia_sdp_parse(rdata->tp_info.pool, (char*) body->data, body->len, &r_sdp) != PJ_SUCCESS)
         r_sdp = NULL;
 
-    if (sipaccount->getActiveAudioCodecs().empty()) {
+    if (account->getActiveAudioCodecs().empty()) {
         try_respond_stateless(endpt_, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL, NULL, NULL);
 
         return PJ_FALSE;
@@ -287,19 +289,19 @@ transaction_request_cb(pjsip_rx_data *rdata)
 
     Manager::instance().hookPreference.runHook(rdata->msg_info.msg);
 
-    auto call = sipaccount->newIncomingCall(Manager::instance().getNewCallID());
+    auto call = account->newIncomingCall(Manager::instance().getNewCallID());
 
     // FIXME : for now, use the same address family as the SIP transport
-    auto family = pjsip_transport_type_get_af(sipaccount->getTransportType());
-    IpAddr addrToUse = ip_utils::getInterfaceAddr(sipaccount->getLocalInterface(), family);
+    auto family = pjsip_transport_type_get_af(account->getTransportType());
+    IpAddr addrToUse = ip_utils::getInterfaceAddr(account->getLocalInterface(), family);
 
     // May use the published address as well
-    IpAddr addrSdp = sipaccount->isStunEnabled() or (not sipaccount->getPublishedSameasLocal())
-                    ? sipaccount->getPublishedIpAddress() : addrToUse;
+    IpAddr addrSdp = account->isStunEnabled() or (not account->getPublishedSameasLocal())
+                    ? account->getPublishedIpAddress() : addrToUse;
 
     char tmp[PJSIP_MAX_URL_SIZE];
     size_t length = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, sip_from_uri, tmp, PJSIP_MAX_URL_SIZE);
-    std::string peerNumber(tmp, std::min(length, sizeof tmp));
+    std::string peerNumber(tmp, length);
     sip_utils::stripSipUriPrefix(peerNumber);
 
     if (not remote_user.empty() and not remote_hostname.empty())
@@ -310,7 +312,7 @@ transaction_request_cb(pjsip_rx_data *rdata)
 
     auto transport = getSIPVoIPLink()->sipTransport->findTransport(rdata->tp_info.transport);
     if (!transport) {
-        transport = sipaccount->getTransport();
+        transport = account->getTransport();
         if (!transport) {
             ERROR("No suitable transport to answer this call.");
             return PJ_FALSE;
@@ -335,7 +337,7 @@ transaction_request_cb(pjsip_rx_data *rdata)
         return PJ_FALSE;
     }
 
-    if (sipaccount->isStunEnabled())
+    if (account->isStunEnabled())
         call->updateSDPFromSTUN();
 
     if (body and body->len > 0 and call->getAudioRtp().isSdesEnabled()) {
@@ -370,7 +372,7 @@ transaction_request_cb(pjsip_rx_data *rdata)
         }
     }
 
-    call->getLocalSDP().receiveOffer(r_sdp, sipaccount->getActiveAudioCodecs(), sipaccount->getActiveVideoCodecs());
+    call->getLocalSDP().receiveOffer(r_sdp, account->getActiveAudioCodecs(), account->getActiveVideoCodecs());
 
     sfl::AudioCodec* ac = Manager::instance().audioCodecFactory.instantiateCodec(PAYLOAD_CODEC_ULAW);
 
@@ -468,7 +470,7 @@ transaction_request_cb(pjsip_rx_data *rdata)
         }
 
         // contactStr must stay in scope as long as tdata
-        const pj_str_t contactStr(sipaccount->getContactHeader());
+        const pj_str_t contactStr(account->getContactHeader());
         sip_utils::addContactHeader(&contactStr, tdata);
 
         if (pjsip_inv_send_msg(call->inv.get(), tdata) != PJ_SUCCESS) {
@@ -636,18 +638,33 @@ SIPVoIPLink::guessAccount(const std::string& userName,
     auto result = std::static_pointer_cast<SIPAccountBase>(Manager::instance().getIP2IPAccount()); // default result
     MatchRank best = MatchRank::NONE;
 
-    for (const auto& sipaccount : Manager::instance().getAllAccounts<SIPAccount>()) {
-        if (!sipaccount)
+    // DHT accounts
+    for (const auto& account : Manager::instance().getAllAccounts<DHTAccount>()) {
+        if (!account)
             continue;
+        const MatchRank match(account->matches(userName, server));
 
-        const MatchRank match(sipaccount->matches(userName, server, endpt_, pool_));
+        // return right away if this is a full match
+        if (match == MatchRank::FULL) {
+            return account;
+        } else if (match > best) {
+            best = match;
+            result = account;
+        }
+    }
+
+    // SIP accounts
+    for (const auto& account : Manager::instance().getAllAccounts<SIPAccount>()) {
+        if (!account)
+            continue;
+        const MatchRank match(account->matches(userName, server, endpt_, pool_));
 
         // return right away if this is a full match
         if (match == MatchRank::FULL) {
-            return std::static_pointer_cast<SIPAccountBase>(sipaccount);
+            return account;
         } else if (match > best) {
             best = match;
-            result = sipaccount;
+            result = account;
         }
     }
 
@@ -820,7 +837,7 @@ invite_session_state_changed_cb(pjsip_inv_session *inv, pjsip_event *ev)
         // After we sent or received a ACK - The connection is established
         call->onAnswered();
     } else if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
-        std::string accId(call->getAccountId());
+        //std::string accId(call->getAccountId());
 
         switch (inv->cause) {
                 // The call terminates normally - BYE / CANCEL