mirror of
https://git.jami.net/savoirfairelinux/jami-daemon.git
synced 2025-08-12 22:09:25 +08:00

A DTLS type is needed for our DHT account. This type of transport is based on UDP + TLS. This kind of transport behaviour don't exist on current PJSIP v2.5.5 release. This patch adds this transport type, both for IPv4 and IPv6. Our PJSIP bridge class is modifier to support this new type for DHT account. Change-Id: I247fcb7c89fe23c99c580705c5f4a1d172b9c917 Reviewed-by: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
462 lines
16 KiB
C++
462 lines
16 KiB
C++
/*
|
|
* Copyright (C) 2004-2016 Savoir-faire Linux Inc.
|
|
*
|
|
* Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
|
|
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "siptransport.h"
|
|
#include "sip_utils.h"
|
|
#include "ip_utils.h"
|
|
#include "ice_transport.h"
|
|
|
|
#include "ringdht/sip_transport_ice.h"
|
|
#include "ringdht/sips_transport_ice.h"
|
|
|
|
#include "array_size.h"
|
|
#include "compiler_intrinsics.h"
|
|
#include "sipvoiplink.h"
|
|
|
|
#include <pjsip.h>
|
|
#include <pjsip/sip_types.h>
|
|
#include <pjsip/sip_transport_tls.h>
|
|
#include <pj/ssl_sock.h>
|
|
#include <pjnath.h>
|
|
#include <pjnath/stun_config.h>
|
|
#include <pjlib.h>
|
|
#include <pjlib-util.h>
|
|
|
|
#include <stdexcept>
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
|
|
#define RETURN_IF_FAIL(A, VAL, M, ...) if (!(A)) { RING_ERR(M, ##__VA_ARGS__); return (VAL); }
|
|
|
|
namespace ring {
|
|
|
|
constexpr const char* TRANSPORT_STATE_STR[] = {
|
|
"CONNECTED", "DISCONNECTED", "SHUTDOWN", "DESTROY", "UNKNOWN STATE"
|
|
};
|
|
constexpr const size_t TRANSPORT_STATE_SZ = arraySize(TRANSPORT_STATE_STR);
|
|
|
|
std::string
|
|
SipTransportDescr::toString() const
|
|
{
|
|
std::stringstream ss;
|
|
ss << "{" << pjsip_transport_get_type_desc(type) << " on " << interface << ":" << listenerPort << "}";
|
|
return ss.str();
|
|
}
|
|
|
|
void
|
|
SipTransport::deleteTransport(pjsip_transport* t)
|
|
{
|
|
pjsip_transport_shutdown(t);
|
|
pjsip_transport_dec_ref(t);
|
|
}
|
|
|
|
SipTransport::SipTransport(pjsip_transport* t)
|
|
: transport_(nullptr, deleteTransport)
|
|
{
|
|
if (not t or pjsip_transport_add_ref(t) != PJ_SUCCESS)
|
|
throw std::runtime_error("invalid transport");
|
|
|
|
// Set pointer here, right after the successful pjsip_transport_add_ref
|
|
transport_.reset(t);
|
|
|
|
RING_DBG("SipTransport@%p {tr=%p {rc=%ld}}",
|
|
this, transport_.get(), pj_atomic_get(transport_->ref_cnt));
|
|
}
|
|
|
|
SipTransport::SipTransport(pjsip_transport* t,
|
|
const std::shared_ptr<TlsListener>& l)
|
|
: SipTransport(t)
|
|
{
|
|
tlsListener_ = l;
|
|
}
|
|
|
|
SipTransport::~SipTransport()
|
|
{
|
|
RING_DBG("~SipTransport@%p {tr=%p {rc=%ld}}",
|
|
this, transport_.get(), pj_atomic_get(transport_->ref_cnt));
|
|
}
|
|
|
|
bool
|
|
SipTransport::isAlive(UNUSED const std::shared_ptr<SipTransport>& t,
|
|
pjsip_transport_state state)
|
|
{
|
|
return state != PJSIP_TP_STATE_DISCONNECTED
|
|
#if PJ_VERSION_NUM > (2 << 24 | 1 << 16)
|
|
&& state != PJSIP_TP_STATE_SHUTDOWN
|
|
&& state != PJSIP_TP_STATE_DESTROY
|
|
#else
|
|
&& t && t->get()
|
|
&& !t->get()->is_shutdown
|
|
&& !t->get()->is_destroying
|
|
#endif
|
|
;
|
|
}
|
|
|
|
const char*
|
|
SipTransport::stateToStr(pjsip_transport_state state)
|
|
{
|
|
return TRANSPORT_STATE_STR[std::min<size_t>(state, TRANSPORT_STATE_SZ-1)];
|
|
}
|
|
|
|
void
|
|
SipTransport::stateCallback(pjsip_transport_state state,
|
|
const pjsip_transport_state_info *info)
|
|
{
|
|
connected_ = state == PJSIP_TP_STATE_CONNECTED;
|
|
|
|
auto extInfo = static_cast<const pjsip_tls_state_info*>(info->ext_info);
|
|
if (isSecure() && extInfo && extInfo->ssl_sock_info && extInfo->ssl_sock_info->established) {
|
|
auto tlsInfo = extInfo->ssl_sock_info;
|
|
tlsInfos_.proto = (pj_ssl_sock_proto)tlsInfo->proto;
|
|
tlsInfos_.cipher = tlsInfo->cipher;
|
|
tlsInfos_.verifyStatus = (pj_ssl_cert_verify_flag_t)tlsInfo->verify_status;
|
|
const auto& peers = tlsInfo->remote_cert_info->raw_chain;
|
|
std::vector<std::pair<const uint8_t*, const uint8_t*>> bits;
|
|
bits.resize(peers.cnt);
|
|
std::transform(peers.cert_raw, peers.cert_raw+peers.cnt, std::begin(bits),
|
|
[](const pj_str_t& crt){
|
|
return std::make_pair((uint8_t*)crt.ptr,
|
|
(uint8_t*)(crt.ptr+crt.slen));
|
|
});
|
|
tlsInfos_.peerCert = std::make_shared<dht::crypto::Certificate>(bits);
|
|
} else {
|
|
tlsInfos_ = {};
|
|
}
|
|
|
|
std::vector<SipTransportStateCallback> cbs;
|
|
{
|
|
std::lock_guard<std::mutex> lock(stateListenersMutex_);
|
|
cbs.reserve(stateListeners_.size());
|
|
for (auto& l : stateListeners_)
|
|
cbs.push_back(l.second);
|
|
}
|
|
for (auto& cb : cbs)
|
|
cb(state, info);
|
|
}
|
|
|
|
void
|
|
SipTransport::addStateListener(uintptr_t lid, SipTransportStateCallback cb)
|
|
{
|
|
std::lock_guard<std::mutex> lock(stateListenersMutex_);
|
|
auto pair = stateListeners_.insert(std::make_pair(lid, cb));
|
|
if (not pair.second)
|
|
pair.first->second = cb;
|
|
}
|
|
|
|
bool
|
|
SipTransport::removeStateListener(uintptr_t lid)
|
|
{
|
|
std::lock_guard<std::mutex> lock(stateListenersMutex_);
|
|
auto it = stateListeners_.find(lid);
|
|
if (it != stateListeners_.end()) {
|
|
stateListeners_.erase(it);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
SipTransportBroker::SipTransportBroker(pjsip_endpoint *endpt,
|
|
pj_caching_pool& cp, pj_pool_t& pool) :
|
|
cp_(cp), pool_(pool), endpt_(endpt)
|
|
{
|
|
/*
|
|
pjsip_transport_register_type(PJSIP_TRANSPORT_DATAGRAM, "ICE",
|
|
pjsip_transport_get_default_port_for_type(PJSIP_TRANSPORT_UDP),
|
|
&ice_pj_transport_type_);
|
|
*/
|
|
RING_DBG("SipTransportBroker@%p", this);
|
|
}
|
|
|
|
SipTransportBroker::~SipTransportBroker()
|
|
{
|
|
RING_DBG("~SipTransportBroker@%p", this);
|
|
|
|
shutdown();
|
|
|
|
udpTransports_.clear();
|
|
transports_.clear();
|
|
|
|
RING_DBG("destroying SipTransportBroker@%p", this);
|
|
}
|
|
|
|
void
|
|
SipTransportBroker::transportStateChanged(pjsip_transport* tp,
|
|
pjsip_transport_state state,
|
|
const pjsip_transport_state_info* info)
|
|
{
|
|
RING_DBG("pjsip transport@%p %s -> %s",
|
|
tp, tp->info, SipTransport::stateToStr(state));
|
|
|
|
// First make sure that this transport is handled by us
|
|
// and remove it from any mapping if destroy pending or done.
|
|
|
|
std::shared_ptr<SipTransport> sipTransport;
|
|
|
|
{
|
|
std::lock_guard<std::mutex> lock(transportMapMutex_);
|
|
auto key = transports_.find(tp);
|
|
if (key == transports_.end()) {
|
|
RING_WARN("spurious pjsip transport state change");
|
|
return;
|
|
}
|
|
|
|
sipTransport = key->second.lock();
|
|
|
|
#if PJ_VERSION_NUM > (2 << 24 | 1 << 16)
|
|
bool destroyed = state == PJSIP_TP_STATE_DESTROY;
|
|
#else
|
|
bool destroyed = tp->is_destroying;
|
|
#endif
|
|
|
|
// maps cleanup
|
|
if (destroyed) {
|
|
RING_DBG("unmap pjsip transport@%p {SipTransport@%p}",
|
|
tp, sipTransport.get());
|
|
transports_.erase(key);
|
|
|
|
// If UDP
|
|
const auto type = tp->key.type;
|
|
if (type == PJSIP_TRANSPORT_UDP or type == PJSIP_TRANSPORT_UDP6) {
|
|
const auto updKey = std::find_if(
|
|
udpTransports_.cbegin(), udpTransports_.cend(),
|
|
[tp](const std::pair<SipTransportDescr, pjsip_transport*>& pair) {
|
|
return pair.second == tp;
|
|
});
|
|
if (updKey != udpTransports_.cend())
|
|
udpTransports_.erase(updKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Propagate the event to the appropriate transport
|
|
// Note the SipTransport may not be in our mappings if marked as dead
|
|
if (sipTransport)
|
|
sipTransport->stateCallback(state, info);
|
|
}
|
|
|
|
std::shared_ptr<SipTransport>
|
|
SipTransportBroker::addTransport(pjsip_transport* t)
|
|
{
|
|
if (t) {
|
|
std::lock_guard<std::mutex> lock(transportMapMutex_);
|
|
|
|
auto key = transports_.find(t);
|
|
if (key != transports_.end()) {
|
|
if (auto sipTr = key->second.lock())
|
|
return sipTr;
|
|
}
|
|
|
|
auto sipTr = std::make_shared<SipTransport>(t);
|
|
if (key != transports_.end())
|
|
key->second = sipTr;
|
|
else
|
|
transports_.emplace(std::make_pair(t, sipTr));
|
|
return sipTr;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void
|
|
SipTransportBroker::shutdown()
|
|
{
|
|
std::unique_lock<std::mutex> lock(transportMapMutex_);
|
|
for (auto& t : transports_) {
|
|
if (auto transport = t.second.lock()) {
|
|
pjsip_transport_shutdown(transport->get());
|
|
}
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<SipTransport>
|
|
SipTransportBroker::getUdpTransport(const SipTransportDescr& descr)
|
|
{
|
|
std::lock_guard<std::mutex> lock(transportMapMutex_);
|
|
auto itp = udpTransports_.find(descr);
|
|
if (itp != udpTransports_.end()) {
|
|
auto it = transports_.find(itp->second);
|
|
if (it != transports_.end()) {
|
|
if (auto spt = it->second.lock()) {
|
|
RING_DBG("Reusing transport %s", descr.toString().c_str());
|
|
return spt;
|
|
}
|
|
else {
|
|
// Transport still exists but have not been destroyed yet.
|
|
RING_WARN("Recycling transport %s", descr.toString().c_str());
|
|
auto ret = std::make_shared<SipTransport>(itp->second);
|
|
it->second = ret;
|
|
return ret;
|
|
}
|
|
} else {
|
|
RING_WARN("Cleaning up UDP transport %s", descr.toString().c_str());
|
|
udpTransports_.erase(itp);
|
|
}
|
|
}
|
|
auto ret = createUdpTransport(descr);
|
|
if (ret) {
|
|
udpTransports_[descr] = ret->get();
|
|
transports_[ret->get()] = ret;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::shared_ptr<SipTransport>
|
|
SipTransportBroker::createUdpTransport(const SipTransportDescr& d)
|
|
{
|
|
RETURN_IF_FAIL(d.listenerPort != 0, nullptr, "Could not determine port for this transport");
|
|
auto family = pjsip_transport_type_get_af(d.type);
|
|
|
|
IpAddr listeningAddress = (d.interface == ip_utils::DEFAULT_INTERFACE) ?
|
|
ip_utils::getAnyHostAddr(family) :
|
|
ip_utils::getInterfaceAddr(d.interface, family);
|
|
listeningAddress.setPort(d.listenerPort);
|
|
RETURN_IF_FAIL(listeningAddress, nullptr, "Could not determine IP address for this transport");
|
|
|
|
pjsip_udp_transport_cfg pj_cfg;
|
|
pjsip_udp_transport_cfg_default(&pj_cfg, family);
|
|
pj_cfg.bind_addr = listeningAddress;
|
|
pjsip_transport *transport = nullptr;
|
|
if (pj_status_t status = pjsip_udp_transport_start2(endpt_, &pj_cfg, &transport)) {
|
|
RING_ERR("pjsip_udp_transport_start2 failed with error %d: %s", status,
|
|
sip_utils::sip_strerror(status).c_str());
|
|
RING_ERR("UDP IPv%s Transport did not start on %s",
|
|
listeningAddress.isIpv4() ? "4" : "6",
|
|
listeningAddress.toString(true).c_str());
|
|
return nullptr;
|
|
}
|
|
|
|
RING_DBG("Created UDP transport on %s : %s", d.interface.c_str(), listeningAddress.toString(true).c_str());
|
|
return std::make_shared<SipTransport>(transport);
|
|
}
|
|
|
|
std::shared_ptr<TlsListener>
|
|
SipTransportBroker::getTlsListener(const SipTransportDescr& d, const pjsip_tls_setting* settings)
|
|
{
|
|
RETURN_IF_FAIL(settings, nullptr, "TLS settings not specified");
|
|
auto family = pjsip_transport_type_get_af(d.type);
|
|
|
|
IpAddr listeningAddress = (d.interface == ip_utils::DEFAULT_INTERFACE) ?
|
|
ip_utils::getAnyHostAddr(family) :
|
|
ip_utils::getInterfaceAddr(d.interface, family);
|
|
listeningAddress.setPort(d.listenerPort);
|
|
|
|
RETURN_IF_FAIL(listeningAddress, nullptr, "Could not determine IP address for this transport");
|
|
RING_DBG("Creating TLS listener %s on %s...", d.toString().c_str(), listeningAddress.toString(true).c_str());
|
|
#if 0
|
|
RING_DBG(" ca_list_file : %s", settings->ca_list_file.ptr);
|
|
RING_DBG(" cert_file : %s", settings->cert_file.ptr);
|
|
RING_DBG(" ciphers_num : %d", settings->ciphers_num);
|
|
RING_DBG(" verify server %d client %d client_cert %d", settings->verify_server, settings->verify_client, settings->require_client_cert);
|
|
RING_DBG(" reuse_addr : %d", settings->reuse_addr);
|
|
#endif
|
|
|
|
pjsip_tpfactory *listener = nullptr;
|
|
const pj_status_t status = pjsip_tls_transport_start2(endpt_, settings, listeningAddress.pjPtr(), nullptr, 1, &listener);
|
|
if (status != PJ_SUCCESS) {
|
|
RING_ERR("TLS listener did not start: %s", sip_utils::sip_strerror(status).c_str());
|
|
return nullptr;
|
|
}
|
|
return std::make_shared<TlsListener>(listener);
|
|
}
|
|
|
|
std::shared_ptr<SipTransport>
|
|
SipTransportBroker::getTlsTransport(const std::shared_ptr<TlsListener>& l, const IpAddr& remote, const std::string& remote_name)
|
|
{
|
|
if (!l || !remote)
|
|
return nullptr;
|
|
IpAddr remoteAddr {remote};
|
|
if (remoteAddr.getPort() == 0)
|
|
remoteAddr.setPort(pjsip_transport_get_default_port_for_type(l->get()->type));
|
|
|
|
RING_DBG("Get new TLS transport to %s", remoteAddr.toString(true).c_str());
|
|
pjsip_tpselector sel {PJSIP_TPSELECTOR_LISTENER, {
|
|
.listener = l->get()
|
|
}};
|
|
|
|
pjsip_tx_data tx_data;
|
|
tx_data.dest_info.name = pj_str_t{(char*)remote_name.data(), (pj_ssize_t)remote_name.size()};
|
|
|
|
pjsip_transport *transport = nullptr;
|
|
pj_status_t status = pjsip_endpt_acquire_transport2(
|
|
endpt_,
|
|
l->get()->type,
|
|
remoteAddr.pjPtr(),
|
|
remoteAddr.getLength(),
|
|
&sel,
|
|
remote_name.empty() ? nullptr : &tx_data,
|
|
&transport);
|
|
|
|
if (!transport || status != PJ_SUCCESS) {
|
|
RING_ERR("Could not get new TLS transport: %s", sip_utils::sip_strerror(status).c_str());
|
|
return nullptr;
|
|
}
|
|
auto ret = std::make_shared<SipTransport>(transport, l);
|
|
pjsip_transport_dec_ref(transport);
|
|
{
|
|
std::lock_guard<std::mutex> lock(transportMapMutex_);
|
|
transports_[ret->get()] = ret;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::shared_ptr<SipTransport>
|
|
SipTransportBroker::getIceTransport(const std::shared_ptr<IceTransport> ice,
|
|
unsigned comp_id)
|
|
{
|
|
auto sip_ice_tr = std::unique_ptr<SipIceTransport>(
|
|
new SipIceTransport(endpt_, pool_, ice_pj_transport_type_, ice, comp_id));
|
|
auto tr = sip_ice_tr->getTransportBase();
|
|
auto sip_tr = std::make_shared<SipTransport>(tr);
|
|
sip_ice_tr.release(); // managed by PJSIP now
|
|
|
|
{
|
|
std::lock_guard<std::mutex> lock(transportMapMutex_);
|
|
// we do not check for key existance as we've just created it
|
|
// (member of new SipIceTransport instance)
|
|
transports_.emplace(std::make_pair(tr, sip_tr));
|
|
}
|
|
return sip_tr;
|
|
}
|
|
|
|
std::shared_ptr<SipTransport>
|
|
SipTransportBroker::getTlsIceTransport(const std::shared_ptr<ring::IceTransport> ice,
|
|
unsigned comp_id,
|
|
const tls::TlsParams& params)
|
|
{
|
|
auto ipv6 = ice->getLocalAddress(comp_id).isIpv6();
|
|
auto type = ipv6 ? PJSIP_TRANSPORT_DTLS6 : PJSIP_TRANSPORT_DTLS;
|
|
auto sip_ice_tr = std::unique_ptr<tls::SipsIceTransport>(
|
|
new tls::SipsIceTransport(endpt_, type, params, ice, comp_id));
|
|
auto tr = sip_ice_tr->getTransportBase();
|
|
auto sip_tr = std::make_shared<SipTransport>(tr);
|
|
sip_ice_tr.release(); // managed by PJSIP now
|
|
|
|
{
|
|
std::lock_guard<std::mutex> lock(transportMapMutex_);
|
|
// we do not check for key existance as we've just created it
|
|
// (member of new SipIceTransport instance)
|
|
transports_.emplace(std::make_pair(tr, sip_tr));
|
|
}
|
|
return sip_tr;
|
|
}
|
|
|
|
} // namespace ring
|