Commit e83a1006 authored by Sébastien Blin's avatar Sébastien Blin Committed by Adrien Béraud

sip: negotiate both UDP and TCP for the control channel

NOTE: SIP over TCP is disabled for now on Windows, waiting for
TLS 1.3 support. To re-enable it, check the #ifdef _WIN32 in
ice_transport.cpp

Our pjsip version supports the RFC6544. With this patch, when
starting a call, the daemon is using two ICE sessions for the SIP
channel. One is negotiating a UDP socket, and the other a TCP socket
and transmits both SDP on the DHT.

If both negotiations succeed, TCP is prefered and will be used
to transmit SIP messages and the VCard. This should solve the 30
seconds timeout on bad networks.

Note that the media channel is still using UDP to transmit audio
and video.

MAJOR CHANGE: the SIP channel use TLS on top of TCP, no DTLS,
so the transport is considered as reliable.

Also lot of changes in rfc6544.patch to link to rfc6062. The patch
needs to be cleaned, cf TODO notes

Also this seems to fix the ICE shutdown at the end of the call
(after the IDLE Timeout)

Change-Id: I55c5f51377fd8787bc951d6d282eec46f8eaf977
Gitlab: #103
Gitlab: #108
parent 8091683e
......@@ -35,7 +35,6 @@ bash -c "%PATCH_CMD% %UNIXPATH%pjproject/fix_ioqueue_ipv6_sendto.patch"
bash -c "%PATCH_CMD% %UNIXPATH%pjproject/add_dtls_transport.patch"
bash -c "%PATCH_CMD% %UNIXPATH%pjproject/rfc6544.patch"
bash -c "%PATCH_CMD% %UNIXPATH%pjproject/ice_config.patch"
bash -c "%PATCH_CMD% %UNIXPATH%pjproject/win32_ice_tcp_temp_fix.patch"
%APPLY_CMD% %SRC%\pjproject\win32_vs_gnutls.patch
%APPLY_CMD% %SRC%\pjproject\win_config.patch
......
This diff is collapsed.
From 5f288fe0067f995b91ea87ba4ed19fd65b75ff31 Mon Sep 17 00:00:00 2001
From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
Date: Tue, 11 Jun 2019 16:47:06 -0400
Subject: [PATCH] fix for windows GetAdaptersAddresses
---
pjnath/src/pjnath/ice_strans.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c
index 6172172..33ac521 100644
--- a/pjnath/src/pjnath/ice_strans.c
+++ b/pjnath/src/pjnath/ice_strans.c
@@ -1645,9 +1645,7 @@ pj_ice_strans_sendto2(pj_ice_strans *ice_st, unsigned comp_id, const void *data,
dest_addr_len = dst_addr_len;
}
- pj_stun_sock_info stun_sock_info;
- pj_stun_sock_get_info(comp->stun[tp_idx].sock, &stun_sock_info);
- pj_bool_t add_header = stun_sock_info.conn_type != PJ_STUN_TP_UDP;
+ pj_bool_t add_header = comp->ice_st->cfg.stun_tp->conn_type == PJ_STUN_TP_TCP;
if (add_header) {
//TCP
/*
@@ -1864,9 +1862,7 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice,
if (comp->stun[tp_idx].sock) {
pj_ssize_t sent_size;
- pj_stun_sock_info stun_sock_info;
- pj_stun_sock_get_info(comp->stun[tp_idx].sock, &stun_sock_info);
- pj_bool_t add_header = stun_sock_info.conn_type != PJ_STUN_TP_UDP;
+ pj_bool_t add_header = comp->ice_st->cfg.stun_tp->conn_type == PJ_STUN_TP_TCP;
if (add_header) {
//TCP
/*
--
2.7.4
......@@ -67,12 +67,13 @@ public:
static constexpr uint16_t IPV4_HEADER_SIZE = 20; // Size in bytes of IPv4 packet header
static constexpr uint16_t UDP_HEADER_SIZE = 8; // Size in bytes of UDP header
IceSocketTransport(std::shared_ptr<IceTransport>& ice, int comp_id)
IceSocketTransport(std::shared_ptr<IceTransport>& ice, int comp_id, bool reliable = false)
: compId_ {comp_id}
, ice_ {ice} {}
, ice_ {ice}
, reliable_ {reliable} {}
bool isReliable() const override {
return false; // we consider that a ICE transport is never reliable (UDP support only)
return reliable_;
}
bool isInitiator() const override;
......@@ -94,6 +95,7 @@ public:
private:
const int compId_;
std::shared_ptr<IceTransport> ice_;
bool reliable_;
};
};
This diff is collapsed.
......@@ -29,6 +29,7 @@
#include <functional>
#include <memory>
#include <msgpack.hpp>
#include <vector>
namespace jami {
......@@ -73,6 +74,14 @@ struct IceTransportOptions {
bool aggressive {false}; // If we use the aggressive nomination strategy
};
struct SDP {
std::string ufrag;
std::string pwd;
std::vector<std::string> candidates;
MSGPACK_DEFINE(ufrag, pwd, candidates)
};
class IceTransport {
public:
using Attribute = struct {
......@@ -85,7 +94,6 @@ public:
*/
IceTransport(const char* name, int component_count, bool master,
const IceTransportOptions& options = {});
/**
* Get current state
*/
......@@ -100,7 +108,7 @@ public:
*/
bool start(const Attribute& rem_attrs,
const std::vector<IceCandidate>& rem_candidates);
bool start(const std::vector<uint8_t>& attrs_candidates);
bool start(const SDP& sdp);
/**
* Stop a started or completed transport.
......@@ -125,6 +133,12 @@ public:
*/
bool isRunning() const;
/**
* Return true if a start operations fails or if stop() has been called
* [mutex protected]
*/
bool isStopped() const;
/**
* Returns true if ICE transport is in failure state
* [mutex protected]
......@@ -156,7 +170,7 @@ public:
/**
* Returns serialized ICE attributes and candidates.
*/
std::vector<uint8_t> packIceMsg() const;
std::vector<uint8_t> packIceMsg(uint8_t version = 1) const;
bool getCandidateFromSDP(const std::string& line, IceCandidate& cand);
......@@ -188,6 +202,15 @@ public:
bool setSlaveSession();
bool setInitiatorSession();
/**
* Get SDP messages list
* @param msg The payload to parse
* @return the list of SDP messages
*/
static std::vector<SDP> parseSDPList(const std::vector<uint8_t>& msg);
bool isTCPEnabled();
private:
class Impl;
std::unique_ptr<Impl> pimpl_;
......
This diff is collapsed.
......@@ -615,6 +615,7 @@ class JamiAccount : public SIPAccountBase {
void saveKnownDevices() const;
void replyToIncomingIceMsg(const std::shared_ptr<SIPCall>&,
const std::shared_ptr<IceTransport>&,
const std::shared_ptr<IceTransport>&,
const dht::IceCandidates&,
const std::shared_ptr<dht::crypto::Certificate>& from_cert,
......
......@@ -236,7 +236,7 @@ SipsIceTransport::SipsIceTransport(pjsip_endpoint* endpt,
std::memset(&localCertInfo_, 0, sizeof(pj_ssl_cert_info));
std::memset(&remoteCertInfo_, 0, sizeof(pj_ssl_cert_info));
iceSocket_ = std::make_unique<IceSocketTransport>(ice_, comp_id);
iceSocket_ = std::make_unique<IceSocketTransport>(ice_, comp_id, PJSIP_TRANSPORT_IS_RELIABLE(&trData_.base));
TlsSession::TlsSessionCallbacks cbs = {
/*.onStateChange = */[this](TlsSessionState state){ onTlsStateChange(state); },
......@@ -249,11 +249,22 @@ SipsIceTransport::SipsIceTransport(pjsip_endpoint* endpt,
if (pjsip_transport_register(base.tpmgr, &base) != PJ_SUCCESS)
throw std::runtime_error("Can't register PJSIP transport.");
if (PJSIP_TRANSPORT_IS_RELIABLE(&trData_.base)) {
eventLoop_ = std::thread([this] {
try {
eventLoop();
} catch (const std::exception& e) {
JAMI_ERR() << "SipIceTransport: eventLoop() failure: " << e.what();
}
});
}
}
SipsIceTransport::~SipsIceTransport()
{
JAMI_DBG("~SipIceTransport@%p {tr=%p}", this, &trData_.base);
stopLoop_ = true;
// Flush send queue with ENOTCONN error
for (auto tdata : txQueue_) {
......@@ -266,6 +277,8 @@ SipsIceTransport::~SipsIceTransport()
auto base = getTransportBase();
// Stop low-level transport first
tls_->shutdown();
if (eventLoop_.joinable()) eventLoop_.join();
tls_.reset();
// If delete not trigged by pjsip_transport_destroy (happen if objet not given to pjsip)
......@@ -500,7 +513,10 @@ SipsIceTransport::getInfo(pj_ssl_sock_info* info, bool established)
std::memset(info, 0, sizeof(*info));
info->established = established;
info->proto = PJ_SSL_SOCK_PROTO_DTLS1;
if (PJSIP_TRANSPORT_IS_RELIABLE(&trData_.base))
info->proto = PJSIP_SSL_DEFAULT_PROTO;
else
info->proto = PJ_SSL_SOCK_PROTO_DTLS1;
pj_sockaddr_cp(&info->local_addr, local_.pjPtr());
......@@ -708,4 +724,23 @@ SipsIceTransport::getTlsSessionMtu()
return tls_->maxPayload();
}
void
SipsIceTransport::eventLoop()
{
while(!stopLoop_) {
std::error_code err;
if (tls_ && tls_->waitForData(100, err)) {
std::vector<uint8_t> pkt;
pkt.resize(PJSIP_MAX_PKT_LEN);
auto read = tls_->read(pkt.data(), PJSIP_MAX_PKT_LEN, err);
if (read > 0) {
pkt.resize(read);
std::lock_guard<std::mutex> l(rxMtx_);
rxPending_.emplace_back(std::move(pkt));
scheduler_.run([this]{ handleEvents(); });
}
}
}
}
}} // namespace jami::tls
......@@ -138,6 +138,10 @@ private:
void onRxData(std::vector<uint8_t>&&);
void onCertificatesUpdate(const gnutls_datum_t*, const gnutls_datum_t*, unsigned int);
int verifyCertificate(gnutls_session_t);
std::thread eventLoop_;
void eventLoop();
std::atomic_bool stopLoop_ {false};
};
}} // namespace jami::tls
......@@ -334,7 +334,7 @@ IceSocketEndpoint::waitForData(unsigned ms_timeout, std::error_code& ec) const
{
if (ice_) {
if (!ice_->isRunning()) return -1;
return iceIsSender ? ice_->isDataAvailable(1) : ice_->waitForData(1, ms_timeout, ec);
return iceIsSender ? ice_->isDataAvailable(compId_) : ice_->waitForData(compId_, ms_timeout, ec);
}
return -1;
}
......@@ -345,7 +345,7 @@ IceSocketEndpoint::read(ValueType* buf, std::size_t len, std::error_code& ec)
if (ice_) {
if (!ice_->isRunning()) return 0;
try {
auto res = ice_->recvfrom(1, reinterpret_cast<char *>(buf), len);
auto res = ice_->recvfrom(compId_, reinterpret_cast<char *>(buf), len);
if (res < 0)
ec.assign(errno, std::generic_category());
else
......@@ -365,7 +365,7 @@ IceSocketEndpoint::write(const ValueType* buf, std::size_t len, std::error_code&
if (ice_) {
if (!ice_->isRunning()) return 0;
auto res = 0;
res = ice_->send(0, reinterpret_cast<const unsigned char *>(buf), len);
res = ice_->send(compId_, reinterpret_cast<const unsigned char *>(buf), len);
if (res < 0) {
ec.assign(errno, std::generic_category());
} else {
......
......@@ -157,7 +157,7 @@ public:
void setOnRecv(RecvCb&& cb) override {
if (ice_) {
ice_->setOnRecv(0, cb);
ice_->setOnRecv(compId_, cb);
}
}
......@@ -165,6 +165,7 @@ private:
std::shared_ptr<IceTransport> ice_ {nullptr};
std::atomic_bool iceStopped{false};
std::atomic_bool iceIsSender{false};
uint8_t compId_ {0};
};
//==============================================================================
......
......@@ -234,6 +234,7 @@ public:
std::unique_ptr<TlsAnonymousClientCredendials> cacred_; // ctor init.
std::unique_ptr<TlsAnonymousServerCredendials> sacred_; // ctor init.
std::unique_ptr<TlsCertificateCredendials> xcred_; // ctor init.
std::mutex sessionMutex_;
gnutls_session_t session_ {nullptr};
gnutls_datum_t cookie_key_ {nullptr, 0};
gnutls_dtls_prestate_st prestate_ {};
......@@ -724,13 +725,16 @@ TlsSession::TlsSessionImpl::cleanup()
state_ = TlsSessionState::SHUTDOWN; // be sure to block any user operations
stateCondition_.notify_all();
if (session_) {
if (transport_.isReliable())
gnutls_bye(session_, GNUTLS_SHUT_RDWR);
else
gnutls_bye(session_, GNUTLS_SHUT_WR); // not wait for a peer answer
gnutls_deinit(session_);
session_ = nullptr;
{
std::lock_guard<std::mutex> lk(sessionMutex_);
if (session_) {
if (transport_.isReliable())
gnutls_bye(session_, GNUTLS_SHUT_RDWR);
else
gnutls_bye(session_, GNUTLS_SHUT_WR); // not wait for a peer answer
gnutls_deinit(session_);
session_ = nullptr;
}
}
if (cookie_key_.data)
......@@ -1218,7 +1222,7 @@ TlsSession::TlsSession(SocketType& transport, const TlsParams& params,
TlsSession::~TlsSession()
{
shutdown();
if (pimpl_) shutdown();
}
bool
......@@ -1237,8 +1241,8 @@ int
TlsSession::maxPayload() const
{
if (pimpl_->state_ == TlsSessionState::SHUTDOWN)
throw std::runtime_error("Getting MTU from non-valid TLS session");
return gnutls_dtls_get_data_mtu(pimpl_->session_);
throw std::runtime_error("Getting maxPayload from non-valid TLS session");
return pimpl_->transport_.maxPayload();
}
const char*
......@@ -1295,15 +1299,22 @@ TlsSession::read(ValueType* data, std::size_t size, std::error_code& ec)
}
while (true) {
auto ret = gnutls_record_recv(pimpl_->session_, data, size);
ssize_t ret;
{
std::lock_guard<std::mutex> lk(pimpl_->sessionMutex_);
if (!pimpl_->session_) return 0;
ret = gnutls_record_recv(pimpl_->session_, data, size);
}
if (ret > 0) {
ec.clear();
return ret;
}
if (ret == 0) {
JAMI_DBG("[TLS] eof");
shutdown();
if (pimpl_) {
JAMI_ERR("[TLS] eof");
shutdown();
}
error = std::errc::broken_pipe;
break;
} else if (ret == GNUTLS_E_REHANDSHAKE) {
......@@ -1312,8 +1323,10 @@ TlsSession::read(ValueType* data, std::size_t size, std::error_code& ec)
pimpl_->rxCv_.notify_one(); // unblock waiting FSM
pimpl_->stateCondition_.notify_all();
} else if (gnutls_error_is_fatal(ret)) {
JAMI_ERR("[TLS] fatal error in recv: %s", gnutls_strerror(ret));
shutdown();
if (pimpl_ && pimpl_->state_ != TlsSessionState::SHUTDOWN) {
JAMI_ERR("[TLS] fatal error in recv: %s", gnutls_strerror(ret));
shutdown();
}
error = std::errc::io_error;
break;
}
......
......@@ -440,6 +440,9 @@ SipTransportBroker::getTlsIceTransport(const std::shared_ptr<jami::IceTransport>
{
auto ipv6 = ice->getLocalAddress(comp_id).isIpv6();
auto type = ipv6 ? PJSIP_TRANSPORT_DTLS6 : PJSIP_TRANSPORT_DTLS;
if (ice->isTCPEnabled()) {
type = ipv6 ? PJSIP_TRANSPORT_TLS6 : PJSIP_TRANSPORT_TLS;
}
auto sip_ice_tr = std::unique_ptr<tls::SipsIceTransport>(
new tls::SipsIceTransport(endpt_, type, params, ice, comp_id));
auto tr = sip_ice_tr->getTransportBase();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment