diff --git a/daemon/src/call.cpp b/daemon/src/call.cpp index dd3356177da3b582b74c2c1a6a18ac1308f64567..0b7dfe679dfa2ac07eb6c84a689941ac61051cf2 100644 --- a/daemon/src/call.cpp +++ b/daemon/src/call.cpp @@ -331,7 +331,7 @@ Call::isIceUsed() const bool Call::isIceRunning() const { - return iceTransport_->isCompleted(); + return iceTransport_->isRunning(); } ring::IceSocket* diff --git a/daemon/src/ice_transport.cpp b/daemon/src/ice_transport.cpp index c0f07097133edc9680d9d6b637d0c278607a524f..0634b03cf1994aeea51fd4eb688a17d02c027f70 100644 --- a/daemon/src/ice_transport.cpp +++ b/daemon/src/ice_transport.cpp @@ -338,6 +338,18 @@ IceTransport::isCompleted() const return pj_ice_strans_sess_is_complete(icest_.get()); } +bool +IceTransport::isRunning() const +{ + return pj_ice_strans_get_state(icest_.get()) == PJ_ICE_STRANS_STATE_RUNNING; +} + +bool +IceTransport::isFailed() const +{ + return pj_ice_strans_get_state(icest_.get()) == PJ_ICE_STRANS_STATE_FAILED; +} + IpAddr IceTransport::getLocalAddress(unsigned comp_id) const { diff --git a/daemon/src/ice_transport.h b/daemon/src/ice_transport.h index a268f0303fe7c131d42404e112a184fb2c831bae..b3c7abaee6a96531b41fbd27df1052f309ec8036 100644 --- a/daemon/src/ice_transport.h +++ b/daemon/src/ice_transport.h @@ -103,6 +103,9 @@ class IceTransport { bool isCompleted() const; + bool isRunning() const; + bool isFailed() const; + IpAddr getLocalAddress(unsigned comp_id) const; IpAddr getRemoteAddress(unsigned comp_id) const; diff --git a/daemon/src/ringdht/ringaccount.cpp b/daemon/src/ringdht/ringaccount.cpp index 775203b3cec7360fa8ef74c47b88303d36c9ac56..fc001770febf62df724ed99d7f45157ddec91b22 100644 --- a/daemon/src/ringdht/ringaccount.cpp +++ b/daemon/src/ringdht/ringaccount.cpp @@ -109,11 +109,12 @@ RingAccount::~RingAccount() std::shared_ptr<SIPCall> RingAccount::newIncomingCall(const std::string& from) { - auto call_it = pendingCalls_.begin(); - while (call_it != pendingCalls_.end()) { - if ((*call_it)->getPeerNumber() == from) { - std::shared_ptr<SIPCall> call = *call_it; - pendingCalls_.erase(call_it); + std::lock_guard<std::mutex> lock(callsMutex_); + auto call_it = pendingSipCalls_.begin(); + while (call_it != pendingSipCalls_.end()) { + if (call_it->call->getPeerNumber() == from) { + auto call = call_it->call; + pendingSipCalls_.erase(call_it); RING_WARN("Found matching call for %s", from.c_str()); return call; } else { @@ -142,6 +143,7 @@ RingAccount::newOutgoingCall(const std::string& id, const std::string& toUrl) throw std::invalid_argument("id must be a ring infohash"); RING_DBG("Calling DHT peer %s", toUri.c_str()); + auto toH = dht::InfoHash(toUri); auto call = Manager::instance().callFactory.newCall<SIPCall, RingAccount>(*this, id, Call::OUTGOING); call->setIPToIP(true); @@ -168,13 +170,13 @@ RingAccount::newOutgoingCall(const std::string& id, const std::string& toUrl) const dht::Value::Id replyvid = callvid+1; dht_.putEncrypted( callkey, - dht::InfoHash(toUri), + toH, dht::Value { ICE_ANNOUCEMENT_TYPE.id, ice->getLocalAttributesAndCandidates(), callvid }, - [callkey, callvid, call, shared](bool ok){ + [callkey, callvid, call, shared](bool ok) { auto& this_ = *std::static_pointer_cast<RingAccount>(shared).get(); if (!ok) { call->setConnectionState(Call::DISCONNECTED); @@ -186,35 +188,28 @@ RingAccount::newOutgoingCall(const std::string& id, const std::string& toUrl) dht_.listen( callkey, - [shared, call, callkey, ice, toUri, replyvid] (const std::vector<std::shared_ptr<dht::Value>>& vals) { + [shared, call, callkey, 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() || v->type != this_.ICE_ANNOUCEMENT_TYPE.id) { - RING_WARN("Ignoring non encrypted or bad type value %s.", v->toString().c_str()); + RING_DBG("Ignoring non encrypted or bad type value %s.", v->toString().c_str()); continue; } - if (v->id != replyvid) { - RING_WARN("Ignoring value ID %llx (expected %llx)", v->id, replyvid); + if (v->id != replyvid) continue; - } - RING_WARN("Got a DHT reply from %s !", toUri.c_str()); + RING_WARN("Got a DHT reply from %s !", toH.toString().c_str()); RING_WARN("Performing ICE negotiation."); ice->start(v->data); - if (ice->waitForNegotiation(ICE_NEGOTIATION_TIMEOUT) <= 0) { - call->setConnectionState(Call::DISCONNECTED); - Manager::instance().callFailure(*call); - return false; - } - call->setTransport(this_.link_->sipTransport->getIceTransport(ice, ICE_COMP_SIP_TRANSPORT)); - call->setConnectionState(Call::PROGRESSING); - this_.createOutgoingCall(call, toUri, ice->getRemoteAddress(ICE_COMP_SIP_TRANSPORT)); return false; } return true; } ); - + { + std::lock_guard<std::mutex> lock(callsMutex_); + pendingCalls_.emplace_back(PendingCall{std::chrono::steady_clock::now(), ice, call, toH}); + } return call; } @@ -526,6 +521,40 @@ std::map<std::string, std::string> RingAccount::getAccountDetails() const return a; } +void +RingAccount::handleEvents() +{ + dht_.loop(); + + std::lock_guard<std::mutex> lock(callsMutex_); + auto now = std::chrono::steady_clock::now(); + auto c = pendingCalls_.begin(); + while (c != pendingCalls_.end()) { + auto ice = c->ice.get(); + auto call = c->call.get(); + if (ice->isRunning()) { + call->setTransport(link_->sipTransport->getIceTransport(c->ice, ICE_COMP_SIP_TRANSPORT)); + call->setConnectionState(Call::PROGRESSING); + if (c->id == dht::InfoHash()) { + RING_WARN("ICE succeeded : moving incomming call to pending sip call"); + auto in = c; + ++c; + pendingSipCalls_.splice(pendingSipCalls_.begin(), pendingCalls_, in, c); + } else { + RING_WARN("ICE succeeded : removing pending outgoing call"); + createOutgoingCall(c->call, c->id.toString(), ice->getRemoteAddress(ICE_COMP_SIP_TRANSPORT)); + c = pendingCalls_.erase(c); + } + } else if (ice->isFailed() || now - c->start > std::chrono::seconds(ICE_NEGOTIATION_TIMEOUT)) { + RING_WARN("ICE timeout : removing pending outgoing call"); + call->setConnectionState(Call::DISCONNECTED); + Manager::instance().callFailure(*call); + c = pendingCalls_.erase(c); + } else + ++c; + } +} + void RingAccount::doRegister() { if (not isEnabled()) { @@ -589,7 +618,7 @@ void RingAccount::doRegister() username_ = dht_.getId().toString(); - Manager::instance().registerEventHandler((uintptr_t)this, [this](){ dht_.loop(); }); + Manager::instance().registerEventHandler((uintptr_t)this, [this]{ handleEvents(); }); setRegistrationState(RegistrationState::TRYING); dht_.bootstrap(loadNodes()); @@ -662,25 +691,22 @@ void RingAccount::doRegister() reply_vid }, [call,ice,shared,listenKey,reply_vid](bool ok) { - RING_WARN("ICE exchange put %d", ok); auto& this_ = *std::static_pointer_cast<RingAccount>(shared).get(); this_.dht_.cancelPut(listenKey, reply_vid); - RING_WARN("waitForIceNegociation"); - if (!ok || ice->waitForNegotiation(ICE_NEGOTIATION_TIMEOUT) <= 0) { - RING_WARN("SIP ICE negotiation failed"); + if (!ok) { + RING_ERR("ICE exchange failed"); call->setConnectionState(Call::DISCONNECTED); Manager::instance().callFailure(*call); - } else { - RING_DBG("SIP ICE negotiation succeeded"); - call->setConnectionState(Call::PROGRESSING); - call->setTransport(this_.link_->sipTransport->getIceTransport(ice, ICE_COMP_SIP_TRANSPORT)); } } ); ice->start(v->data); call->setPeerNumber(from); call->initRecFilename(from); - this_.pendingCalls_.push_back(call); + { + std::lock_guard<std::mutex> lock(this_.callsMutex_); + this_.pendingCalls_.emplace_back(PendingCall{std::chrono::steady_clock::now(), ice, call, dht::InfoHash()}); + } return true; } catch (const std::exception& e) { RING_ERR("ICE/DHT error: %s", e.what()); @@ -704,7 +730,11 @@ void RingAccount::doRegister() void RingAccount::doUnregister(std::function<void(bool)> released_cb) { - pendingCalls_.clear(); + { + std::lock_guard<std::mutex> lock(callsMutex_); + pendingCalls_.clear(); + pendingSipCalls_.clear(); + } Manager::instance().unregisterEventHandler((uintptr_t)this); saveNodes(dht_.exportNodes()); saveValues(dht_.exportValues()); diff --git a/daemon/src/ringdht/ringaccount.h b/daemon/src/ringdht/ringaccount.h index d60baaf9dbc23c79080358f8d18caa821531e645..9089c647465c0e8ed59cc9dad3dc57d8a9c9c8b5 100644 --- a/daemon/src/ringdht/ringaccount.h +++ b/daemon/src/ringdht/ringaccount.h @@ -254,7 +254,11 @@ class RingAccount : public SIPAccountBase { private: const dht::ValueType USER_PROFILE_TYPE = {9, "User profile", std::chrono::hours(24 * 7)}; - const dht::ValueType ICE_ANNOUCEMENT_TYPE = {10, "ICE descriptors", std::chrono::minutes(15)}; + const dht::ValueType ICE_ANNOUCEMENT_TYPE = {10, "ICE descriptors", std::chrono::minutes(3)}; + + NON_COPYABLE(RingAccount); + + void handleEvents(); void createOutgoingCall(const std::shared_ptr<SIPCall>& call, const std::string& to_id, IpAddr target); @@ -264,8 +268,6 @@ class RingAccount : public SIPAccountBase { */ virtual void setAccountDetails(const std::map<std::string, std::string> &details); - NON_COPYABLE(RingAccount); - /** * Start a SIP Call * @param call The current call @@ -286,11 +288,22 @@ class RingAccount : public SIPAccountBase { dht::DhtRunner dht_ {}; + struct PendingCall { + std::chrono::steady_clock::time_point start; + std::shared_ptr<ring::IceTransport> ice; + std::shared_ptr<SIPCall> call; + dht::InfoHash id; + }; + /** + * DHT calls waiting for negotiation + */ + std::list<PendingCall> pendingCalls_ {}; /** - * Incomming DHT calls that are not yet actual SIP calls. + * Incoming DHT calls that are not yet actual SIP calls. */ - std::list<std::shared_ptr<SIPCall>> pendingCalls_ {}; + std::list<PendingCall> pendingSipCalls_ {}; std::set<dht::Value::Id> treatedCalls_ {}; + mutable std::mutex callsMutex_ {}; std::string cacertPath_ {}; std::string privkeyPath_ {}; diff --git a/daemon/src/ringdht/sip_transport_ice.cpp b/daemon/src/ringdht/sip_transport_ice.cpp index f3c730dd4e2f4a774a24265696abfe91b68a41cc..d7410981e2906af68dd74b623ac07b770cf8e445 100644 --- a/daemon/src/ringdht/sip_transport_ice.cpp +++ b/daemon/src/ringdht/sip_transport_ice.cpp @@ -66,8 +66,8 @@ SipIceTransport::SipIceTransport(pjsip_endpoint* endpt, pj_pool_t& /* pool */, , comp_id_(comp_id) , destroy_cb_(destroy_cb) { - if (not ice or not ice->isCompleted()) - throw std::logic_error("ice transport must exist and negociation completed"); + if (not ice or not ice->isRunning()) + throw std::logic_error("ice transport must exist and negotiation completed"); RING_DBG("Creating SipIceTransport");