Ring Daemon 16.0.0
Loading...
Searching...
No Matches
siptransport.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2025 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
23
24#include "compiler_intrinsics.h"
25#include "sip/sipvoiplink.h"
26
27#include <pjsip.h>
28#include <pjsip/sip_types.h>
29#include <pjsip/sip_transport_tls.h>
30#include <pj/ssl_sock.h>
31#include <pjnath.h>
32#include <pjnath/stun_config.h>
33#include <pjlib.h>
34#include <pjlib-util.h>
35
36#include <dhtnet/multiplexed_socket.h>
37#include <dhtnet/ip_utils.h>
38#include <dhtnet/tls_session.h>
39
40#include <opendht/crypto.h>
41
42#include <stdexcept>
43#include <sstream>
44#include <algorithm>
45
46#define RETURN_IF_FAIL(A, VAL, ...) \
47 if (!(A)) { \
48 JAMI_ERROR(__VA_ARGS__); \
49 return (VAL); \
50 }
51
52namespace jami {
53
54constexpr const char* TRANSPORT_STATE_STR[] = {"CONNECTED",
55 "DISCONNECTED",
56 "SHUTDOWN",
57 "DESTROY",
58 "UNKNOWN STATE"};
59constexpr const size_t TRANSPORT_STATE_SZ = std::size(TRANSPORT_STATE_STR);
60
61void
62SipTransport::deleteTransport(pjsip_transport* t)
63{
65}
66
68 : transport_(nullptr, deleteTransport)
69{
71 throw std::runtime_error("Invalid transport");
72
73 // Set pointer here, right after the successful pjsip_transport_add_ref
74 transport_.reset(t);
75
76 JAMI_DEBUG("SipTransport@{} tr={} rc={:d}",
77 fmt::ptr(this),
78 fmt::ptr(transport_.get()),
79 pj_atomic_get(transport_->ref_cnt));
80}
81
82SipTransport::SipTransport(pjsip_transport* t, const std::shared_ptr<TlsListener>& l)
83 : SipTransport(t)
84{
85 tlsListener_ = l;
86}
87
89 const std::shared_ptr<dht::crypto::Certificate>& peerCertficate)
90 : SipTransport(t)
91{
92 tlsInfos_.peerCert = peerCertficate;
93}
94
96{
97 JAMI_DEBUG("~SipTransport@{} tr={} rc={:d}",
98 fmt::ptr(this),
99 fmt::ptr(transport_.get()),
100 pj_atomic_get(transport_->ref_cnt));
101}
102
103bool
109
110const char*
112{
113 return TRANSPORT_STATE_STR[std::min<size_t>(state, TRANSPORT_STATE_SZ - 1)];
114}
115
116void
118{
119 connected_ = state == PJSIP_TP_STATE_CONNECTED;
120
121 auto extInfo = static_cast<const pjsip_tls_state_info*>(info->ext_info);
122 if (isSecure() && extInfo && extInfo->ssl_sock_info && extInfo->ssl_sock_info->established) {
123 auto tlsInfo = extInfo->ssl_sock_info;
124 tlsInfos_.proto = (pj_ssl_sock_proto) tlsInfo->proto;
125 tlsInfos_.cipher = tlsInfo->cipher;
126 tlsInfos_.verifyStatus = (pj_ssl_cert_verify_flag_t) tlsInfo->verify_status;
127 if (!tlsInfos_.peerCert) {
128 const auto& peers = tlsInfo->remote_cert_info->raw_chain;
129 std::vector<std::pair<const uint8_t*, const uint8_t*>> bits;
130 bits.resize(peers.cnt);
131 std::transform(peers.cert_raw,
132 peers.cert_raw + peers.cnt,
133 std::begin(bits),
134 [](const pj_str_t& crt) {
135 return std::make_pair((uint8_t*) crt.ptr,
136 (uint8_t*) (crt.ptr + crt.slen));
137 });
138 tlsInfos_.peerCert = std::make_shared<dht::crypto::Certificate>(bits);
139 }
140 } else {
141 tlsInfos_ = {};
142 }
143
144 std::vector<SipTransportStateCallback> cbs;
145 {
146 std::lock_guard lock(stateListenersMutex_);
147 cbs.reserve(stateListeners_.size());
148 for (auto& l : stateListeners_)
149 cbs.push_back(l.second);
150 }
151 for (auto& cb : cbs)
152 cb(state, info);
153}
154
155void
157{
158 std::lock_guard lock(stateListenersMutex_);
159 auto pair = stateListeners_.insert(std::make_pair(lid, cb));
160 if (not pair.second)
161 pair.first->second = cb;
162}
163
164bool
166{
167 std::lock_guard lock(stateListenersMutex_);
168 auto it = stateListeners_.find(lid);
169 if (it != stateListeners_.end()) {
170 stateListeners_.erase(it);
171 return true;
172 }
173 return false;
174}
175
178{
179 return 1232; /* Hardcoded yes (it's the IPv6 value).
180 * This method is broken by definition.
181 * A MTU should not be defined at this layer.
182 * And a correct value should come from the underlying transport itself,
183 * not from a constant…
184 */
185}
186
190
192{
193 shutdown();
194
195 udpTransports_.clear();
196 transports_.clear();
197
198 JAMI_DEBUG("Destroying SipTransportBroker@{}…", fmt::ptr(this));
199}
200
201void
204 const pjsip_transport_state_info* info)
205{
206 JAMI_DEBUG("PJSIP transport@{} {} → {}", fmt::ptr(tp), tp->info, SipTransport::stateToStr(state));
207
208 // First ensure that this transport is handled by us
209 // and remove it from any mapping if destroy pending or done.
210
211 std::shared_ptr<SipTransport> sipTransport;
212 std::lock_guard lock(transportMapMutex_);
213 auto key = transports_.find(tp);
214 if (key == transports_.end())
215 return;
216
217 sipTransport = key->second.lock();
218
219 if (!isDestroying_ && state == PJSIP_TP_STATE_DESTROY) {
220 // maps cleanup
221 JAMI_DEBUG("Unmap PJSIP transport@{} {{SipTransport@{}}}", fmt::ptr(tp), fmt::ptr(sipTransport.get()));
222 transports_.erase(key);
223
224 // If UDP
225 const auto type = tp->key.type;
226 if (type == PJSIP_TRANSPORT_UDP or type == PJSIP_TRANSPORT_UDP6) {
227 const auto updKey = std::find_if(udpTransports_.cbegin(),
228 udpTransports_.cend(),
229 [tp](const std::pair<dhtnet::IpAddr, pjsip_transport*>& pair) {
230 return pair.second == tp;
231 });
232 if (updKey != udpTransports_.cend())
233 udpTransports_.erase(updKey);
234 }
235 }
236
237 // Propagate the event to the appropriate transport
238 // Note the SipTransport may not be in our mappings if marked as dead
239 if (sipTransport)
240 sipTransport->stateCallback(state, info);
241}
242
243std::shared_ptr<SipTransport>
245{
246 if (t) {
247 std::lock_guard lock(transportMapMutex_);
248
249 auto key = transports_.find(t);
250 if (key != transports_.end()) {
251 if (auto sipTr = key->second.lock())
252 return sipTr;
253 }
254
255 auto sipTr = std::make_shared<SipTransport>(t);
256 if (key != transports_.end())
257 key->second = sipTr;
258 else
259 transports_.emplace(std::make_pair(t, sipTr));
260 return sipTr;
261 }
262
263 return nullptr;
264}
265
266void
268{
269 std::unique_lock lock(transportMapMutex_);
270 isDestroying_ = true;
271 for (auto& t : transports_) {
272 if (auto transport = t.second.lock()) {
273 pjsip_transport_shutdown(transport->get());
274 }
275 }
276}
277
278std::shared_ptr<SipTransport>
280{
281 std::lock_guard lock(transportMapMutex_);
282 auto itp = udpTransports_.find(ipAddress);
283 if (itp != udpTransports_.end()) {
284 auto it = transports_.find(itp->second);
285 if (it != transports_.end()) {
286 if (auto spt = it->second.lock()) {
287 JAMI_DEBUG("Reusing transport {}", ipAddress.toString(true));
288 return spt;
289 } else {
290 // Transport still exists but have not been destroyed yet.
291 JAMI_WARNING("Recycling transport {}", ipAddress.toString(true));
292 auto ret = std::make_shared<SipTransport>(itp->second);
293 it->second = ret;
294 return ret;
295 }
296 } else {
297 JAMI_WARNING("Cleaning up UDP transport {}", ipAddress.toString(true));
298 udpTransports_.erase(itp);
299 }
300 }
301 auto ret = createUdpTransport(ipAddress);
302 if (ret) {
303 udpTransports_[ipAddress] = ret->get();
304 transports_[ret->get()] = ret;
305 }
306 return ret;
307}
308
309std::shared_ptr<SipTransport>
310SipTransportBroker::createUdpTransport(const dhtnet::IpAddr& ipAddress)
311{
312 RETURN_IF_FAIL(ipAddress, nullptr, "Unable to determine IP address for this transport");
313
316 pj_cfg.bind_addr = ipAddress;
317 pjsip_transport* transport = nullptr;
318 if (pj_status_t status = pjsip_udp_transport_start2(endpt_, &pj_cfg, &transport)) {
319 JAMI_ERROR("pjsip_udp_transport_start2 failed with error {:d}: {:s}",
320 status, sip_utils::sip_strerror(status));
321 JAMI_ERROR("UDP IPv{} Transport did not start on {}",
322 ipAddress.isIpv4() ? "4" : "6",
324 return nullptr;
325 }
326
327 JAMI_DEBUG("Created UDP transport on address {}", ipAddress.toString(true));
328 return std::make_shared<SipTransport>(transport);
329}
330
331std::shared_ptr<TlsListener>
333{
334 RETURN_IF_FAIL(settings, nullptr, "TLS settings not specified");
335 RETURN_IF_FAIL(ipAddress, nullptr, "Unable to determine IP address for this transport");
336 JAMI_DEBUG("Creating TLS listener on {:s}…", ipAddress.toString(true));
337
338 pjsip_tpfactory* listener = nullptr;
339 const pj_status_t status
340 = pjsip_tls_transport_start2(endpt_, settings, ipAddress.pjPtr(), nullptr, 1, &listener);
341 if (status != PJ_SUCCESS) {
342 JAMI_ERROR("TLS listener did not start: {}", sip_utils::sip_strerror(status));
343 return nullptr;
344 }
345 return std::make_shared<TlsListener>(listener);
346}
347
348std::shared_ptr<SipTransport>
349SipTransportBroker::getTlsTransport(const std::shared_ptr<TlsListener>& l,
350 const dhtnet::IpAddr& remote,
351 const std::string& remote_name)
352{
353 if (!l || !remote)
354 return nullptr;
355 dhtnet::IpAddr remoteAddr {remote};
356 if (remoteAddr.getPort() == 0)
358
359 JAMI_DEBUG("Get new TLS transport to {}", remoteAddr.toString(true));
362 sel.u.listener = l->get();
363 sel.disable_connection_reuse = PJ_FALSE;
364
366 tx_data.dest_info.name = pj_str_t {(char*) remote_name.data(), (pj_ssize_t) remote_name.size()};
367
368 pjsip_transport* transport = nullptr;
370 l->get()->type,
371 remoteAddr.pjPtr(),
372 remoteAddr.getLength(),
373 &sel,
374 remote_name.empty() ? nullptr : &tx_data,
375 &transport);
376
377 if (!transport || status != PJ_SUCCESS) {
378 JAMI_ERROR("Unable to get new TLS transport: {}", sip_utils::sip_strerror(status));
379 return nullptr;
380 }
381 auto ret = std::make_shared<SipTransport>(transport, l);
382 pjsip_transport_dec_ref(transport);
383 {
384 std::lock_guard lock(transportMapMutex_);
385 transports_[ret->get()] = ret;
386 }
387 return ret;
388}
389
390std::shared_ptr<SipTransport>
391SipTransportBroker::getChanneledTransport(const std::shared_ptr<SIPAccountBase>& account,
392 const std::shared_ptr<dhtnet::ChannelSocket>& socket,
394{
395 if (!socket)
396 return {};
397 auto sips_tr = std::make_unique<tls::ChanneledSIPTransport>(endpt_,
398 socket,
399 std::move(cb));
400 auto tr = sips_tr->getTransportBase();
401 auto sip_tr = std::make_shared<SipTransport>(tr, socket->peerCertificate());
402 sip_tr->setDeviceId(socket->deviceId().toString());
403 sip_tr->setAccount(account);
404
405 {
406 std::lock_guard lock(transportMapMutex_);
407 // we do not check for key existence as we've just created it
408 // (member of new SipIceTransport instance)
409 transports_.emplace(tr, sip_tr);
410 }
411
412 sips_tr->start();
413 sips_tr.release(); // managed by PJSIP now
414 return sip_tr;
415}
416
417} // 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, 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:228
#define JAMI_DEBUG(formatstr,...)
Definition logger.h:226
#define JAMI_WARNING(formatstr,...)
Definition logger.h:227
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 ring_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