Files
jami-daemon/src/sip/siptransport.cpp
Guillaume Roguez 88be4bd9c7 contrib: add dtls transport type to pjsip
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>
2016-12-06 17:57:44 -05:00

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