mirror of
https://git.jami.net/savoirfairelinux/jami-daemon.git
synced 2025-08-12 22:09:25 +08:00
connectionmanager: prevent JamiAccount usage
GitLab: #778 Change-Id: I5e7eb072ebda81c4ae45316cc46842dafdeaad13
This commit is contained in:

committed by
Sébastien Blin

parent
7c7c1d38c3
commit
edebe17139
@ -16,12 +16,14 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "connectionmanager.h"
|
||||
#include "jamidht/jamiaccount.h"
|
||||
#include "account_const.h"
|
||||
#include "jamidht/account_manager.h"
|
||||
#include "manager.h"
|
||||
#include "peer_connection.h"
|
||||
#include "logger.h"
|
||||
#include "fileutils.h"
|
||||
#include "connectivity/upnp/upnp_control.h"
|
||||
#include "connectivity/sip_utils.h"
|
||||
#include "connectivity/security/certstore.h"
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <opendht/crypto.h>
|
||||
@ -61,14 +63,31 @@ struct ConnectionInfo
|
||||
std::unique_ptr<asio::steady_timer> waitForAnswer_ {};
|
||||
};
|
||||
|
||||
/**
|
||||
* returns whether or not UPnP is enabled and active_
|
||||
* ie: if it is able to make port mappings
|
||||
*/
|
||||
bool
|
||||
ConnectionManager::Config::getUPnPActive() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(upnp_mtx);
|
||||
if (upnpCtrl_)
|
||||
return upnpCtrl_->isReady();
|
||||
return false;
|
||||
}
|
||||
|
||||
class ConnectionManager::Impl : public std::enable_shared_from_this<ConnectionManager::Impl>
|
||||
{
|
||||
public:
|
||||
explicit Impl(JamiAccount& account)
|
||||
: account {account}
|
||||
explicit Impl(std::shared_ptr<ConnectionManager::Config> config_)
|
||||
: config_ {std::move(config_)}
|
||||
, rand {dht::crypto::getSeededRandomEngine<std::mt19937_64>()}
|
||||
{}
|
||||
~Impl() {}
|
||||
|
||||
std::shared_ptr<dht::DhtRunner> dht() { return config_->dht_; }
|
||||
const dht::crypto::Identity& identity() const { return config_->id_; }
|
||||
|
||||
void removeUnusedConnections(const DeviceId& deviceId = {})
|
||||
{
|
||||
std::vector<std::shared_ptr<ConnectionInfo>> unused {};
|
||||
@ -163,6 +182,87 @@ public:
|
||||
void onPeerResponse(const PeerConnectionRequest& req);
|
||||
void onDhtConnected(const dht::crypto::PublicKey& devicePk);
|
||||
|
||||
const std::shared_future<tls::DhParams> dhParams() const;
|
||||
tls::CertificateStore& certStore() const { return *config_->certStore_; }
|
||||
|
||||
mutable std::mutex messageMutex_ {};
|
||||
std::set<std::string, std::less<>> treatedMessages_ {};
|
||||
|
||||
void loadTreatedMessages();
|
||||
void saveTreatedMessages() const;
|
||||
|
||||
/// \return true if the given DHT message identifier has been treated
|
||||
/// \note if message has not been treated yet this method store this id and returns true at
|
||||
/// further calls
|
||||
bool isMessageTreated(std::string_view id);
|
||||
|
||||
/**
|
||||
* Published IPv4/IPv6 addresses, used only if defined by the user in account
|
||||
* configuration
|
||||
*
|
||||
*/
|
||||
IpAddr publishedIp_[2] {};
|
||||
|
||||
// This will be stored in the configuration
|
||||
std::string publishedIpAddress_ {};
|
||||
|
||||
/**
|
||||
* Published port, used only if defined by the user
|
||||
*/
|
||||
pj_uint16_t publishedPort_ {sip_utils::DEFAULT_SIP_PORT};
|
||||
|
||||
/**
|
||||
* interface name on which this account is bound
|
||||
*/
|
||||
std::string interface_ {"default"};
|
||||
|
||||
/**
|
||||
* Get the local interface name on which this account is bound.
|
||||
*/
|
||||
const std::string& getLocalInterface() const { return interface_; }
|
||||
|
||||
/**
|
||||
* Get the published IP address, fallbacks to NAT if family is unspecified
|
||||
* Prefers the usage of IPv4 if possible.
|
||||
*/
|
||||
IpAddr getPublishedIpAddress(uint16_t family = PF_UNSPEC) const;
|
||||
|
||||
/**
|
||||
* Set published IP address according to given family
|
||||
*/
|
||||
void setPublishedAddress(const IpAddr& ip_addr);
|
||||
|
||||
/**
|
||||
* Store the local/public addresses used to register
|
||||
*/
|
||||
void storeActiveIpAddress(std::function<void()>&& cb = {});
|
||||
|
||||
/**
|
||||
* Create and return ICE options.
|
||||
*/
|
||||
void getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept;
|
||||
IceTransportOptions getIceOptions() const noexcept;
|
||||
|
||||
/**
|
||||
* Inform that a potential peer device have been found.
|
||||
* Returns true only if the device certificate is a valid device certificate.
|
||||
* In that case (true is returned) the account_id parameter is set to the peer account ID.
|
||||
*/
|
||||
static bool foundPeerDevice(const std::shared_ptr<dht::crypto::Certificate>& crt,
|
||||
dht::InfoHash& account_id);
|
||||
|
||||
bool findCertificate(const dht::PkId& id,
|
||||
std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb);
|
||||
bool findCertificate(
|
||||
const dht::InfoHash& h,
|
||||
std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb = {});
|
||||
|
||||
/**
|
||||
* returns whether or not UPnP is enabled and active
|
||||
* ie: if it is able to make port mappings
|
||||
*/
|
||||
bool getUPnPActive() const;
|
||||
|
||||
/**
|
||||
* Triggered when a new TLS socket is ready to use
|
||||
* @param ok If succeed
|
||||
@ -175,7 +275,11 @@ public:
|
||||
const dht::Value::Id& vid,
|
||||
const std::string& name = "");
|
||||
|
||||
JamiAccount& account;
|
||||
std::shared_ptr<ConnectionManager::Config> config_;
|
||||
|
||||
mutable std::mt19937_64 rand;
|
||||
|
||||
iOSConnectedCallback iOSConnectedCb_ {};
|
||||
|
||||
std::mutex infosMtx_ {};
|
||||
// Note: Someone can ask multiple sockets, so to avoid any race condition,
|
||||
@ -348,17 +452,16 @@ ConnectionManager::Impl::connectDeviceStartIce(
|
||||
value->user_type = "peer_request";
|
||||
|
||||
// Send connection request through DHT
|
||||
JAMI_DBG() << account << "Request connection to " << deviceId;
|
||||
account.dht()->putEncrypted(
|
||||
dht::InfoHash::get(PeerConnectionRequest::key_prefix + devicePk->getId().toString()),
|
||||
devicePk,
|
||||
value,
|
||||
[deviceId, accId = account.getAccountID()](bool ok) {
|
||||
JAMI_DEBUG("[Account {:s}] Send connection request to {:s}. Put encrypted {:s}",
|
||||
accId,
|
||||
deviceId.toString(),
|
||||
(ok ? "ok" : "failed"));
|
||||
});
|
||||
JAMI_DBG() << "Request connection to " << deviceId;
|
||||
dht()->putEncrypted(dht::InfoHash::get(PeerConnectionRequest::key_prefix
|
||||
+ devicePk->getId().toString()),
|
||||
devicePk,
|
||||
value,
|
||||
[deviceId](bool ok) {
|
||||
JAMI_DEBUG("Sent connection request to {:s}. Put encrypted {:s}",
|
||||
deviceId.toString(),
|
||||
(ok ? "ok" : "failed"));
|
||||
});
|
||||
// Wait for call to onResponse() operated by DHT
|
||||
if (isDestroying_) {
|
||||
onConnected(true); // This avoid to wait new negotiation when destroying
|
||||
@ -404,7 +507,7 @@ ConnectionManager::Impl::onResponse(const asio::error_code& ec,
|
||||
auto sdp = ice->parseIceCandidates(info->response_.ice_msg);
|
||||
|
||||
if (not ice->startIce({sdp.rem_ufrag, sdp.rem_pwd}, std::move(sdp.rem_candidates))) {
|
||||
JAMI_WARN("[Account:%s] start ICE failed", account.getAccountID().c_str());
|
||||
JAMI_WARN("start ICE failed");
|
||||
info->onConnected_(false);
|
||||
return;
|
||||
}
|
||||
@ -440,13 +543,12 @@ ConnectionManager::Impl::connectDeviceOnNegoDone(
|
||||
true);
|
||||
|
||||
// Negotiate a TLS session
|
||||
JAMI_DBG() << account
|
||||
<< "Start TLS session - Initied by connectDevice(). Launched by channel: " << name
|
||||
JAMI_DBG() << "Start TLS session - Initied by connectDevice(). Launched by channel: " << name
|
||||
<< " - device:" << deviceId << " - vid: " << vid;
|
||||
info->tls_ = std::make_unique<TlsSocketEndpoint>(std::move(endpoint),
|
||||
account.certStore(),
|
||||
account.identity(),
|
||||
account.dhParams(),
|
||||
certStore(),
|
||||
identity(),
|
||||
dhParams(),
|
||||
*cert);
|
||||
|
||||
info->tls_->setOnReady(
|
||||
@ -466,37 +568,37 @@ ConnectionManager::Impl::connectDevice(const DeviceId& deviceId,
|
||||
bool forceNewSocket,
|
||||
const std::string& connType)
|
||||
{
|
||||
if (!account.dht()) {
|
||||
if (!dht()) {
|
||||
cb(nullptr, deviceId);
|
||||
return;
|
||||
}
|
||||
if (deviceId.toString() == account.currentDeviceId()) {
|
||||
if (deviceId.toString() == identity().second->getLongId().toString()) {
|
||||
cb(nullptr, deviceId);
|
||||
return;
|
||||
}
|
||||
account.findCertificate(deviceId,
|
||||
[w = weak(),
|
||||
deviceId,
|
||||
name,
|
||||
cb = std::move(cb),
|
||||
noNewSocket,
|
||||
forceNewSocket,
|
||||
connType](const std::shared_ptr<dht::crypto::Certificate>& cert) {
|
||||
if (!cert) {
|
||||
JAMI_ERR("No valid certificate found for device %s",
|
||||
deviceId.to_c_str());
|
||||
cb(nullptr, deviceId);
|
||||
return;
|
||||
}
|
||||
if (auto shared = w.lock()) {
|
||||
shared->connectDevice(cert,
|
||||
name,
|
||||
std::move(cb),
|
||||
noNewSocket,
|
||||
forceNewSocket,
|
||||
connType);
|
||||
}
|
||||
});
|
||||
findCertificate(deviceId,
|
||||
[w = weak(),
|
||||
deviceId,
|
||||
name,
|
||||
cb = std::move(cb),
|
||||
noNewSocket,
|
||||
forceNewSocket,
|
||||
connType](const std::shared_ptr<dht::crypto::Certificate>& cert) {
|
||||
if (!cert) {
|
||||
JAMI_ERR("No valid certificate found for device %s",
|
||||
deviceId.to_c_str());
|
||||
cb(nullptr, deviceId);
|
||||
return;
|
||||
}
|
||||
if (auto shared = w.lock()) {
|
||||
shared->connectDevice(cert,
|
||||
name,
|
||||
std::move(cb),
|
||||
noNewSocket,
|
||||
forceNewSocket,
|
||||
connType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@ -522,7 +624,7 @@ ConnectionManager::Impl::connectDevice(const std::shared_ptr<dht::crypto::Certif
|
||||
cb(nullptr, deviceId);
|
||||
return;
|
||||
}
|
||||
dht::Value::Id vid = ValueIdDist(1, JAMI_ID_MAX_VAL)(sthis->account.rand);
|
||||
dht::Value::Id vid = ValueIdDist(1, JAMI_ID_MAX_VAL)(sthis->rand);
|
||||
auto isConnectingToDevice = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(sthis->connectCbsMtx_);
|
||||
@ -531,7 +633,7 @@ ConnectionManager::Impl::connectDevice(const std::shared_ptr<dht::crypto::Certif
|
||||
const auto& pendings = pendingsIt->second;
|
||||
while (pendings.connecting.find(vid) != pendings.connecting.end()
|
||||
&& pendings.waiting.find(vid) != pendings.waiting.end()) {
|
||||
vid = ValueIdDist(1, JAMI_ID_MAX_VAL)(sthis->account.rand);
|
||||
vid = ValueIdDist(1, JAMI_ID_MAX_VAL)(sthis->rand);
|
||||
}
|
||||
}
|
||||
// Check if already connecting
|
||||
@ -580,14 +682,14 @@ ConnectionManager::Impl::connectDevice(const std::shared_ptr<dht::crypto::Certif
|
||||
};
|
||||
|
||||
// If no socket exists, we need to initiate an ICE connection.
|
||||
sthis->account.getIceOptions([w,
|
||||
deviceId = std::move(deviceId),
|
||||
devicePk = std::move(devicePk),
|
||||
name = std::move(name),
|
||||
cert = std::move(cert),
|
||||
vid,
|
||||
connType,
|
||||
eraseInfo](auto&& ice_config) {
|
||||
sthis->getIceOptions([w,
|
||||
deviceId = std::move(deviceId),
|
||||
devicePk = std::move(devicePk),
|
||||
name = std::move(name),
|
||||
cert = std::move(cert),
|
||||
vid,
|
||||
connType,
|
||||
eraseInfo](auto&& ice_config) {
|
||||
auto sthis = w.lock();
|
||||
if (!sthis) {
|
||||
dht::ThreadPool::io().run([eraseInfo = std::move(eraseInfo)] { eraseInfo(); });
|
||||
@ -623,11 +725,11 @@ ConnectionManager::Impl::connectDevice(const std::shared_ptr<dht::crypto::Certif
|
||||
});
|
||||
};
|
||||
ice_config.onNegoDone = [w,
|
||||
deviceId = std::move(deviceId),
|
||||
name = std::move(name),
|
||||
cert = std::move(cert),
|
||||
vid,
|
||||
eraseInfo](bool ok) {
|
||||
deviceId,
|
||||
name,
|
||||
cert = std::move(cert),
|
||||
vid,
|
||||
eraseInfo](bool ok) {
|
||||
dht::ThreadPool::io().run([w = std::move(w),
|
||||
deviceId = std::move(deviceId),
|
||||
name = std::move(name),
|
||||
@ -650,10 +752,9 @@ ConnectionManager::Impl::connectDevice(const std::shared_ptr<dht::crypto::Certif
|
||||
}
|
||||
std::unique_lock<std::mutex> lk {info->mutex_};
|
||||
ice_config.master = false;
|
||||
ice_config.streamsCount = JamiAccount::ICE_STREAMS_COUNT;
|
||||
ice_config.compCountPerStream = JamiAccount::ICE_COMP_COUNT_PER_STREAM;
|
||||
info->ice_ = Manager::instance().getIceTransportFactory().createUTransport(
|
||||
sthis->account.getAccountID());
|
||||
ice_config.streamsCount = 1;
|
||||
ice_config.compCountPerStream = 1;
|
||||
info->ice_ = Manager::instance().getIceTransportFactory().createUTransport("");
|
||||
if (!info->ice_) {
|
||||
JAMI_ERR("Cannot initialize ICE session.");
|
||||
eraseInfo();
|
||||
@ -715,7 +816,7 @@ void
|
||||
ConnectionManager::Impl::onPeerResponse(const PeerConnectionRequest& req)
|
||||
{
|
||||
auto device = req.owner->getLongId();
|
||||
JAMI_INFO() << account << " New response received from " << device.to_c_str();
|
||||
JAMI_INFO() << "New response received from " << device.to_c_str();
|
||||
if (auto info = getInfo(device, req.id)) {
|
||||
std::lock_guard<std::mutex> lk {info->mutex_};
|
||||
info->responseReceived_ = true;
|
||||
@ -727,26 +828,24 @@ ConnectionManager::Impl::onPeerResponse(const PeerConnectionRequest& req)
|
||||
device,
|
||||
req.id));
|
||||
} else {
|
||||
JAMI_WARN() << account << " respond received, but cannot find request";
|
||||
JAMI_WARN() << "Respond received, but cannot find request";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionManager::Impl::onDhtConnected(const dht::crypto::PublicKey& devicePk)
|
||||
{
|
||||
if (!account.dht()) {
|
||||
if (!dht())
|
||||
return;
|
||||
}
|
||||
account.dht()->listen<PeerConnectionRequest>(
|
||||
dht()->listen<PeerConnectionRequest>(
|
||||
dht::InfoHash::get(PeerConnectionRequest::key_prefix + devicePk.getId().toString()),
|
||||
[w = weak()](PeerConnectionRequest&& req) {
|
||||
auto shared = w.lock();
|
||||
if (!shared)
|
||||
return false;
|
||||
if (shared->account.isMessageTreated(to_hex_string(req.id))) {
|
||||
// Message already treated. Just ignore
|
||||
// Message already treated. Just ignore
|
||||
if (shared->isMessageTreated(to_hex_string(req.id)))
|
||||
return true;
|
||||
}
|
||||
if (req.isAnswer) {
|
||||
JAMI_DBG() << "Received request answer from " << req.owner->getLongId();
|
||||
} else {
|
||||
@ -756,7 +855,7 @@ ConnectionManager::Impl::onDhtConnected(const dht::crypto::PublicKey& devicePk)
|
||||
shared->onPeerResponse(req);
|
||||
} else {
|
||||
// Async certificate checking
|
||||
shared->account.findCertificate(
|
||||
shared->findCertificate(
|
||||
req.from,
|
||||
[w, req = std::move(req)](
|
||||
const std::shared_ptr<dht::crypto::Certificate>& cert) mutable {
|
||||
@ -764,21 +863,15 @@ ConnectionManager::Impl::onDhtConnected(const dht::crypto::PublicKey& devicePk)
|
||||
if (!shared)
|
||||
return;
|
||||
dht::InfoHash peer_h;
|
||||
if (AccountManager::foundPeerDevice(cert, peer_h)) {
|
||||
if (foundPeerDevice(cert, peer_h)) {
|
||||
#if TARGET_OS_IOS
|
||||
if ((req.connType == "videoCall" || req.connType == "audioCall")
|
||||
&& jami::Manager::instance().isIOSExtension) {
|
||||
bool hasVideo = req.connType == "videoCall";
|
||||
emitSignal<libjami::ConversationSignal::CallConnectionRequest>(
|
||||
shared->account.getAccountID(), peer_h.toString(), hasVideo);
|
||||
if (shared->iOSConnectedCb_(req.connType, peer_h))
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
shared->onDhtPeerRequest(req, cert);
|
||||
} else {
|
||||
JAMI_WARN()
|
||||
<< shared->account << "Rejected untrusted connection request from "
|
||||
<< req.owner->getLongId();
|
||||
JAMI_WARN() << "Rejected untrusted connection request from "
|
||||
<< req.owner->getLongId();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -860,17 +953,16 @@ ConnectionManager::Impl::answerTo(IceTransport& ice,
|
||||
auto value = std::make_shared<dht::Value>(std::move(val));
|
||||
value->user_type = "peer_request";
|
||||
|
||||
JAMI_DBG() << account << "[CNX] connection accepted, DHT reply to " << from->getLongId();
|
||||
account.dht()->putEncrypted(
|
||||
dht::InfoHash::get(PeerConnectionRequest::key_prefix + from->getId().toString()),
|
||||
from,
|
||||
value,
|
||||
[from, accId = account.getAccountID()](bool ok) {
|
||||
JAMI_DEBUG("[Account {:s}] Answer to connection request from {:s}. Put encrypted {:s}",
|
||||
accId,
|
||||
from->getLongId().toString(),
|
||||
(ok ? "ok" : "failed"));
|
||||
});
|
||||
JAMI_DBG() << "[CNX] connection accepted, DHT reply to " << from->getLongId();
|
||||
dht()->putEncrypted(dht::InfoHash::get(PeerConnectionRequest::key_prefix
|
||||
+ from->getId().toString()),
|
||||
from,
|
||||
value,
|
||||
[from](bool ok) {
|
||||
JAMI_DEBUG("Answer to connection request from {:s}. Put encrypted {:s}",
|
||||
from->getLongId().toString(),
|
||||
(ok ? "ok" : "failed"));
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
@ -893,7 +985,7 @@ ConnectionManager::Impl::onRequestStartIce(const PeerConnectionRequest& req)
|
||||
auto sdp = ice->parseIceCandidates(req.ice_msg);
|
||||
answerTo(*ice, req.id, req.owner);
|
||||
if (not ice->startIce({sdp.rem_ufrag, sdp.rem_pwd}, std::move(sdp.rem_candidates))) {
|
||||
JAMI_ERR("[Account:%s] start ICE failed - fallback to TURN", account.getAccountID().c_str());
|
||||
JAMI_ERR("Start ICE failed - fallback to TURN");
|
||||
ice = nullptr;
|
||||
if (connReadyCb_)
|
||||
connReadyCb_(deviceId, "", nullptr);
|
||||
@ -924,18 +1016,18 @@ ConnectionManager::Impl::onRequestOnNegoDone(const PeerConnectionRequest& req)
|
||||
|
||||
// init TLS session
|
||||
auto ph = req.from;
|
||||
JAMI_DBG() << account << "Start TLS session - Initied by DHT request. Device:" << req.from
|
||||
JAMI_DBG() << "Start TLS session - Initied by DHT request. Device:" << req.from
|
||||
<< " - vid: " << req.id;
|
||||
info->tls_ = std::make_unique<TlsSocketEndpoint>(
|
||||
std::move(endpoint),
|
||||
account.certStore(),
|
||||
account.identity(),
|
||||
account.dhParams(),
|
||||
certStore(),
|
||||
identity(),
|
||||
dhParams(),
|
||||
[ph, w = weak()](const dht::crypto::Certificate& cert) {
|
||||
auto shared = w.lock();
|
||||
if (!shared)
|
||||
return false;
|
||||
auto crt = shared->account.certStore().getCertificate(cert.getLongId().toString());
|
||||
auto crt = shared->certStore().getCertificate(cert.getLongId().toString());
|
||||
if (!crt)
|
||||
return false;
|
||||
return crt->getPacked() == cert.getPacked();
|
||||
@ -954,16 +1046,14 @@ ConnectionManager::Impl::onDhtPeerRequest(const PeerConnectionRequest& req,
|
||||
const std::shared_ptr<dht::crypto::Certificate>& /*cert*/)
|
||||
{
|
||||
auto deviceId = req.owner->getLongId();
|
||||
JAMI_INFO() << account << "New connection requested by " << deviceId;
|
||||
JAMI_INFO() << "New connection requested by " << deviceId;
|
||||
if (!iceReqCb_ || !iceReqCb_(deviceId)) {
|
||||
JAMI_INFO("[Account:%s] refuse connection from %s",
|
||||
account.getAccountID().c_str(),
|
||||
deviceId.toString().c_str());
|
||||
JAMI_INFO("Refuse connection from %s", deviceId.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Because the connection is accepted, create an ICE socket.
|
||||
account.getIceOptions([w = weak(), req, deviceId](auto&& ice_config) {
|
||||
getIceOptions([w = weak(), req, deviceId](auto&& ice_config) {
|
||||
auto shared = w.lock();
|
||||
if (!shared)
|
||||
return;
|
||||
@ -1025,15 +1115,12 @@ ConnectionManager::Impl::onDhtPeerRequest(const PeerConnectionRequest& req,
|
||||
std::lock_guard<std::mutex> lk(shared->infosMtx_);
|
||||
shared->infos_[{deviceId, req.id}] = info;
|
||||
}
|
||||
JAMI_INFO("[Account:%s] accepting connection from %s",
|
||||
shared->account.getAccountID().c_str(),
|
||||
deviceId.toString().c_str());
|
||||
JAMI_INFO("accepting connection from %s", deviceId.toString().c_str());
|
||||
std::unique_lock<std::mutex> lk {info->mutex_};
|
||||
ice_config.streamsCount = JamiAccount::ICE_STREAMS_COUNT;
|
||||
ice_config.compCountPerStream = JamiAccount::ICE_COMP_COUNT_PER_STREAM;
|
||||
ice_config.streamsCount = 1;
|
||||
ice_config.compCountPerStream = 1; // TCP
|
||||
ice_config.master = true;
|
||||
info->ice_ = Manager::instance().getIceTransportFactory().createUTransport(
|
||||
shared->account.getAccountID());
|
||||
info->ice_ = Manager::instance().getIceTransportFactory().createUTransport("");
|
||||
if (not info->ice_) {
|
||||
JAMI_ERR("Cannot initialize ICE session.");
|
||||
eraseInfo();
|
||||
@ -1094,8 +1181,296 @@ ConnectionManager::Impl::addNewMultiplexedSocket(const CallbackId& id, const std
|
||||
});
|
||||
}
|
||||
|
||||
ConnectionManager::ConnectionManager(JamiAccount& account)
|
||||
: pimpl_ {std::make_shared<Impl>(account)}
|
||||
const std::shared_future<tls::DhParams>
|
||||
ConnectionManager::Impl::dhParams() const
|
||||
{
|
||||
return dht::ThreadPool::computation().get<tls::DhParams>(
|
||||
std::bind(tls::DhParams::loadDhParams, fileutils::get_cache_dir() + DIR_SEPARATOR_STR "dhParams"));
|
||||
;
|
||||
}
|
||||
|
||||
template<typename ID = dht::Value::Id>
|
||||
std::set<ID, std::less<>>
|
||||
loadIdList(const std::string& path)
|
||||
{
|
||||
std::set<ID, std::less<>> ids;
|
||||
std::ifstream file = fileutils::ifstream(path);
|
||||
if (!file.is_open()) {
|
||||
JAMI_DBG("Could not load %s", path.c_str());
|
||||
return ids;
|
||||
}
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
if constexpr (std::is_same<ID, std::string>::value) {
|
||||
ids.emplace(std::move(line));
|
||||
} else if constexpr (std::is_integral<ID>::value) {
|
||||
ID vid;
|
||||
if (auto [p, ec] = std::from_chars(line.data(), line.data() + line.size(), vid, 16);
|
||||
ec == std::errc()) {
|
||||
ids.emplace(vid);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
template<typename List = std::set<dht::Value::Id>>
|
||||
void
|
||||
saveIdList(const std::string& path, const List& ids)
|
||||
{
|
||||
std::ofstream file = fileutils::ofstream(path, std::ios::trunc | std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
JAMI_ERR("Could not save to %s", path.c_str());
|
||||
return;
|
||||
}
|
||||
for (auto& c : ids)
|
||||
file << std::hex << c << "\n";
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionManager::Impl::loadTreatedMessages()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(messageMutex_);
|
||||
auto path = config_->cachePath + DIR_SEPARATOR_STR "treatedMessages";
|
||||
treatedMessages_ = loadIdList<std::string>(path);
|
||||
if (treatedMessages_.empty()) {
|
||||
auto messages = loadIdList(path);
|
||||
for (const auto& m : messages)
|
||||
treatedMessages_.emplace(to_hex_string(m));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionManager::Impl::saveTreatedMessages() const
|
||||
{
|
||||
dht::ThreadPool::io().run([w = weak()]() {
|
||||
if (auto sthis = w.lock()) {
|
||||
auto& this_ = *sthis;
|
||||
std::lock_guard<std::mutex> lock(this_.messageMutex_);
|
||||
fileutils::check_dir(this_.config_->cachePath.c_str());
|
||||
saveIdList<decltype(this_.treatedMessages_)>(this_.config_->cachePath
|
||||
+ DIR_SEPARATOR_STR "treatedMessages",
|
||||
this_.treatedMessages_);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
ConnectionManager::Impl::isMessageTreated(std::string_view id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(messageMutex_);
|
||||
auto res = treatedMessages_.emplace(id);
|
||||
if (res.second) {
|
||||
saveTreatedMessages();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns whether or not UPnP is enabled and active_
|
||||
* ie: if it is able to make port mappings
|
||||
*/
|
||||
bool
|
||||
ConnectionManager::Impl::getUPnPActive() const
|
||||
{
|
||||
return config_->getUPnPActive();
|
||||
}
|
||||
|
||||
IpAddr
|
||||
ConnectionManager::Impl::getPublishedIpAddress(uint16_t family) const
|
||||
{
|
||||
if (family == AF_INET)
|
||||
return publishedIp_[0];
|
||||
if (family == AF_INET6)
|
||||
return publishedIp_[1];
|
||||
|
||||
assert(family == AF_UNSPEC);
|
||||
|
||||
// If family is not set, prefere IPv4 if available. It's more
|
||||
// likely to succeed behind NAT.
|
||||
if (publishedIp_[0])
|
||||
return publishedIp_[0];
|
||||
if (publishedIp_[1])
|
||||
return publishedIp_[1];
|
||||
return {};
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionManager::Impl::setPublishedAddress(const IpAddr& ip_addr)
|
||||
{
|
||||
if (ip_addr.getFamily() == AF_INET) {
|
||||
publishedIp_[0] = ip_addr;
|
||||
} else {
|
||||
publishedIp_[1] = ip_addr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionManager::Impl::storeActiveIpAddress(std::function<void()>&& cb)
|
||||
{
|
||||
dht()->getPublicAddress([this, cb = std::move(cb)](std::vector<dht::SockAddr>&& results) {
|
||||
bool hasIpv4 {false}, hasIpv6 {false};
|
||||
for (auto& result : results) {
|
||||
auto family = result.getFamily();
|
||||
if (family == AF_INET) {
|
||||
if (not hasIpv4) {
|
||||
hasIpv4 = true;
|
||||
JAMI_DBG("Store DHT public IPv4 address : %s", result.toString().c_str());
|
||||
setPublishedAddress(*result.get());
|
||||
if (config_->upnpCtrl_) {
|
||||
config_->upnpCtrl_->setPublicAddress(*result.get());
|
||||
}
|
||||
}
|
||||
} else if (family == AF_INET6) {
|
||||
if (not hasIpv6) {
|
||||
hasIpv6 = true;
|
||||
JAMI_DBG("Store DHT public IPv6 address : %s", result.toString().c_str());
|
||||
setPublishedAddress(*result.get());
|
||||
}
|
||||
}
|
||||
if (hasIpv4 and hasIpv6)
|
||||
break;
|
||||
}
|
||||
if (cb)
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionManager::Impl::getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept
|
||||
{
|
||||
storeActiveIpAddress([this, cb = std::move(cb)] {
|
||||
IceTransportOptions opts = ConnectionManager::Impl::getIceOptions();
|
||||
auto publishedAddr = getPublishedIpAddress();
|
||||
|
||||
if (publishedAddr) {
|
||||
auto interfaceAddr = ip_utils::getInterfaceAddr(getLocalInterface(),
|
||||
publishedAddr.getFamily());
|
||||
if (interfaceAddr) {
|
||||
opts.accountLocalAddr = interfaceAddr;
|
||||
opts.accountPublicAddr = publishedAddr;
|
||||
}
|
||||
}
|
||||
if (cb)
|
||||
cb(std::move(opts));
|
||||
});
|
||||
}
|
||||
|
||||
IceTransportOptions
|
||||
ConnectionManager::Impl::getIceOptions() const noexcept
|
||||
{
|
||||
IceTransportOptions opts;
|
||||
opts.upnpEnable = getUPnPActive();
|
||||
|
||||
if (config_->stunEnabled_)
|
||||
opts.stunServers.emplace_back(StunServerInfo().setUri(config_->stunServer_));
|
||||
if (config_->turnEnabled_) {
|
||||
auto cached = false;
|
||||
std::lock_guard<std::mutex> lk(config_->cachedTurnMutex_);
|
||||
cached = config_->cacheTurnV4_ || config_->cacheTurnV6_;
|
||||
if (config_->cacheTurnV4_ && *config_->cacheTurnV4_) {
|
||||
opts.turnServers.emplace_back(TurnServerInfo()
|
||||
.setUri(config_->cacheTurnV4_->toString(true))
|
||||
.setUsername(config_->turnServerUserName_)
|
||||
.setPassword(config_->turnServerPwd_)
|
||||
.setRealm(config_->turnServerRealm_));
|
||||
}
|
||||
// NOTE: first test with ipv6 turn was not concluant and resulted in multiple
|
||||
// co issues. So this needs some debug. for now just disable
|
||||
// if (cacheTurnV6_ && *cacheTurnV6_) {
|
||||
// opts.turnServers.emplace_back(TurnServerInfo()
|
||||
// .setUri(cacheTurnV6_->toString(true))
|
||||
// .setUsername(turnServerUserName_)
|
||||
// .setPassword(turnServerPwd_)
|
||||
// .setRealm(turnServerRealm_));
|
||||
//}
|
||||
// Nothing cached, so do the resolution
|
||||
if (!cached) {
|
||||
opts.turnServers.emplace_back(TurnServerInfo()
|
||||
.setUri(config_->turnServer_)
|
||||
.setUsername(config_->turnServerUserName_)
|
||||
.setPassword(config_->turnServerPwd_)
|
||||
.setRealm(config_->turnServerRealm_));
|
||||
}
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
|
||||
bool
|
||||
ConnectionManager::Impl::foundPeerDevice(const std::shared_ptr<dht::crypto::Certificate>& crt,
|
||||
dht::InfoHash& account_id)
|
||||
{
|
||||
if (not crt)
|
||||
return false;
|
||||
|
||||
auto top_issuer = crt;
|
||||
while (top_issuer->issuer)
|
||||
top_issuer = top_issuer->issuer;
|
||||
|
||||
// Device certificate can't be self-signed
|
||||
if (top_issuer == crt) {
|
||||
JAMI_WARN("Found invalid peer device: %s", crt->getLongId().toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check peer certificate chain
|
||||
// Trust store with top issuer as the only CA
|
||||
dht::crypto::TrustList peer_trust;
|
||||
peer_trust.add(*top_issuer);
|
||||
if (not peer_trust.verify(*crt)) {
|
||||
JAMI_WARN("Found invalid peer device: %s", crt->getLongId().toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check cached OCSP response
|
||||
if (crt->ocspResponse and crt->ocspResponse->getCertificateStatus() != GNUTLS_OCSP_CERT_GOOD) {
|
||||
JAMI_ERR("Certificate %s is disabled by cached OCSP response", crt->getLongId().to_c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
account_id = crt->issuer->getId();
|
||||
JAMI_WARN("Found peer device: %s account:%s CA:%s",
|
||||
crt->getLongId().toString().c_str(),
|
||||
account_id.toString().c_str(),
|
||||
top_issuer->getId().toString().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ConnectionManager::Impl::findCertificate(
|
||||
const dht::PkId& id, std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
|
||||
{
|
||||
if (auto cert = certStore().getCertificate(id.toString())) {
|
||||
if (cb)
|
||||
cb(cert);
|
||||
} else if (cb)
|
||||
cb(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ConnectionManager::Impl::findCertificate(const dht::InfoHash& h,
|
||||
std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
|
||||
{
|
||||
if (auto cert = certStore().getCertificate(h.toString())) {
|
||||
if (cb)
|
||||
cb(cert);
|
||||
} else {
|
||||
dht()->findCertificate(h,
|
||||
[cb = std::move(cb), this](
|
||||
const std::shared_ptr<dht::crypto::Certificate>& crt) {
|
||||
if (crt)
|
||||
certStore().pinCertificate(crt);
|
||||
if (cb)
|
||||
cb(crt);
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ConnectionManager::ConnectionManager(std::shared_ptr<ConnectionManager::Config> config_)
|
||||
: pimpl_ {std::make_shared<Impl>(config_)}
|
||||
{}
|
||||
|
||||
ConnectionManager::~ConnectionManager()
|
||||
@ -1144,7 +1519,7 @@ ConnectionManager::closeConnectionsWith(const std::string& peerUri)
|
||||
for (auto iter = pimpl_->infos_.begin(); iter != pimpl_->infos_.end();) {
|
||||
auto const& [key, value] = *iter;
|
||||
auto deviceId = key.first;
|
||||
auto cert = pimpl_->account.certStore().getCertificate(deviceId.toString());
|
||||
auto cert = pimpl_->certStore().getCertificate(deviceId.toString());
|
||||
if (cert && cert->issuer && peerUri == cert->issuer->getId().toString()) {
|
||||
connInfos.emplace_back(value);
|
||||
peersDevices.emplace(deviceId);
|
||||
@ -1197,6 +1572,12 @@ ConnectionManager::onConnectionReady(ConnectionReadyCallback&& cb)
|
||||
pimpl_->connReadyCb_ = std::move(cb);
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionManager::oniOSConnected(iOSConnectedCallback&& cb)
|
||||
{
|
||||
pimpl_->iOSConnectedCb_ = std::move(cb);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
ConnectionManager::activeSockets() const
|
||||
{
|
||||
@ -1208,16 +1589,12 @@ void
|
||||
ConnectionManager::monitor() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(pimpl_->infosMtx_);
|
||||
JAMI_DBG("ConnectionManager for account %s (%s), current status:",
|
||||
pimpl_->account.getAccountID().c_str(),
|
||||
pimpl_->account.getUserUri().c_str());
|
||||
JAMI_DBG("ConnectionManager current status:");
|
||||
for (const auto& [_, ci] : pimpl_->infos_) {
|
||||
if (ci->socket_)
|
||||
ci->socket_->monitor();
|
||||
}
|
||||
JAMI_DBG("ConnectionManager for account %s (%s), end status.",
|
||||
pimpl_->account.getAccountID().c_str(),
|
||||
pimpl_->account.getUserUri().c_str());
|
||||
JAMI_DBG("ConnectionManager end status.");
|
||||
}
|
||||
|
||||
void
|
||||
@ -1230,4 +1607,55 @@ ConnectionManager::connectivityChanged()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionManager::getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept
|
||||
{
|
||||
pimpl_->getIceOptions(std::move(cb));
|
||||
}
|
||||
|
||||
IceTransportOptions
|
||||
ConnectionManager::getIceOptions() const noexcept
|
||||
{
|
||||
return pimpl_->getIceOptions();
|
||||
}
|
||||
|
||||
IpAddr
|
||||
ConnectionManager::getPublishedIpAddress(uint16_t family) const
|
||||
{
|
||||
return pimpl_->getPublishedIpAddress(family);
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionManager::setPublishedAddress(const IpAddr& ip_addr)
|
||||
{
|
||||
pimpl_->setPublishedAddress(ip_addr);
|
||||
}
|
||||
|
||||
void
|
||||
ConnectionManager::storeActiveIpAddress(std::function<void()>&& cb)
|
||||
{
|
||||
pimpl_->storeActiveIpAddress(std::move(cb));
|
||||
}
|
||||
|
||||
std::shared_ptr<ConnectionManager::Config>
|
||||
ConnectionManager::getConfig()
|
||||
{
|
||||
return pimpl_->config_;
|
||||
}
|
||||
|
||||
bool
|
||||
ConnectionManager::findCertificate(const dht::PkId& id,
|
||||
std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
|
||||
{
|
||||
return pimpl_->findCertificate(id, std::move(cb));
|
||||
}
|
||||
|
||||
bool
|
||||
ConnectionManager::findCertificate(
|
||||
const dht::InfoHash& h,
|
||||
std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
|
||||
{
|
||||
return pimpl_->findCertificate(h, std::move(cb));
|
||||
}
|
||||
|
||||
} // namespace jami
|
||||
|
@ -27,10 +27,11 @@
|
||||
#include <opendht/default_types.h>
|
||||
|
||||
#include "multiplexed_socket.h"
|
||||
#include "connectivity/security/diffie-hellman.h"
|
||||
#include "connectivity/upnp/upnp_control.h"
|
||||
|
||||
namespace jami {
|
||||
|
||||
class JamiAccount;
|
||||
class ChannelSocket;
|
||||
class ConnectionManager;
|
||||
|
||||
@ -69,6 +70,9 @@ using ConnectCallback = std::function<void(const std::shared_ptr<ChannelSocket>&
|
||||
using ConnectionReadyCallback = std::function<
|
||||
void(const DeviceId&, const std::string& /* channel_name */, std::shared_ptr<ChannelSocket>)>;
|
||||
|
||||
using iOSConnectedCallback
|
||||
= std::function<bool(const std::string& /* connType */, dht::InfoHash /* peer_h */)>;
|
||||
|
||||
/**
|
||||
* Manages connections to other devices
|
||||
* @note the account MUST be valid if ConnectionManager lives
|
||||
@ -76,7 +80,9 @@ using ConnectionReadyCallback = std::function<
|
||||
class ConnectionManager
|
||||
{
|
||||
public:
|
||||
ConnectionManager(JamiAccount& account);
|
||||
class Config;
|
||||
|
||||
ConnectionManager(std::shared_ptr<Config> config_);
|
||||
~ConnectionManager();
|
||||
|
||||
/**
|
||||
@ -145,6 +151,12 @@ public:
|
||||
*/
|
||||
void onConnectionReady(ConnectionReadyCallback&& cb);
|
||||
|
||||
/**
|
||||
* Trigger cb when connection with peer is ready for iOS devices
|
||||
* @param cb Callback to trigger
|
||||
*/
|
||||
void oniOSConnected(iOSConnectedCallback&& cb);
|
||||
|
||||
/**
|
||||
* @return the number of active sockets
|
||||
*/
|
||||
@ -160,10 +172,119 @@ public:
|
||||
*/
|
||||
void connectivityChanged();
|
||||
|
||||
/**
|
||||
* Create and return ICE options.
|
||||
*/
|
||||
void getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept;
|
||||
IceTransportOptions getIceOptions() const noexcept;
|
||||
|
||||
/**
|
||||
* Get the published IP address, fallbacks to NAT if family is unspecified
|
||||
* Prefers the usage of IPv4 if possible.
|
||||
*/
|
||||
IpAddr getPublishedIpAddress(uint16_t family = PF_UNSPEC) const;
|
||||
|
||||
/**
|
||||
* Set published IP address according to given family
|
||||
*/
|
||||
void setPublishedAddress(const IpAddr& ip_addr);
|
||||
|
||||
/**
|
||||
* Store the local/public addresses used to register
|
||||
*/
|
||||
void storeActiveIpAddress(std::function<void()>&& cb = {});
|
||||
|
||||
std::shared_ptr<Config> getConfig();
|
||||
|
||||
bool findCertificate(const dht::PkId& id,
|
||||
std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb);
|
||||
bool findCertificate(
|
||||
const dht::InfoHash& h,
|
||||
std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb = {});
|
||||
|
||||
private:
|
||||
ConnectionManager() = delete;
|
||||
class Impl;
|
||||
std::shared_ptr<Impl> pimpl_;
|
||||
};
|
||||
|
||||
class ConnectionManager::Config : public std::enable_shared_from_this<ConnectionManager::Config>
|
||||
{
|
||||
public:
|
||||
explicit Config(std::shared_ptr<dht::DhtRunner> dht_,
|
||||
const dht::crypto::Identity& id_,
|
||||
tls::CertificateStore& certStore_,
|
||||
std::shared_ptr<jami::upnp::Controller> upnpCtrl_,
|
||||
std::string turnServer_,
|
||||
std::string turnServerUserName_,
|
||||
std::string turnServerPwd_,
|
||||
std::string turnServerRealm_,
|
||||
bool turnEnabled_)
|
||||
: dht_ {std::move(dht_)}
|
||||
, id_ {id_}
|
||||
, upnpCtrl_ {std::move(upnpCtrl_)}
|
||||
, turnServer_ {std::move(turnServer_)}
|
||||
, turnServerUserName_ {std::move(turnServerUserName_)}
|
||||
, turnServerPwd_ {std::move(turnServerPwd_)}
|
||||
, turnServerRealm_ {std::move(turnServerRealm_)}
|
||||
, turnEnabled_ {turnEnabled_}
|
||||
, certStore_(&certStore_)
|
||||
{}
|
||||
|
||||
~Config() {}
|
||||
|
||||
/**
|
||||
* Determine if STUN public address resolution is required to register this account. In this
|
||||
* case a STUN server hostname must be specified.
|
||||
*/
|
||||
bool stunEnabled_ {false};
|
||||
|
||||
/**
|
||||
* The STUN server hostname (optional), used to provide the public IP address in case the
|
||||
* softphone stay behind a NAT.
|
||||
*/
|
||||
std::string stunServer_ {};
|
||||
|
||||
/**
|
||||
* Determine if TURN public address resolution is required to register this account. In this
|
||||
* case a TURN server hostname must be specified.
|
||||
*/
|
||||
bool turnEnabled_ {false};
|
||||
|
||||
/**
|
||||
* The TURN server hostname (optional), used to provide the public IP address in case the
|
||||
* softphone stay behind a NAT.
|
||||
*/
|
||||
std::string turnServer_;
|
||||
std::string turnServerUserName_;
|
||||
std::string turnServerPwd_;
|
||||
std::string turnServerRealm_;
|
||||
|
||||
mutable std::mutex cachedTurnMutex_ {};
|
||||
std::unique_ptr<IpAddr> cacheTurnV4_ {};
|
||||
std::unique_ptr<IpAddr> cacheTurnV6_ {};
|
||||
|
||||
std::string cachePath {};
|
||||
|
||||
std::shared_ptr<dht::DhtRunner> dht_;
|
||||
const dht::crypto::Identity& id_;
|
||||
|
||||
const dht::crypto::Identity& identity() const { return id_; }
|
||||
|
||||
tls::CertificateStore* certStore_;
|
||||
|
||||
/**
|
||||
* UPnP IGD controller and the mutex to access it
|
||||
*/
|
||||
bool upnpEnabled_;
|
||||
mutable std::mutex upnp_mtx {};
|
||||
std::shared_ptr<jami::upnp::Controller> upnpCtrl_;
|
||||
|
||||
/**
|
||||
* returns whether or not UPnP is enabled and active
|
||||
* ie: if it is able to make port mappings
|
||||
*/
|
||||
bool getUPnPActive() const;
|
||||
};
|
||||
|
||||
} // namespace jami
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "diffie-hellman.h"
|
||||
#include "logger.h"
|
||||
#include "fileutils.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <ciso646>
|
||||
@ -106,5 +107,33 @@ DhParams::generate()
|
||||
return params;
|
||||
}
|
||||
|
||||
DhParams
|
||||
DhParams::loadDhParams(const std::string& path)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(fileutils::getFileLock(path));
|
||||
try {
|
||||
// writeTime throw exception if file doesn't exist
|
||||
auto duration = std::chrono::system_clock::now() - fileutils::writeTime(path);
|
||||
if (duration >= std::chrono::hours(24 * 3)) // file is valid only 3 days
|
||||
throw std::runtime_error("file too old");
|
||||
|
||||
JAMI_DBG("Loading DhParams from file '%s'", path.c_str());
|
||||
return {fileutils::loadFile(path)};
|
||||
} catch (const std::exception& e) {
|
||||
JAMI_DBG("Failed to load DhParams file '%s': %s", path.c_str(), e.what());
|
||||
if (auto params = tls::DhParams::generate()) {
|
||||
try {
|
||||
fileutils::saveFile(path, params.serialize(), 0600);
|
||||
JAMI_DBG("Saved DhParams to file '%s'", path.c_str());
|
||||
} catch (const std::exception& ex) {
|
||||
JAMI_WARN("Failed to save DhParams in file '%s': %s", path.c_str(), ex.what());
|
||||
}
|
||||
return params;
|
||||
}
|
||||
JAMI_ERR("Can't generate DH params.");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tls
|
||||
} // namespace jami
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace jami {
|
||||
namespace tls {
|
||||
@ -60,6 +61,8 @@ public:
|
||||
|
||||
static DhParams generate();
|
||||
|
||||
static DhParams loadDhParams(const std::string& path);
|
||||
|
||||
private:
|
||||
std::unique_ptr<gnutls_dh_params_int, decltype(gnutls_dh_params_deinit)*>
|
||||
params_ {nullptr, gnutls_dh_params_deinit};
|
||||
|
@ -381,7 +381,7 @@ JamiAccount::newOutgoingCall(std::string_view toUrl, const std::vector<libjami::
|
||||
return {};
|
||||
|
||||
auto uri = Uri(toUrl);
|
||||
getIceOptions([call, w = weak(), uri = std::move(uri)](auto&& opts) {
|
||||
connectionManager_->getIceOptions([call, w = weak(), uri = std::move(uri)](auto&& opts) {
|
||||
if (call->isIceEnabled()) {
|
||||
if (not call->createIceMediaTransport(false)
|
||||
or not call->initIceMediaTransport(true, std::forward<IceTransportOptions>(opts))) {
|
||||
@ -752,8 +752,9 @@ JamiAccount::onConnectedOutgoingCall(const std::shared_ptr<SIPCall>& call,
|
||||
|
||||
const auto localAddress = ip_utils::getInterfaceAddr(getLocalInterface(), target.getFamily());
|
||||
|
||||
IpAddr addrSdp = getPublishedSameasLocal() ? localAddress
|
||||
: getPublishedIpAddress(target.getFamily());
|
||||
IpAddr addrSdp = getPublishedSameasLocal()
|
||||
? localAddress
|
||||
: connectionManager_->getPublishedIpAddress(target.getFamily());
|
||||
|
||||
// fallback on local address
|
||||
if (not addrSdp)
|
||||
@ -1928,7 +1929,7 @@ JamiAccount::doRegister_()
|
||||
connectionManager_->onICERequest([this](const DeviceId& deviceId) {
|
||||
std::promise<bool> accept;
|
||||
std::future<bool> fut = accept.get_future();
|
||||
accountManager_->findCertificate(
|
||||
connectionManager_->findCertificate(
|
||||
deviceId, [this, &accept](const std::shared_ptr<dht::crypto::Certificate>& cert) {
|
||||
dht::InfoHash peer_account_id;
|
||||
auto res = accountManager_->onPeerCertificate(cert,
|
||||
@ -2336,7 +2337,8 @@ JamiAccount::setRegistrationState(RegistrationState state,
|
||||
if (state == RegistrationState::REGISTERED) {
|
||||
JAMI_WARNING("[Account {}] connected", getAccountID());
|
||||
turnCache_->refresh();
|
||||
storeActiveIpAddress();
|
||||
if (connectionManager_)
|
||||
connectionManager_->storeActiveIpAddress();
|
||||
} else if (state == RegistrationState::TRYING) {
|
||||
JAMI_WARNING("[Account {}] connecting…", getAccountID());
|
||||
} else {
|
||||
@ -2362,11 +2364,12 @@ JamiAccount::connectivityChanged()
|
||||
dht_->connectivityChanged();
|
||||
{
|
||||
std::lock_guard<std::mutex> lkCM(connManagerMtx_);
|
||||
if (connectionManager_)
|
||||
if (connectionManager_) {
|
||||
connectionManager_->connectivityChanged();
|
||||
// reset cache
|
||||
connectionManager_->setPublishedAddress({});
|
||||
}
|
||||
}
|
||||
// reset cache
|
||||
setPublishedAddress({});
|
||||
}
|
||||
|
||||
bool
|
||||
@ -2374,8 +2377,8 @@ JamiAccount::findCertificate(
|
||||
const dht::InfoHash& h,
|
||||
std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
|
||||
{
|
||||
if (accountManager_)
|
||||
return accountManager_->findCertificate(h, std::move(cb));
|
||||
if (connectionManager_)
|
||||
return connectionManager_->findCertificate(h, std::move(cb));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2383,16 +2386,16 @@ bool
|
||||
JamiAccount::findCertificate(
|
||||
const dht::PkId& id, std::function<void(const std::shared_ptr<dht::crypto::Certificate>&)>&& cb)
|
||||
{
|
||||
if (accountManager_)
|
||||
return accountManager_->findCertificate(id, std::move(cb));
|
||||
if (connectionManager_)
|
||||
return connectionManager_->findCertificate(id, std::move(cb));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
JamiAccount::findCertificate(const std::string& crt_id)
|
||||
{
|
||||
if (accountManager_)
|
||||
return accountManager_->findCertificate(dht::InfoHash(crt_id));
|
||||
if (connectionManager_)
|
||||
return connectionManager_->findCertificate(dht::InfoHash(crt_id));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2525,34 +2528,6 @@ JamiAccount::getKnownDevices() const
|
||||
return ids;
|
||||
}
|
||||
|
||||
tls::DhParams
|
||||
JamiAccount::loadDhParams(std::string path)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(fileutils::getFileLock(path));
|
||||
try {
|
||||
// writeTime throw exception if file doesn't exist
|
||||
auto duration = clock::now() - fileutils::writeTime(path);
|
||||
if (duration >= std::chrono::hours(24 * 3)) // file is valid only 3 days
|
||||
throw std::runtime_error("file too old");
|
||||
|
||||
JAMI_DBG("Loading DhParams from file '%s'", path.c_str());
|
||||
return {fileutils::loadFile(path)};
|
||||
} catch (const std::exception& e) {
|
||||
JAMI_DBG("Failed to load DhParams file '%s': %s", path.c_str(), e.what());
|
||||
if (auto params = tls::DhParams::generate()) {
|
||||
try {
|
||||
fileutils::saveFile(path, params.serialize(), 0600);
|
||||
JAMI_DBG("Saved DhParams to file '%s'", path.c_str());
|
||||
} catch (const std::exception& ex) {
|
||||
JAMI_WARN("Failed to save DhParams in file '%s': %s", path.c_str(), ex.what());
|
||||
}
|
||||
return params;
|
||||
}
|
||||
JAMI_ERR("Can't generate DH params.");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
JamiAccount::loadCachedUrl(const std::string& url,
|
||||
const std::string& cachePath,
|
||||
@ -2687,7 +2662,7 @@ JamiAccount::generateDhParams()
|
||||
// make sure cachePath_ is writable
|
||||
fileutils::check_dir(cachePath_.c_str(), 0700);
|
||||
dhParams_ = dht::ThreadPool::computation().get<tls::DhParams>(
|
||||
std::bind(loadDhParams, cachePath_ + DIR_SEPARATOR_STR "dhParams"));
|
||||
std::bind(tls::DhParams::loadDhParams, cachePath_ + DIR_SEPARATOR_STR "dhParams"));
|
||||
}
|
||||
|
||||
MatchRank
|
||||
@ -3332,59 +3307,10 @@ JamiAccount::onIsComposing(const std::string& conversationId,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
JamiAccount::getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept
|
||||
IceTransportOptions
|
||||
JamiAccount::getIceOptions() const noexcept
|
||||
{
|
||||
storeActiveIpAddress([this, cb = std::move(cb)] {
|
||||
auto opts = SIPAccountBase::getIceOptions();
|
||||
auto publishedAddr = getPublishedIpAddress();
|
||||
|
||||
if (publishedAddr) {
|
||||
auto interfaceAddr = ip_utils::getInterfaceAddr(getLocalInterface(),
|
||||
publishedAddr.getFamily());
|
||||
if (interfaceAddr) {
|
||||
opts.accountLocalAddr = interfaceAddr;
|
||||
opts.accountPublicAddr = publishedAddr;
|
||||
}
|
||||
}
|
||||
if (cb)
|
||||
cb(std::move(opts));
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
JamiAccount::storeActiveIpAddress(std::function<void()>&& cb)
|
||||
{
|
||||
dht_->getPublicAddress([this, cb = std::move(cb)](std::vector<dht::SockAddr>&& results) {
|
||||
bool hasIpv4 {false}, hasIpv6 {false};
|
||||
for (auto& result : results) {
|
||||
auto family = result.getFamily();
|
||||
if (family == AF_INET) {
|
||||
if (not hasIpv4) {
|
||||
hasIpv4 = true;
|
||||
JAMI_DBG("[Account %s] Store DHT public IPv4 address : %s",
|
||||
getAccountID().c_str(),
|
||||
result.toString().c_str());
|
||||
setPublishedAddress(*result.get());
|
||||
if (upnpCtrl_) {
|
||||
upnpCtrl_->setPublicAddress(*result.get());
|
||||
}
|
||||
}
|
||||
} else if (family == AF_INET6) {
|
||||
if (not hasIpv6) {
|
||||
hasIpv6 = true;
|
||||
JAMI_DBG("[Account %s] Store DHT public IPv6 address : %s",
|
||||
getAccountID().c_str(),
|
||||
result.toString().c_str());
|
||||
setPublishedAddress(*result.get());
|
||||
}
|
||||
}
|
||||
if (hasIpv4 and hasIpv6)
|
||||
break;
|
||||
}
|
||||
if (cb)
|
||||
cb();
|
||||
});
|
||||
return connectionManager_->getIceOptions();
|
||||
}
|
||||
|
||||
bool
|
||||
@ -4255,15 +4181,38 @@ void
|
||||
JamiAccount::initConnectionManager()
|
||||
{
|
||||
if (!connectionManager_) {
|
||||
connectionManager_ = std::make_unique<ConnectionManager>(*this);
|
||||
channelHandlers_[Uri::Scheme::SWARM]
|
||||
= std::make_unique<SwarmChannelHandler>(shared(), *connectionManager_.get());
|
||||
auto connectionManagerConfig
|
||||
= std::make_shared<ConnectionManager::Config>(dht(),
|
||||
identity(),
|
||||
certStore(),
|
||||
upnpCtrl_,
|
||||
config().turnServer,
|
||||
config().turnServerUserName,
|
||||
config().turnServerPwd,
|
||||
config().turnServerRealm,
|
||||
config().turnEnabled);
|
||||
connectionManagerConfig->cachePath = cachePath_;
|
||||
connectionManager_ = std::make_unique<ConnectionManager>(connectionManagerConfig);
|
||||
channelHandlers_[Uri::Scheme::GIT]
|
||||
= std::make_unique<ConversationChannelHandler>(shared(), *connectionManager_.get());
|
||||
channelHandlers_[Uri::Scheme::SYNC]
|
||||
= std::make_unique<SyncChannelHandler>(shared(), *connectionManager_.get());
|
||||
channelHandlers_[Uri::Scheme::DATA_TRANSFER]
|
||||
= std::make_unique<TransferChannelHandler>(shared(), *connectionManager_.get());
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
connectionManager_->oniOSConnected([&](const std::string& connType, dht::InfoHash peer_h) {
|
||||
if ((connType == "videoCall" || connType == "audioCall")
|
||||
&& jami::Manager::instance().isIOSExtension) {
|
||||
bool hasVideo = connType == "videoCall";
|
||||
emitSignal<libjami::ConversationSignal::CallConnectionRequest>("",
|
||||
peer_h.toString(),
|
||||
hasVideo);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,6 +329,11 @@ public:
|
||||
const std::map<std::string, std::string>& msg);
|
||||
void onIsComposing(const std::string& conversationId, const std::string& peer, bool isWriting);
|
||||
|
||||
/**
|
||||
* Create and return ICE options.
|
||||
*/
|
||||
IceTransportOptions getIceOptions() const noexcept;
|
||||
|
||||
/* Devices */
|
||||
void addDevice(const std::string& password);
|
||||
/**
|
||||
@ -423,16 +428,6 @@ public:
|
||||
*/
|
||||
std::map<std::string, std::string> getNearbyPeers() const override;
|
||||
|
||||
/**
|
||||
* Store the local/public addresses used to register
|
||||
*/
|
||||
void storeActiveIpAddress(std::function<void()>&& cb = {});
|
||||
|
||||
/**
|
||||
* Create and return ICE options.
|
||||
*/
|
||||
void getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept;
|
||||
|
||||
#ifdef LIBJAMI_TESTABLE
|
||||
ConnectionManager& connectionManager()
|
||||
{
|
||||
@ -693,8 +688,6 @@ private:
|
||||
const std::shared_ptr<dht::crypto::Certificate>& from_cert,
|
||||
const dht::InfoHash& from);
|
||||
|
||||
static tls::DhParams loadDhParams(std::string path);
|
||||
|
||||
void loadCachedUrl(const std::string& url,
|
||||
const std::string& cachePath,
|
||||
const std::chrono::seconds& cacheDuration,
|
||||
@ -792,7 +785,6 @@ private:
|
||||
*/
|
||||
// TODO move in separate class
|
||||
void testTurn(IpAddr server);
|
||||
void cacheTurnServers();
|
||||
std::unique_ptr<TurnTransport> testTurnV4_;
|
||||
std::unique_ptr<TurnTransport> testTurnV6_;
|
||||
void refreshTurnDelay(bool scheduleNext);
|
||||
|
@ -411,7 +411,6 @@ transaction_request_cb(pjsip_rx_data* rdata)
|
||||
auto call = account->newIncomingCall(std::string(remote_user),
|
||||
MediaAttribute::mediaAttributesToMediaMaps(localMediaList),
|
||||
transport);
|
||||
|
||||
if (!call) {
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
@ -297,9 +297,10 @@ CallTest::testDeclineMultiDevice()
|
||||
|
||||
JAMI_INFO("Start call between alice and Bob");
|
||||
auto bobAccount2 = Manager::instance().getAccount<JamiAccount>(bob2Id);
|
||||
|
||||
auto call = libjami::placeCallWithMedia(aliceId, bobUri, {});
|
||||
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return callReceived == 2 && !callIdBob.empty(); }));
|
||||
CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return callReceived == 2 && !callIdBob.empty(); }));
|
||||
|
||||
JAMI_INFO("Stop call between alice and Bob");
|
||||
callStopped = 0;
|
||||
|
Reference in New Issue
Block a user