Ring Daemon
Loading...
Searching...
No Matches
siptransport.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2026 Savoir-faire Linux Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "sip/siptransport.h"
20
22
23#include "sip/sipvoiplink.h"
24
25#include <pjsip.h>
26#include <pjsip/sip_types.h>
27#include <pjsip/sip_transport_tls.h>
28#include <pj/ssl_sock.h>
29#include <pjnath.h>
30#include <pjnath/stun_config.h>
31#include <pjlib.h>
32#include <pjlib-util.h>
33
34#include <dhtnet/multiplexed_socket.h>
35#include <dhtnet/ip_utils.h>
36#include <dhtnet/tls_session.h>
37
38#include <opendht/crypto.h>
39
40#include <stdexcept>
41#include <algorithm>
42
43#define RETURN_IF_FAIL(A, VAL, ...) \
44 if (!(A)) { \
45 JAMI_ERROR(__VA_ARGS__); \
46 return (VAL); \
47 }
48
49namespace jami {
50
51constexpr const char* TRANSPORT_STATE_STR[] = {"CONNECTED", "DISCONNECTED", "SHUTDOWN", "DESTROY", "UNKNOWN STATE"};
52constexpr const size_t TRANSPORT_STATE_SZ = std::size(TRANSPORT_STATE_STR);
53
54void
55SipTransport::deleteTransport(pjsip_transport* t)
56{
58}
59
61 : transport_(nullptr)
62{
64 throw std::runtime_error("Invalid transport");
65
66 // Set pointer here, right after the successful pjsip_transport_add_ref
67 transport_.reset(t);
68
69 JAMI_DEBUG("SipTransport@{} tr={} rc={:d}",
70 fmt::ptr(this),
71 fmt::ptr(transport_.get()),
72 pj_atomic_get(transport_->ref_cnt));
73}
74
75SipTransport::SipTransport(pjsip_transport* t, const std::shared_ptr<TlsListener>& l)
76 : SipTransport(t)
77{
78 tlsListener_ = l;
79}
80
81SipTransport::SipTransport(pjsip_transport* t, const std::shared_ptr<dht::crypto::Certificate>& peerCertficate)
82 : SipTransport(t)
83{
84 tlsInfos_.peerCert = peerCertficate;
85}
86
88{
89 JAMI_DEBUG("~SipTransport@{} tr={} rc={:d}",
90 fmt::ptr(this),
91 fmt::ptr(transport_.get()),
92 pj_atomic_get(transport_->ref_cnt));
93}
94
95bool
100
101const char*
103{
104 return TRANSPORT_STATE_STR[std::min<size_t>(state, TRANSPORT_STATE_SZ - 1)];
105}
106
107void
109{
110 connected_ = state == PJSIP_TP_STATE_CONNECTED;
111
112 const auto* extInfo = static_cast<const pjsip_tls_state_info*>(info->ext_info);
113 if (isSecure() && extInfo && extInfo->ssl_sock_info && extInfo->ssl_sock_info->established) {
114 auto* tlsInfo = extInfo->ssl_sock_info;
115 tlsInfos_.proto = (pj_ssl_sock_proto) tlsInfo->proto;
116 tlsInfos_.cipher = tlsInfo->cipher;
117 tlsInfos_.verifyStatus = (pj_ssl_cert_verify_flag_t) tlsInfo->verify_status;
118 if (!tlsInfos_.peerCert) {
119 const auto& peers = tlsInfo->remote_cert_info->raw_chain;
120 std::vector<std::pair<const uint8_t*, const uint8_t*>> bits;
121 bits.resize(peers.cnt);
122 std::transform(peers.cert_raw, peers.cert_raw + peers.cnt, std::begin(bits), [](const pj_str_t& crt) {
123 return std::make_pair((uint8_t*) crt.ptr, (uint8_t*) (crt.ptr + crt.slen));
124 });
125 tlsInfos_.peerCert = std::make_shared<dht::crypto::Certificate>(bits);
126 }
127 } else {
128 tlsInfos_ = {};
129 }
130
131 std::vector<SipTransportStateCallback> cbs;
132 {
133 std::lock_guard lock(stateListenersMutex_);
134 cbs.reserve(stateListeners_.size());
135 for (auto& l : stateListeners_)
136 cbs.push_back(l.second);
137 }
138 for (auto& cb : cbs)
139 cb(state, info);
140}
141
142void
144{
145 std::lock_guard lock(stateListenersMutex_);
146 auto pair = stateListeners_.insert(std::make_pair(lid, cb));
147 if (not pair.second)
148 pair.first->second = cb;
149}
150
151bool
153{
154 std::lock_guard lock(stateListenersMutex_);
155 auto it = stateListeners_.find(lid);
156 if (it != stateListeners_.end()) {
157 stateListeners_.erase(it);
158 return true;
159 }
160 return false;
161}
162
165{
166 return 1232; /* Hardcoded yes (it's the IPv6 value).
167 * This method is broken by definition.
168 * A MTU should not be defined at this layer.
169 * And a correct value should come from the underlying transport itself,
170 * not from a constant…
171 */
172}
173
177
179{
180 shutdown();
181
182 udpTransports_.clear();
183 transports_.clear();
184
185 JAMI_DEBUG("Destroying SipTransportBroker@{}…", fmt::ptr(this));
186}
187
188void
191 const pjsip_transport_state_info* info)
192{
193 JAMI_DEBUG("PJSIP transport@{} {} → {}", fmt::ptr(tp), tp->info, SipTransport::stateToStr(state));
194
195 // First ensure that this transport is handled by us
196 // and remove it from any mapping if destroy pending or done.
197
198 std::shared_ptr<SipTransport> sipTransport;
199 std::lock_guard lock(transportMapMutex_);
200 auto key = transports_.find(tp);
201 if (key == transports_.end())
202 return;
203
204 sipTransport = key->second.lock();
205
206 if (!isDestroying_ && state == PJSIP_TP_STATE_DESTROY) {
207 // maps cleanup
208 JAMI_DEBUG("Unmap PJSIP transport@{} {{SipTransport@{}}}", fmt::ptr(tp), fmt::ptr(sipTransport.get()));
209 transports_.erase(key);
210
211 // If UDP
212 const auto type = tp->key.type;
213 if (type == PJSIP_TRANSPORT_UDP or type == PJSIP_TRANSPORT_UDP6) {
214 const auto updKey = std::find_if(udpTransports_.cbegin(),
215 udpTransports_.cend(),
216 [tp](const std::pair<dhtnet::IpAddr, pjsip_transport*>& pair) {
217 return pair.second == tp;
218 });
219 if (updKey != udpTransports_.cend())
220 udpTransports_.erase(updKey);
221 }
222 }
223
224 // Propagate the event to the appropriate transport
225 // Note the SipTransport may not be in our mappings if marked as dead
226 if (sipTransport)
227 sipTransport->stateCallback(state, info);
228}
229
230std::shared_ptr<SipTransport>
232{
233 if (t) {
234 std::lock_guard lock(transportMapMutex_);
235
236 auto key = transports_.find(t);
237 if (key != transports_.end()) {
238 if (auto sipTr = key->second.lock())
239 return sipTr;
240 }
241
242 auto sipTr = std::make_shared<SipTransport>(t);
243 if (key != transports_.end())
244 key->second = sipTr;
245 else
246 transports_.emplace(std::make_pair(t, sipTr));
247 return sipTr;
248 }
249
250 return nullptr;
251}
252
253void
255{
256 std::unique_lock lock(transportMapMutex_);
257 isDestroying_ = true;
258 for (auto& t : transports_) {
259 if (auto transport = t.second.lock()) {
260 pjsip_transport_shutdown(transport->get());
261 }
262 }
263}
264
265std::shared_ptr<SipTransport>
267{
268 std::lock_guard lock(transportMapMutex_);
269 auto itp = udpTransports_.find(ipAddress);
270 if (itp != udpTransports_.end()) {
271 auto it = transports_.find(itp->second);
272 if (it != transports_.end()) {
273 if (auto spt = it->second.lock()) {
274 JAMI_DEBUG("Reusing transport {}", ipAddress.toString(true));
275 return spt;
276 } else {
277 // Transport still exists but have not been destroyed yet.
278 JAMI_WARNING("Recycling transport {}", ipAddress.toString(true));
279 auto ret = std::make_shared<SipTransport>(itp->second);
280 it->second = ret;
281 return ret;
282 }
283 } else {
284 JAMI_WARNING("Cleaning up UDP transport {}", ipAddress.toString(true));
285 udpTransports_.erase(itp);
286 }
287 }
288 auto ret = createUdpTransport(ipAddress);
289 if (ret) {
290 udpTransports_[ipAddress] = ret->get();
291 transports_[ret->get()] = ret;
292 }
293 return ret;
294}
295
296std::shared_ptr<SipTransport>
297SipTransportBroker::createUdpTransport(const dhtnet::IpAddr& ipAddress)
298{
299 RETURN_IF_FAIL(ipAddress, nullptr, "Unable to determine IP address for this transport");
300
303 pj_cfg.bind_addr = ipAddress;
304 pjsip_transport* transport = nullptr;
305 if (pj_status_t status = pjsip_udp_transport_start2(endpt_, &pj_cfg, &transport)) {
306 JAMI_ERROR("pjsip_udp_transport_start2 failed with error {:d}: {:s}", status, sip_utils::sip_strerror(status));
307 JAMI_ERROR("UDP IPv{} Transport did not start on {}", ipAddress.isIpv4() ? "4" : "6", ipAddress.toString(true));
308 return nullptr;
309 }
310
311 JAMI_DEBUG("Created UDP transport on address {}", ipAddress.toString(true));
312 return std::make_shared<SipTransport>(transport);
313}
314
315std::shared_ptr<TlsListener>
317{
318 RETURN_IF_FAIL(settings, nullptr, "TLS settings not specified");
319 RETURN_IF_FAIL(ipAddress, nullptr, "Unable to determine IP address for this transport");
320 JAMI_DEBUG("Creating TLS listener on {:s}…", ipAddress.toString(true));
321
322 pjsip_tpfactory* listener = nullptr;
323 const pj_status_t status = pjsip_tls_transport_start2(endpt_, settings, ipAddress.pjPtr(), nullptr, 1, &listener);
324 if (status != PJ_SUCCESS) {
325 JAMI_ERROR("TLS listener did not start: {}", sip_utils::sip_strerror(status));
326 return nullptr;
327 }
328 return std::make_shared<TlsListener>(listener);
329}
330
331std::shared_ptr<SipTransport>
332SipTransportBroker::getTlsTransport(const std::shared_ptr<TlsListener>& l,
333 const dhtnet::IpAddr& remote,
334 const std::string& remote_name)
335{
336 if (!l || !remote)
337 return nullptr;
338 dhtnet::IpAddr remoteAddr {remote};
339 if (remoteAddr.getPort() == 0)
341
342 JAMI_DEBUG("Get new TLS transport to {}", remoteAddr.toString(true));
345 sel.u.listener = l->get();
346 sel.disable_connection_reuse = PJ_FALSE;
347
349 tx_data.dest_info.name = pj_str_t {(char*) remote_name.data(), (pj_ssize_t) remote_name.size()};
350
351 pjsip_transport* transport = nullptr;
353 l->get()->type,
354 remoteAddr.pjPtr(),
355 static_cast<int>(remoteAddr.getLength()),
356 &sel,
357 remote_name.empty() ? nullptr : &tx_data,
358 &transport);
359
360 if (!transport || status != PJ_SUCCESS) {
361 JAMI_ERROR("Unable to get new TLS transport: {}", sip_utils::sip_strerror(status));
362 return nullptr;
363 }
364 auto ret = std::make_shared<SipTransport>(transport, l);
365 pjsip_transport_dec_ref(transport);
366 {
367 std::lock_guard lock(transportMapMutex_);
368 transports_[ret->get()] = ret;
369 }
370 return ret;
371}
372
373std::shared_ptr<SipTransport>
374SipTransportBroker::getChanneledTransport(const std::shared_ptr<SIPAccountBase>& account,
375 const std::shared_ptr<dhtnet::ChannelSocket>& socket,
377{
378 if (!socket)
379 return {};
380 auto sips_tr = std::make_unique<tls::ChanneledSIPTransport>(endpt_, socket, std::move(cb));
381 auto* tr = sips_tr->getTransportBase();
382 auto sip_tr = std::make_shared<SipTransport>(tr, socket->peerCertificate());
383 sip_tr->setDeviceId(socket->deviceId().toString());
384 sip_tr->setAccount(account);
385
386 {
387 std::lock_guard lock(transportMapMutex_);
388 // we do not check for key existence as we've just created it
389 // (member of new SipIceTransport instance)
390 transports_.emplace(tr, sip_tr);
391 }
392
393 sips_tr->start();
394 // NOLINTNEXTLINE: No value in capturing the released pointer
395 sips_tr.release(); // managed by PJSIP now
396 return sip_tr;
397}
398
399} // namespace jami
std::shared_ptr< SipTransport > addTransport(pjsip_transport *)
SipTransportBroker(pjsip_endpoint *endpt)
std::shared_ptr< SipTransport > getChanneledTransport(const std::shared_ptr< SIPAccountBase > &account, const std::shared_ptr< dhtnet::ChannelSocket > &socket, onShutdownCb &&cb)
void transportStateChanged(pjsip_transport *, pjsip_transport_state, const pjsip_transport_state_info *)
std::shared_ptr< SipTransport > getTlsTransport(const std::shared_ptr< TlsListener > &, const dhtnet::IpAddr &remote, const std::string &remote_name={})
std::shared_ptr< TlsListener > getTlsListener(const dhtnet::IpAddr &, const pjsip_tls_setting *)
void shutdown()
Start graceful shutdown procedure for all transports.
std::shared_ptr< SipTransport > getUdpTransport(const dhtnet::IpAddr &)
SIP transport wraps pjsip_transport.
bool isSecure() const
bool removeStateListener(uintptr_t lid)
void addStateListener(uintptr_t lid, const SipTransportStateCallback &cb)
void stateCallback(pjsip_transport_state state, const pjsip_transport_state_info *info)
SipTransport(pjsip_transport *)
static const char * stateToStr(pjsip_transport_state state)
static bool isAlive(pjsip_transport_state state)
#define JAMI_ERROR(formatstr,...)
Definition logger.h:243
#define JAMI_DEBUG(formatstr,...)
Definition logger.h:238
#define JAMI_WARNING(formatstr,...)
Definition logger.h:242
std::string sip_strerror(pj_status_t code)
static constexpr std::string_view toString(AuthDecodingState state)
constexpr const size_t TRANSPORT_STATE_SZ
void emitSignal(Args... args)
Definition jami_signal.h:64
constexpr const char * TRANSPORT_STATE_STR[]
static pjsip_endpoint * endpt_
std::function< void(pjsip_transport_state, const pjsip_transport_state_info *)> SipTransportStateCallback
std::function< void(void)> onShutdownCb
#define RETURN_IF_FAIL(A, VAL,...)
pj_ssl_sock_proto proto
pj_ssl_cipher cipher
pj_ssl_cert_verify_flag_t verifyStatus
std::shared_ptr< dht::crypto::Certificate > peerCert