diff --git a/daemon/contrib/src/opendht/rules.mak b/daemon/contrib/src/opendht/rules.mak
index 475576169faeea87f265f5a66857aa0b5cb8a8bd..d5877138b54883d2aac836f5d126ab0fc20c833a 100644
--- a/daemon/contrib/src/opendht/rules.mak
+++ b/daemon/contrib/src/opendht/rules.mak
@@ -1,6 +1,6 @@
 # OPENDHT
-OPENDHT_VERSION := 1789a5680e1e50bc593b9aa715a7f0a655d044fe
-OPENDHT_URL := https://github.com/aberaud/opendht/archive/$(OPENDHT_VERSION).tar.gz
+OPENDHT_VERSION := fde26fef5b2dfbcb5960fb91a75f86a83b5b1f3c
+OPENDHT_URL := https://github.com/savoirfairelinux/opendht/archive/$(OPENDHT_VERSION).tar.gz
 
 PKGS += opendht
 ifeq ($(call need_pkg,'opendht'),)
diff --git a/daemon/contrib/src/pjproject/rules.mak b/daemon/contrib/src/pjproject/rules.mak
index 947bf8e44101d12d139576968e8ef229be548bf0..a4e1d79618d8c38c899975841218767195608d0f 100644
--- a/daemon/contrib/src/pjproject/rules.mak
+++ b/daemon/contrib/src/pjproject/rules.mak
@@ -24,7 +24,7 @@ ifdef HAVE_ANDROID
 PJPROJECT_OPTIONS += --with-ssl=$(PREFIX)
 endif
 
-PJPROJECT_EXTRA_CFLAGS = -DPJ_ICE_MAX_CHECKS=150
+PJPROJECT_EXTRA_CFLAGS = -DPJ_ICE_MAX_CHECKS=150 -DPJ_ICE_COMP_BITS=2
 
 PKGS += pjproject
 # FIXME: nominally 2.2.0 is enough, but it has to be patched for gnutls
diff --git a/daemon/src/ringdht/ringaccount.cpp b/daemon/src/ringdht/ringaccount.cpp
index ad06cfe858e6fee078b5be7d3a17c4fa4a9da78f..32f807ddb53a7963ab308b37949d697b64bf4d74 100644
--- a/daemon/src/ringdht/ringaccount.cpp
+++ b/daemon/src/ringdht/ringaccount.cpp
@@ -40,6 +40,9 @@
 #include "sip/sipcall.h"
 #include "sip/sip_utils.h"
 
+#include "sip_transport_ice.h"
+#include "ice_transport.h"
+
 #include <opendht/securedht.h>
 
 #include "array_size.h"
@@ -69,13 +72,18 @@
 #include <sstream>
 #include <cstdlib>
 
+static constexpr int ICE_COMPONENTS {5};
+static constexpr int ICE_COMP_SIP_TRANSPORT {4};
+static constexpr int ICE_INIT_TIMEOUT {5};
+static constexpr int ICE_NEGOTIATION_TIMEOUT {60};
+
 constexpr const char * const RingAccount::ACCOUNT_TYPE;
 
 RingAccount::RingAccount(const std::string& accountID, bool /* presenceEnabled */)
     : SIPAccountBase(accountID), tlsSetting_(), via_addr_()
 {
     fileutils::check_dir(fileutils::get_cache_dir().c_str());
-    nodePath_ = fileutils::get_cache_dir()+DIR_SEPARATOR_STR+getAccountID();
+    cachePath_ = fileutils::get_cache_dir()+DIR_SEPARATOR_STR+getAccountID();
 
     /*  ~/.local/{appname}    */
     fileutils::check_dir(fileutils::get_data_dir().c_str());
@@ -104,71 +112,113 @@ RingAccount::~RingAccount()
 }
 
 std::shared_ptr<SIPCall>
-RingAccount::newIncomingCall(const std::string& id)
+RingAccount::newIncomingCall(const std::string& from)
 {
-    return Manager::instance().callFactory.newCall<SIPCall, RingAccount>(*this, id, Call::INCOMING);
+    for (auto& c : pendingCalls_) {
+        if (c->getPeerNumber() == from) {
+            SFL_WARN("Found matching call for %s", from.c_str());
+            return c;
+        }
+    }
+    SFL_ERR("Can't find matching call for %s", from.c_str());
+    return nullptr;
 }
 
 template <>
 std::shared_ptr<SIPCall>
 RingAccount::newOutgoingCall(const std::string& id, const std::string& toUrl)
 {
-    auto call = Manager::instance().callFactory.newCall<SIPCall, RingAccount>(*this, id, Call::OUTGOING);
     auto dhtf = toUrl.find("ring:");
     dhtf = (dhtf == std::string::npos) ? 0 : dhtf+5;
+    if (toUrl.length() - dhtf < 40)
+        throw std::invalid_argument("id must be a ring infohash");
     const std::string toUri = toUrl.substr(dhtf, 40);
     SFL_DBG("Calling DHT peer %s", toUri.c_str());
+
+    auto call = Manager::instance().callFactory.newCall<SIPCall, RingAccount>(*this, id, Call::OUTGOING);
     call->setIPToIP(true);
+    call->initIceTransport(true, ICE_COMPONENTS);
+    call->waitForIceInitialization(ICE_INIT_TIMEOUT);
+    auto ice = call->getIceTransport();
+    if (not ice or not ice->isInitialized()) {
+        call->setConnectionState(Call::DISCONNECTED);
+        call->setState(Call::MERROR);
+        return call;
+    }
 
-    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) {
-                SFL_DBG("Resolved value : %s", v->toString().c_str());
-                IpAddr peer;
-                try {
-                    peer = IpAddr{ dht::ServiceAnnouncement(v->data).getPeerAddr() };
-                } catch (const std::exception& e) {
-                    SFL_ERR("Exception while reading value : %s", e.what());
+    call->setConnectionState(Call::TRYING);
+
+    auto shared = shared_from_this();
+    const auto callkey = dht::InfoHash::get("callto:"+toUri);
+
+    const dht::Value::Id callvid  = std::uniform_int_distribution<dht::Value::Id>{}(rand_);
+    const dht::Value::Id replyvid = callvid+1;
+    dht_.putEncrypted(
+        callkey,
+        dht::InfoHash(toUri),
+        dht::Value {
+            ICE_ANNOUCEMENT_TYPE.id,
+            ice->getLocalAttributesAndCandidates(),
+            callvid
+        },
+        [callkey, callvid, call, shared](bool ok){
+            auto& this_ = *std::static_pointer_cast<RingAccount>(shared).get();
+            this_.dht_.cancelPut(callkey, callvid);
+            if (ok)
+                call->setConnectionState(Call::PROGRESSING);
+            else
+                call->setConnectionState(Call::DISCONNECTED);
+        }
+    );
+
+    dht_.listen(
+        callkey,
+        [shared, call, callkey, ice, toUri, replyvid] (const std::vector<std::shared_ptr<dht::Value>>& vals) {
+            SFL_WARN("Got a DHT reply from %s !", toUri.c_str());
+            auto& this_ = *std::static_pointer_cast<RingAccount>(shared).get();
+            for (const auto& v : vals) {
+                if (v->recipient != this_.dht_.getId() || v->type != this_.ICE_ANNOUCEMENT_TYPE.id) {
+                    SFL_WARN("Ignoring non encrypted or bad type value %s.", v->toString().c_str());
                     continue;
                 }
-                *resolved = true;
-                std::string toip = getToUri(toUri+"@"+peer.toString(true, true));
-                SFL_DBG("Got DHT peer IP: %s", toip.c_str());
-                createOutgoingCall(call, toUri, toip, peer);
+                if (v->id != replyvid) {
+                    SFL_WARN("Ignoring value ID %llx (expected %llx)", v->id, replyvid);
+                    continue;
+                }
+                SFL_WARN("Performing ICE negotiation.");
+                ice->start(v->data);
+                if (call->waitForIceNegotiation(ICE_NEGOTIATION_TIMEOUT) <= 0) {
+                    call->setConnectionState(Call::DISCONNECTED);
+                    return false;
+                }
+                call->setConnectionState(Call::PROGRESSING);
+                this_.createOutgoingCall(call, toUri, ice->getRemoteAddress(0));
                 return false;
             }
             return true;
-        },
-        [call,resolved] (bool /*ok*/){
-            if (not *resolved) {
-                call->setConnectionState(Call::DISCONNECTED);
-                call->setState(Call::MERROR);
-            }
-        },
-        dht::Value::TypeFilter(dht::ServiceAnnouncement::TYPE));
+        }
+    );
+
     return call;
 }
 
 void
-RingAccount::createOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::string& to, const std::string& toUri, const IpAddr& peer)
+RingAccount::createOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::string& to_id, IpAddr target)
 {
-    SFL_WARN("RingAccount::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)));
+    SFL_WARN("RingAccount::createOutgoingCall to: %s target: %s tlsListener: %d", to_id.c_str(), target.toString(true).c_str(), tlsListener_?1:0);
+    auto t = link_->sipTransport->getIceTransport(call->getIceTransport(), ICE_COMP_SIP_TRANSPORT);
     setTransport(t);
     call->setTransport(t);
     call->setIPToIP(true);
-    call->setPeerNumber(toUri);
-    call->initRecFilename(to);
+    call->setPeerNumber(getToUri(to_id+"@"+target.toString(true).c_str()));
+    call->initRecFilename(to_id);
 
-    const auto localAddress = ip_utils::getInterfaceAddr(getLocalInterface(), peer.getFamily());
-    call->setCallMediaLocal(localAddress);
+    //const auto localAddress = ip_utils::getInterfaceAddr(getLocalInterface(), peer.getFamily());
+    call->setCallMediaLocal(call->getIceTransport()->getDefaultLocalAddress());
 
     // May use the published address as well
-    const auto addrSdp = isStunEnabled() or (not getPublishedSameasLocal()) ?
-        getPublishedIpAddress() : localAddress;
+    //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
@@ -198,14 +248,15 @@ RingAccount::createOutgoingCall(const std::shared_ptr<SIPCall>& call, const std:
     // Building the local SDP offer
     auto& sdp = call->getSDP();
 
-    if (getPublishedSameasLocal())
+    /*if (getPublishedSameasLocal())
         sdp.setPublishedIP(addrSdp);
     else
         sdp.setPublishedIP(getPublishedAddress());
-
+*/
+    sdp.setPublishedIP(ip_utils::getInterfaceAddr(getLocalInterface()));
     const bool created = sdp.createOffer(getActiveAudioCodecs(), getActiveVideoCodecs());
 
-    if (not created or not SIPStartCall(call))
+    if (not created or not SIPStartCall(call, target))
         throw VoipLinkException("Could not send outgoing INVITE request for new call");
 }
 
@@ -216,7 +267,7 @@ RingAccount::newOutgoingCall(const std::string& id, const std::string& toUrl)
 }
 
 bool
-RingAccount::SIPStartCall(const std::shared_ptr<SIPCall>& call)
+RingAccount::SIPStartCall(const std::shared_ptr<SIPCall>& call, IpAddr target)
 {
     std::string toUri(call->getPeerNumber()); // expecting a fully well formed sip uri
 
@@ -226,19 +277,20 @@ RingAccount::SIPStartCall(const std::shared_ptr<SIPCall>& call)
     std::string from(getFromUri());
     pj_str_t pjFrom = pj_str((char*) from.c_str());
 
+    std::string targetStr = getToUri(target.toString(true)/*+";transport=ICE"*/);
+    pj_str_t pjTarget = pj_str((char*) targetStr.c_str());
+
     pj_str_t pjContact;
     {
         auto transport = call->getTransport();
         pjContact = getContactHeader(transport ? transport->get() : nullptr);
     }
 
-    const std::string debugContactHeader(pj_strbuf(&pjContact), pj_strlen(&pjContact));
-    SFL_DBG("contact header: %s / %s -> %s",
-          debugContactHeader.c_str(), from.c_str(), toUri.c_str());
+    SFL_DBG("contact header: %.*s / %s -> %s / %.*s",
+          pjContact.slen, pjContact.ptr, from.c_str(), toUri.c_str(), pjTarget.slen, pjTarget.ptr);
 
     pjsip_dialog *dialog = NULL;
-
-    if (pjsip_dlg_create_uac(pjsip_ua_instance(), &pjFrom, &pjContact, &pjTo, NULL, &dialog) != PJ_SUCCESS) {
+    if (pjsip_dlg_create_uac(pjsip_ua_instance(), &pjFrom, &pjContact, &pjTo, &pjTarget, &dialog) != PJ_SUCCESS) {
         SFL_ERR("Unable to create SIP dialogs for user agent client when "
               "calling %s", toUri.c_str());
         return false;
@@ -276,7 +328,8 @@ RingAccount::SIPStartCall(const std::shared_ptr<SIPCall>& call)
         return false;
     }
 
-    const pjsip_tpselector tp_sel = getTransportSelector();
+    //const pjsip_tpselector tp_sel = getTransportSelector();
+    const pjsip_tpselector tp_sel = {PJSIP_TPSELECTOR_TRANSPORT, {call->getTransport()->get()}};
     if (pjsip_dlg_set_transport(dialog, &tp_sel) != PJ_SUCCESS) {
         SFL_ERR("Unable to associate transport for invite session dialog");
         return false;
@@ -333,7 +386,7 @@ RingAccount::checkIdentityPath()
         return;
 
     const auto idPath = fileutils::get_data_dir()+DIR_SEPARATOR_STR+getAccountID();
-    privkeyPath_ = idPath + DIR_SEPARATOR_STR "dht.pem";
+    privkeyPath_ = idPath + DIR_SEPARATOR_STR "dht.key";
     certPath_ = idPath + DIR_SEPARATOR_STR "dht.crt";
     cacertPath_ = idPath + DIR_SEPARATOR_STR "ca.crt";
 }
@@ -398,7 +451,7 @@ RingAccount::loadIdentity()
 
         saveIdentity(id, idPath_ + DIR_SEPARATOR_STR "dht");
         certPath_ = idPath_ + DIR_SEPARATOR_STR "dht.crt";
-        privkeyPath_ = idPath_ + DIR_SEPARATOR_STR "dht.pem";
+        privkeyPath_ = idPath_ + DIR_SEPARATOR_STR "dht.key";
 
         return {ca.second, id};
     }
@@ -416,7 +469,7 @@ void
 RingAccount::saveIdentity(const dht::crypto::Identity id, const std::string& path) const
 {
     if (id.first)
-        fileutils::saveFile(path + ".pem", id.first->serialize());
+        fileutils::saveFile(path + ".key", id.first->serialize());
     if (id.second)
         fileutils::saveFile(path + ".crt", id.second->getPacked());
 }
@@ -464,6 +517,7 @@ void RingAccount::doRegister()
     }
 
     try {
+        loadTreatedCalls();
         if (dht_.isRunning()) {
             SFL_ERR("DHT already running (stopping it first).");
             dht_.join();
@@ -476,7 +530,7 @@ void RingAccount::doRegister()
             case dht::Dht::Status::Connecting:
             case dht::Dht::Status::Connected:
                 setRegistrationState(status == dht::Dht::Status::Connected ? RegistrationState::REGISTERED : RegistrationState::TRYING);
-                if (!tlsListener_) {
+                /*if (!tlsListener_) {
                     initTlsConfiguration();
                     tlsListener_ = link_->sipTransport->getTlsListener(
                         SipTransportDescr {getTransportType(), getTlsListenerPort(), getLocalInterface()},
@@ -486,7 +540,7 @@ void RingAccount::doRegister()
                         SFL_ERR("Error creating TLS listener.");
                         return;
                     }
-                }
+                }*/
                 break;
             case dht::Dht::Status::Disconnected:
             default:
@@ -496,11 +550,13 @@ void RingAccount::doRegister()
             }
         });
 
+#if 0
         dht_.setLoggers(
             [](char const* m, va_list args){ vlogger(LOG_ERR, m, args); },
             [](char const* m, va_list args){ vlogger(LOG_WARNING, m, args); },
             [](char const* m, va_list args){ vlogger(LOG_DEBUG, m, args); }
         );
+#endif
 
         dht_.registerType(USER_PROFILE_TYPE);
         dht_.registerType(ICE_ANNOUCEMENT_TYPE);
@@ -510,15 +566,8 @@ void RingAccount::doRegister()
         // Publish our own CA
         dht_.put(identity.first->getPublicKey().getId(), dht::Value {
             dht::CERTIFICATE_TYPE,
-            *identity.first
-        });
-
-        // Publish the SIP service announcement
-        dht_.put(dht_.getId(), dht::Value {
-            dht::ServiceAnnouncement::TYPE,
-            dht::ServiceAnnouncement(getTlsListenerPort())
-        }, [](bool ok) {
-            SFL_DBG("Peer announce callback ! %d", ok);
+            *identity.first,
+            1
         });
 
         username_ = dht_.getId().toString();
@@ -551,6 +600,76 @@ void RingAccount::doRegister()
                 SFL_DBG("Bootstrap node: %s", IpAddr(ip).toString(true).c_str());
             dht_.bootstrap(bootstrap);
         }
+
+        // Listen for incoming calls
+        auto shared = shared_from_this();
+        auto listenKey = "callto:"+dht_.getId().toString();
+        SFL_WARN("Listening on %s : %s", listenKey.c_str(), dht::InfoHash::get(listenKey).toString().c_str());
+        dht_.listen (
+            listenKey,
+            [shared,listenKey] (const std::vector<std::shared_ptr<dht::Value>>& vals) {
+                auto& this_ = *std::static_pointer_cast<RingAccount>(shared).get();
+                for (const auto& v : vals) {
+                    try {
+                        if (v->recipient != this_.dht_.getId() || v->type != this_.ICE_ANNOUCEMENT_TYPE.id) {
+                            SFL_WARN("Ignoring non encrypted or bad type value %s.", v->toString().c_str());
+                            continue;
+                        }
+                        if (v->owner.getId() == this_.dht_.getId())
+                            continue;
+                        auto res = this_.treatedCalls_.insert(v->id);
+                        this_.saveTreatedCalls();
+                        if (!res.second) {
+                            SFL_DBG("Ignoring already treated incomming call");
+                            continue;
+                        }
+                        auto from = v->owner.getId().toString();
+                        auto from_vid = v->id;
+                        auto reply_vid = from_vid+1;
+                        SFL_WARN("Received incomming DHT call request from %s (vid %llx) !!", from.c_str(), from_vid);
+                        auto call = Manager::instance().callFactory.newCall<SIPCall, RingAccount>(this_, Manager::instance().getNewCallID(), Call::INCOMING);
+                        call->initIceTransport(false, ICE_COMPONENTS);
+                        if (call->waitForIceInitialization(ICE_INIT_TIMEOUT) <= 0)
+                            throw std::runtime_error("Can't initialize ICE..");
+                        this_.dht_.putEncrypted(
+                            listenKey,
+                            v->owner.getId(),
+                            dht::Value {
+                                this_.ICE_ANNOUCEMENT_TYPE.id,
+                                call->getIceTransport()->getLocalAttributesAndCandidates(),
+                                reply_vid
+                            },
+                            [call,shared,listenKey,reply_vid](bool ok) {
+                                SFL_WARN("ICE exchange put %d", ok);
+                                auto& this_ = *std::static_pointer_cast<RingAccount>(shared).get();
+                                this_.dht_.cancelPut(listenKey, reply_vid);
+                                SFL_WARN("waitForIceNegociation");
+                                if (!ok || call->waitForIceNegotiation(ICE_NEGOTIATION_TIMEOUT) <= 0) {
+                                    SFL_WARN("nego failed");
+                                    call->setConnectionState(Call::DISCONNECTED);
+                                } else {
+                                    SFL_WARN("nego succeeded");
+                                    call->setConnectionState(Call::PROGRESSING);
+                                    auto t = this_.link_->sipTransport->getIceTransport(call->getIceTransport(), ICE_COMP_SIP_TRANSPORT);
+                                    this_.setTransport(t);
+                                    call->setTransport(t);
+                                }
+                            }
+                        );
+                        call->getIceTransport()->start(v->data);
+                        call->setPeerNumber(from);
+                        call->initRecFilename(from);
+                        SFL_WARN("Pushing pending call with peer %s", from.c_str());
+                        this_.pendingCalls_.push_back(call);
+                        return true;
+                    } catch (const std::exception& e) {
+                        SFL_ERR("ICE/DHT error: %s", e.what());
+                    }
+                }
+                return true;
+            }
+        );
+
     }
     catch (const std::exception& e) {
         SFL_ERR("Error registering DHT account: %s", e.what());
@@ -561,6 +680,7 @@ void RingAccount::doRegister()
 
 void RingAccount::doUnregister(std::function<void(bool)> released_cb)
 {
+    pendingCalls_.clear();
     Manager::instance().unregisterEventHandler((uintptr_t)this);
     saveNodes(dht_.exportNodes());
     saveValues(dht_.exportValues());
@@ -592,6 +712,42 @@ RingAccount::unregisterCA(const dht::InfoHash& crt_id)
     return deleted;
 }
 
+void
+RingAccount::loadTreatedCalls()
+{
+    std::string treatedcallPath = cachePath_+DIR_SEPARATOR_STR "treatedCalls";
+    {
+        std::ifstream file(treatedcallPath);
+        if (!file.is_open()) {
+            SFL_WARN("Could not load treated calls from %s", treatedcallPath.c_str());
+            return;
+        }
+        std::string line;
+        while (std::getline(file, line)) {
+            std::istringstream iss(line);
+            dht::Value::Id vid;
+            if (!(iss >> std::hex >> vid)) { break; }
+            treatedCalls_.insert(vid);
+        }
+    }
+}
+
+void
+RingAccount::saveTreatedCalls() const
+{
+    fileutils::check_dir(cachePath_.c_str());
+    std::string treatedcallPath = cachePath_+DIR_SEPARATOR_STR "treatedCalls";
+    {
+        std::ofstream file(treatedcallPath, std::ios::trunc);
+        if (!file.is_open()) {
+            SFL_ERR("Could not save treated calls to %s", treatedcallPath.c_str());
+            return;
+        }
+        for (auto& c : treatedCalls_)
+            file << std::hex << c << "\n";
+    }
+}
+
 std::vector<std::string>
 RingAccount::getRegistredCAs()
 {
@@ -603,7 +759,7 @@ RingAccount::regenerateCAList()
 {
     std::ofstream list(caListPath_, std::ios::trunc | std::ios::binary);
     if (!list.is_open()) {
-        SFL_ERR("Could open CA list");
+        SFL_ERR("Could write CA list");
         return;
     }
     auto cas = getRegistredCAs();
@@ -623,8 +779,8 @@ void RingAccount::saveNodes(const std::vector<dht::Dht::NodeExport>& nodes) cons
 {
     if (nodes.empty())
         return;
-    fileutils::check_dir(nodePath_.c_str());
-    std::string nodesPath = nodePath_+DIR_SEPARATOR_STR "nodes";
+    fileutils::check_dir(cachePath_.c_str());
+    std::string nodesPath = cachePath_+DIR_SEPARATOR_STR "nodes";
     {
         std::ofstream file(nodesPath, std::ios::trunc);
         if (!file.is_open()) {
@@ -650,7 +806,7 @@ std::vector<dht::Dht::NodeExport>
 RingAccount::loadNodes() const
 {
     std::vector<dht::Dht::NodeExport> nodes;
-    std::string nodesPath = nodePath_+DIR_SEPARATOR_STR "nodes";
+    std::string nodesPath = cachePath_+DIR_SEPARATOR_STR "nodes";
     {
         std::ifstream file(nodesPath);
         if (!file.is_open()) {
@@ -717,31 +873,29 @@ void RingAccount::loadConfig()
     transportType_ = PJSIP_TRANSPORT_TLS;
 }
 
-bool RingAccount::userMatch(const std::string& username) const
-{
-    return !username.empty() and username == dht_.getId().toString();
-}
-
 MatchRank
-RingAccount::matches(const std::string &userName, const std::string &/*server*/) const
+RingAccount::matches(const std::string &userName, const std::string &server) const
 {
-    if (userMatch(userName)) {
+    auto dhtId = dht_.getId().toString();
+    if (userName == dhtId || server == dhtId) {
         SFL_DBG("Matching account id in request with username %s", userName.c_str());
         return MatchRank::FULL;
     } else {
+        SFL_DBG("No match for account %s in request with username %s", dht_.getId().toString().c_str(), userName.c_str());
         return MatchRank::NONE;
     }
 }
 
 std::string RingAccount::getFromUri() const
 {
-    return "ring:" + dht_.getId().toString() + "@ring.dht";
+    return "<sip:" + dht_.getId().toString() + "@ring.dht>";
 }
 
 std::string RingAccount::getToUri(const std::string& to) const
 {
     const std::string transport {pjsip_transport_get_type_name(transportType_)};
-    return "<sips:" + to + ";transport=" + transport + ">";
+    return "<sip:" + to + ">";
+    //return "<sips:" + to + ";transport=" + transport + ">";
 }
 
 pj_str_t
@@ -749,36 +903,54 @@ RingAccount::getContactHeader(pjsip_transport* t)
 {
     if (!t && transport_)
         t = transport_->get();
-    if (!t)
+    if (!t) {
         SFL_ERR("Transport not created yet");
+        pj_cstr(&contact_, "<sip:>");
+        return contact_;
+    }
+
+    auto ice = reinterpret_cast<SipIceTransport*>(t)->getIceTransport();
 
     // The transport type must be specified, in our case START_OTHER refers to stun transport
-    pjsip_transport_type_e transportType = transportType_;
+    /*pjsip_transport_type_e transportType = transportType_;
 
     if (transportType == PJSIP_TRANSPORT_START_OTHER)
-        transportType = PJSIP_TRANSPORT_UDP;
+        transportType = PJSIP_TRANSPORT_UDP;*/
 
     // Else we determine this infor based on transport information
-    std::string address = "ring.dht";
-    pj_uint16_t port = getTlsListenerPort();
-
-    link_->sipTransport->findLocalAddressFromTransport(t, transportType, hostname_, address, port);
-
+    //std::string address = "ring.dht";
+    //pj_uint16_t port = getTlsListenerPort();
+
+    //link_->sipTransport->findLocalAddressFromTransport(t, transportType, hostname_, address, port);
+    auto address = ice->getDefaultLocalAddress();
+    /*if (addr) {
+        address = addr;
+        port =
+    }*/
+    /*auto ports = ice->getLocalPorts();
+    if (not ports.empty())
+        port = ports[0];*/
+/*
 #if HAVE_IPV6
-    /* Enclose IPv6 address in square brackets */
+    // Enclose IPv6 address in square brackets
     if (IpAddr::isIpv6(address)) {
-        address = IpAddr(address).toString(false, true);
+        address = IpAddr(address);//.toString(false, true);
     }
 #endif
-
-    SFL_WARN("getContactHeader %s@%s:%d", username_.c_str(), address.c_str(), port);
+*/
+    SFL_WARN("getContactHeader %s@%s", username_.c_str(), address.toString(true).c_str());
+    contact_.slen = pj_ansi_snprintf(contact_.ptr, PJSIP_MAX_URL_SIZE,
+                                     "<sip:%s%s%s>",
+                                     username_.c_str(),
+                                     (username_.empty() ? "" : "@"),
+                                     address.toString(true).c_str());    /*
     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));
+                                     pjsip_transport_get_type_name(transportType));*/
     return contact_;
 }
 
diff --git a/daemon/src/ringdht/ringaccount.h b/daemon/src/ringdht/ringaccount.h
index 6eb77fcc2db3b4e06c0c12553bd02ce33625ce4f..72d59d920481c66cfd29008768634d67505c9c2d 100644
--- a/daemon/src/ringdht/ringaccount.h
+++ b/daemon/src/ringdht/ringaccount.h
@@ -230,14 +230,14 @@ class RingAccount : public SIPAccountBase {
          *      This type can be any base class of SIPCall class (included).
          */
         virtual std::shared_ptr<SIPCall>
-        newIncomingCall(const std::string& id);
+        newIncomingCall(const std::string& from = {});
 
         virtual bool isTlsEnabled() const {
-            return true;
+            return false;
         }
 
         virtual bool getSrtpEnabled() const {
-            return true;
+            return false;
         }
 
         virtual std::string getSrtpKeyExchange() const {
@@ -257,7 +257,7 @@ class RingAccount : public SIPAccountBase {
         const dht::ValueType USER_PROFILE_TYPE = {9, "User profile", 60 * 60 * 24 * 7};
         const dht::ValueType ICE_ANNOUCEMENT_TYPE = {10, "ICE descriptors", 15 * 60};
 
-        void createOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::string& to, const std::string& toUrl, const IpAddr& peer);
+        void createOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::string& to_id, IpAddr target);
 
         /**
          * Set the internal state for this account, mainly used to manage account details from the client application.
@@ -272,9 +272,7 @@ class RingAccount : public SIPAccountBase {
          * @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;
+        bool SIPStartCall(const std::shared_ptr<SIPCall>& call, IpAddr target);
 
         void regenerateCAList();
 
@@ -289,12 +287,18 @@ class RingAccount : public SIPAccountBase {
 
         dht::DhtRunner dht_ {};
 
+        /**
+         * Incomming DHT calls that are not yet actual SIP calls.
+         */
+        std::vector<std::shared_ptr<SIPCall>> pendingCalls_ {};
+        std::set<dht::Value::Id> treatedCalls_ {};
+
         std::string cacertPath_ {};
         std::string privkeyPath_ {};
         std::string certPath_ {};
         std::string idPath_ {};
 
-        std::string nodePath_ {};
+        std::string cachePath_ {};
         std::string dataPath_ {};
         std::string caPath_ {};
         std::string caListPath_ {};
@@ -309,6 +313,9 @@ class RingAccount : public SIPAccountBase {
         void saveNodes(const std::vector<dht::Dht::NodeExport>&) const;
         void saveValues(const std::vector<dht::Dht::ValuesExport>&) const;
 
+        void loadTreatedCalls();
+        void saveTreatedCalls() const;
+
         /**
          * If privkeyPath_ is a valid private key file (PEM or DER),
          * and certPath_ a valid certificate file, load and returns them.
diff --git a/daemon/src/sip/sipaccount.cpp b/daemon/src/sip/sipaccount.cpp
index 0ccc54d006182921b32289d09d0cc7443285f172..aa255f6c9013f3f8db00373c2b0379cf961436aa 100644
--- a/daemon/src/sip/sipaccount.cpp
+++ b/daemon/src/sip/sipaccount.cpp
@@ -162,9 +162,10 @@ SIPAccount::~SIPAccount()
 }
 
 std::shared_ptr<SIPCall>
-SIPAccount::newIncomingCall(const std::string& id)
+SIPAccount::newIncomingCall(const std::string&)
 {
-    return Manager::instance().callFactory.newCall<SIPCall, SIPAccount>(*this, id, Call::INCOMING);
+    auto& manager = Manager::instance();
+    return manager.callFactory.newCall<SIPCall, SIPAccount>(*this, manager.getNewCallID(), Call::INCOMING);
 }
 
 template <>
diff --git a/daemon/src/sip/sipaccount.h b/daemon/src/sip/sipaccount.h
index ccedc00d8f213e1087662678830401200ea23f51..26fcfee0f958d759de0f839ae1b17eb535a51c11 100644
--- a/daemon/src/sip/sipaccount.h
+++ b/daemon/src/sip/sipaccount.h
@@ -482,7 +482,7 @@ class SIPAccount : public SIPAccountBase {
          *      This type can be any base class of SIPCall class (included).
          */
         std::shared_ptr<SIPCall>
-        newIncomingCall(const std::string& id);
+        newIncomingCall(const std::string&);
 
         void onRegister(pjsip_regc_cbparam *param);
 
diff --git a/daemon/src/sip/sipaccountbase.h b/daemon/src/sip/sipaccountbase.h
index 98c1a3a0decd458629403228c7a3fdd7184bbc4b..d800ff1191abf5116433d4a53a6be3057d3ea911 100644
--- a/daemon/src/sip/sipaccountbase.h
+++ b/daemon/src/sip/sipaccountbase.h
@@ -125,7 +125,7 @@ public:
      *      This type can be any base class of SIPCall class (included).
      */
     virtual std::shared_ptr<SIPCall>
-    newIncomingCall(const std::string& id) = 0;
+    newIncomingCall(const std::string& from) = 0;
 
     virtual bool isStunEnabled() const {
         return false;
diff --git a/daemon/src/sip/siptransport.cpp b/daemon/src/sip/siptransport.cpp
index eabc34e7d43646a9f2f95289a591be493e84c4e9..96467faa84078bcecc6f88bc76b6c01cb0e9e9fa 100644
--- a/daemon/src/sip/siptransport.cpp
+++ b/daemon/src/sip/siptransport.cpp
@@ -379,10 +379,10 @@ SipTransportBroker::getTlsTransport(const std::shared_ptr<TlsListener>& l, const
 
 #if HAVE_DHT
 std::shared_ptr<SipTransport>
-SipTransportBroker::getIceTransport(const std::shared_ptr<sfl::IceTransport>& ice)
+SipTransportBroker::getIceTransport(const std::shared_ptr<sfl::IceTransport>& ice, unsigned comp_id)
 {
     std::unique_lock<std::mutex> lock(iceMutex_);
-    iceTransports_.emplace_front(endpt_, pool_, ice_pj_transport_type_, ice, 0);
+    iceTransports_.emplace_front(endpt_, pool_, ice_pj_transport_type_, ice, comp_id);
     auto& sip_ice_tr = iceTransports_.front();
     auto ret = std::make_shared<SipTransport>(&sip_ice_tr.base);
     {
diff --git a/daemon/src/sip/siptransport.h b/daemon/src/sip/siptransport.h
index 6301d4d0e2256b40bc4e2da6d6eff7415abf1faa..93e7b97877c15f20c3a660a53e4ae14c8b3af4c1 100644
--- a/daemon/src/sip/siptransport.h
+++ b/daemon/src/sip/siptransport.h
@@ -179,7 +179,7 @@ public:
 #endif
 
 #if HAVE_DHT
-    std::shared_ptr<SipTransport> getIceTransport(const std::shared_ptr<sfl::IceTransport>&);
+    std::shared_ptr<SipTransport> getIceTransport(const std::shared_ptr<sfl::IceTransport>&, unsigned comp_id);
 #endif
 
     std::shared_ptr<SipTransport> findTransport(pjsip_transport*);
diff --git a/daemon/src/sip/sipvoiplink.cpp b/daemon/src/sip/sipvoiplink.cpp
index 4bf5c4e232d74bb9f023a450d8eed13a296c0863..7bea74cee8bb2bdf88f8f45e370804d5dfc853c5 100644
--- a/daemon/src/sip/sipvoiplink.cpp
+++ b/daemon/src/sip/sipvoiplink.cpp
@@ -289,7 +289,10 @@ transaction_request_cb(pjsip_rx_data *rdata)
 
     Manager::instance().hookPreference.runHook(rdata->msg_info.msg);
 
-    auto call = account->newIncomingCall(Manager::instance().getNewCallID());
+    auto call = account->newIncomingCall(remote_user);
+    if (!call) {
+        return PJ_FALSE;
+    }
 
     // FIXME : for now, use the same address family as the SIP transport
     auto family = pjsip_transport_type_get_af(account->getTransportType());
@@ -320,6 +323,7 @@ transaction_request_cb(pjsip_rx_data *rdata)
             SFL_WARN("Using transport from account.");
         }
     }
+    call->setTransport(transport);
 
     call->setConnectionState(Call::PROGRESSING);
     call->setPeerNumber(peerNumber);
@@ -329,10 +333,6 @@ transaction_request_cb(pjsip_rx_data *rdata)
     call->getSDP().setPublishedIP(addrSdp);
 #if USE_CCRTP
     call->getAudioRtp().initConfig();
-#endif
-    call->setTransport(transport);
-
-#if USE_CCRTP
     try {
         call->getAudioRtp().initSession();
     } catch (const ost::Socket::Error &err) {
@@ -379,7 +379,10 @@ transaction_request_cb(pjsip_rx_data *rdata)
 #endif
 
     call->getSDP().receiveOffer(r_sdp, account->getActiveAudioCodecs(), account->getActiveVideoCodecs());
-    call->initIceTransport(false);
+    if (not call->getIceTransport()) {
+        SFL_DBG("Initializing ICE transport");
+        call->initIceTransport(false);
+    }
     call->setupLocalSDPFromIce();
 
     sfl::AudioCodec* ac = Manager::instance().audioCodecFactory.instantiateCodec(PAYLOAD_CODEC_ULAW);
@@ -398,6 +401,7 @@ transaction_request_cb(pjsip_rx_data *rdata)
     pjsip_dialog *dialog = 0;
 
     if (pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, nullptr, &dialog) != PJ_SUCCESS) {
+        SFL_ERR("Could not create uas");
         call.reset();
         try_respond_stateless(endpt_, rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, nullptr, nullptr, nullptr);
         return PJ_FALSE;