diff --git a/daemon/src/ringdht/ringaccount.cpp b/daemon/src/ringdht/ringaccount.cpp index 4eb3cce9526aa62ac05cb4e7b60007aea8a5404b..8976fffbc3131d62a7419721b0a826bd53b31797 100644 --- a/daemon/src/ringdht/ringaccount.cpp +++ b/daemon/src/ringdht/ringaccount.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2014 Savoir-Faire Linux Inc. * * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com> + * Author: Guillaume Roguez <guillaume.roguez@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 @@ -137,7 +138,6 @@ template <> std::shared_ptr<SIPCall> RingAccount::newOutgoingCall(const std::string& toUrl) { - auto& manager = Manager::instance(); auto dhtf = toUrl.find("ring:"); if (dhtf != std::string::npos) { dhtf = dhtf+5; @@ -147,93 +147,110 @@ RingAccount::newOutgoingCall(const std::string& toUrl) } if (toUrl.length() - dhtf < 40) throw std::invalid_argument("id must be a ring infohash"); + const std::string toUri = toUrl.substr(dhtf, 40); if (std::find_if_not(toUri.cbegin(), toUri.cend(), ::isxdigit) != toUri.cend()) throw std::invalid_argument("id must be a ring infohash"); - RING_DBG("Calling DHT peer %s", toUri.c_str()); - auto toH = dht::InfoHash(toUri); + RING_DBG("Calling DHT peer %s", toUrl.c_str()); + auto& manager = Manager::instance(); auto call = manager.callFactory.newCall<SIPCall, RingAccount>(*this, manager.getNewCallID(), Call::OUTGOING); call->setIPToIP(true); call->setSecure(isTlsEnabled()); - auto& iceTransportFactory = manager.getIceTransportFactory(); - auto ice = iceTransportFactory.createTransport( - ("sip:"+call->getCallId()).c_str(), - ICE_COMPONENTS, - true, - getUPnPActive() - ); - if (not ice or ice->waitForInitialization(ICE_INIT_TIMEOUT) <= 0) { - call->setConnectionState(Call::DISCONNECTED); - call->setState(Call::MERROR); - return call; + // Create an ICE transport for SIP channel + auto& tfactory = manager.getIceTransportFactory(); + auto ice = tfactory.createTransport(("sip:" + call->getCallId()).c_str(), + ICE_COMPONENTS, true, getUPnPActive()); + if (not ice) { + call->removeCall(); + return nullptr; } - call->setState(Call::INACTIVE); - 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; - - std::weak_ptr<SIPCall> weak_call = call; - - dht_.putEncrypted( - callkey, - toH, - dht::Value { - dht::DhtMessage::TYPE, - dht::DhtMessage( - dht::DhtMessage::Service::ICE_CANDIDATES, - ice->getLocalAttributesAndCandidates() - ), - callvid - }, - [callkey, callvid, weak_call, shared](bool ok) { - auto& this_ = *std::static_pointer_cast<RingAccount>(shared).get(); - if (!ok) { - RING_WARN("Can't put ICE descriptor on DHT"); - if (auto call = weak_call.lock()) { - call->setConnectionState(Call::DISCONNECTED); - Manager::instance().callFailure(*call); - call->removeCall(); - } - } - this_.dht_.cancelPut(callkey, callvid); + auto shared_this = std::static_pointer_cast<RingAccount>(shared_from_this()); + auto iceInitTimeout = std::chrono::steady_clock::now() + std::chrono::seconds {ICE_INIT_TIMEOUT}; + + manager.addTask([=] { + static std::uniform_int_distribution<dht::Value::Id> udist; + + /* First step: wait for an initialized ICE transport for SIP channel */ + if (ice->isFailed() or std::chrono::steady_clock::now() >= iceInitTimeout) { + RING_DBG("ice init failed (or timeout)"); + call->setConnectionState(Call::DISCONNECTED); + call->setState(Call::MERROR); + Manager::instance().callFailure(*call); // signal client + call->removeCall(); + return false; } - ); - - auto lk = dht_.listen( - callkey, - [shared, ice, toH, replyvid] (const std::vector<std::shared_ptr<dht::Value>>& vals) { - auto& this_ = *std::static_pointer_cast<RingAccount>(shared).get(); - RING_DBG("Outcall listen callback (%d values)", vals.size()); - for (const auto& v : vals) { - if (v->recipient != this_.dht_.getId()) { - RING_DBG("Ignoring non encrypted or bad type value %s.", v->toString().c_str()); - continue; - } - if (v->id != replyvid) - continue; - dht::DhtMessage msg {v->data}; - RING_WARN("Got a DHT reply from %s !", toH.toString().c_str()); - RING_WARN("Performing ICE negotiation."); - ice->start(msg.getMessage()); - return false; - } + + if (not ice->isInitialized()) return true; - }, - dht::DhtMessage::ServiceFilter(dht::DhtMessage::Service::ICE_CANDIDATES) - ); - { - std::lock_guard<std::mutex> lock(callsMutex_); - pendingCalls_.emplace_back(PendingCall{std::chrono::steady_clock::now(), ice, weak_call, std::move(lk), callkey, toH}); - } + + /* Next step: sent the ICE data to peer through DHT */ + const dht::Value::Id callvid = udist(shared_this->rand_); + const dht::Value::Id replyvid = callvid + 1; + const auto toH = dht::InfoHash(toUri); + const auto callkey = dht::InfoHash::get("callto:" + toUri); + + std::weak_ptr<SIPCall> weak_call = call; + + call->setConnectionState(Call::TRYING); + shared_this->dht_.putEncrypted( + callkey, toH, + dht::Value { + dht::DhtMessage::TYPE, + dht::DhtMessage( + dht::DhtMessage::Service::ICE_CANDIDATES, + ice->getLocalAttributesAndCandidates() + ), + callvid + }, + [=](bool ok) { // Put complete callback + if (!ok) { + RING_WARN("Can't put ICE descriptor on DHT"); + if (auto call = weak_call.lock()) { + call->setConnectionState(Call::DISCONNECTED); + Manager::instance().callFailure(*call); // signal client + call->removeCall(); + } + } + shared_this->dht_.cancelPut(callkey, callvid); + }); + + auto listenKey = shared_this->dht_.listen( + callkey, + [=] (const std::vector<std::shared_ptr<dht::Value>>& vals) { + RING_DBG("Outcall listen callback (%d values)", vals.size()); + for (const auto& v : vals) { + if (v->recipient != shared_this->dht_.getId()) { + RING_DBG("Ignoring non encrypted or bad type value %s", + v->toString().c_str()); + continue; + } + if (v->id != replyvid) + continue; + dht::DhtMessage msg {v->data}; + RING_WARN("ICE request replied from DHT peer %s", + toH.toString().c_str()); + ice->start(msg.getMessage()); + return false; + } + return true; + }, + dht::DhtMessage::ServiceFilter(dht::DhtMessage::Service::ICE_CANDIDATES) + ); + + shared_this->pendingCalls_.emplace_back(PendingCall{ + std::chrono::steady_clock::now(), + ice, weak_call, + std::move(listenKey), + callkey, toH + }); + return false; + }); + return call; }