Line data Source code
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"
19 : #include "connectivity/sip_utils.h"
20 :
21 : #include "jamidht/abstract_sip_transport.h"
22 : #include "jamidht/channeled_transport.h"
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 :
52 : namespace jami {
53 :
54 : constexpr const char* TRANSPORT_STATE_STR[] = {"CONNECTED", "DISCONNECTED", "SHUTDOWN", "DESTROY", "UNKNOWN STATE"};
55 : constexpr const size_t TRANSPORT_STATE_SZ = std::size(TRANSPORT_STATE_STR);
56 :
57 : void
58 185 : SipTransport::deleteTransport(pjsip_transport* t)
59 : {
60 185 : pjsip_transport_dec_ref(t);
61 185 : }
62 :
63 185 : SipTransport::SipTransport(pjsip_transport* t)
64 185 : : transport_(nullptr)
65 : {
66 185 : if (not t or pjsip_transport_add_ref(t) != PJ_SUCCESS)
67 0 : throw std::runtime_error("Invalid transport");
68 :
69 : // Set pointer here, right after the successful pjsip_transport_add_ref
70 185 : transport_.reset(t);
71 :
72 740 : JAMI_DEBUG("SipTransport@{} tr={} rc={:d}",
73 : fmt::ptr(this),
74 : fmt::ptr(transport_.get()),
75 : pj_atomic_get(transport_->ref_cnt));
76 185 : }
77 :
78 0 : SipTransport::SipTransport(pjsip_transport* t, const std::shared_ptr<TlsListener>& l)
79 0 : : SipTransport(t)
80 : {
81 0 : tlsListener_ = l;
82 0 : }
83 :
84 176 : SipTransport::SipTransport(pjsip_transport* t, const std::shared_ptr<dht::crypto::Certificate>& peerCertficate)
85 176 : : SipTransport(t)
86 : {
87 176 : tlsInfos_.peerCert = peerCertficate;
88 176 : }
89 :
90 185 : SipTransport::~SipTransport()
91 : {
92 740 : JAMI_DEBUG("~SipTransport@{} tr={} rc={:d}",
93 : fmt::ptr(this),
94 : fmt::ptr(transport_.get()),
95 : pj_atomic_get(transport_->ref_cnt));
96 185 : }
97 :
98 : bool
99 16 : SipTransport::isAlive(pjsip_transport_state state)
100 : {
101 16 : return state != PJSIP_TP_STATE_DISCONNECTED && state != PJSIP_TP_STATE_SHUTDOWN && state != PJSIP_TP_STATE_DESTROY;
102 : }
103 :
104 : const char*
105 336 : SipTransport::stateToStr(pjsip_transport_state state)
106 : {
107 336 : return TRANSPORT_STATE_STR[std::min<size_t>(state, TRANSPORT_STATE_SZ - 1)];
108 : }
109 :
110 : void
111 102 : SipTransport::stateCallback(pjsip_transport_state state, const pjsip_transport_state_info* info)
112 : {
113 102 : connected_ = state == PJSIP_TP_STATE_CONNECTED;
114 :
115 102 : auto extInfo = static_cast<const pjsip_tls_state_info*>(info->ext_info);
116 102 : if (isSecure() && extInfo && extInfo->ssl_sock_info && extInfo->ssl_sock_info->established) {
117 0 : auto tlsInfo = extInfo->ssl_sock_info;
118 0 : tlsInfos_.proto = (pj_ssl_sock_proto) tlsInfo->proto;
119 0 : tlsInfos_.cipher = tlsInfo->cipher;
120 0 : tlsInfos_.verifyStatus = (pj_ssl_cert_verify_flag_t) tlsInfo->verify_status;
121 0 : if (!tlsInfos_.peerCert) {
122 0 : const auto& peers = tlsInfo->remote_cert_info->raw_chain;
123 0 : std::vector<std::pair<const uint8_t*, const uint8_t*>> bits;
124 0 : bits.resize(peers.cnt);
125 0 : std::transform(peers.cert_raw, peers.cert_raw + peers.cnt, std::begin(bits), [](const pj_str_t& crt) {
126 0 : return std::make_pair((uint8_t*) crt.ptr, (uint8_t*) (crt.ptr + crt.slen));
127 : });
128 0 : tlsInfos_.peerCert = std::make_shared<dht::crypto::Certificate>(bits);
129 0 : }
130 : } else {
131 102 : tlsInfos_ = {};
132 : }
133 :
134 102 : std::vector<SipTransportStateCallback> cbs;
135 : {
136 102 : std::lock_guard lock(stateListenersMutex_);
137 102 : cbs.reserve(stateListeners_.size());
138 216 : for (auto& l : stateListeners_)
139 114 : cbs.push_back(l.second);
140 102 : }
141 216 : for (auto& cb : cbs)
142 114 : cb(state, info);
143 102 : }
144 :
145 : void
146 274 : SipTransport::addStateListener(uintptr_t lid, SipTransportStateCallback cb)
147 : {
148 274 : std::lock_guard lock(stateListenersMutex_);
149 274 : auto pair = stateListeners_.insert(std::make_pair(lid, cb));
150 274 : if (not pair.second)
151 1 : pair.first->second = cb;
152 274 : }
153 :
154 : bool
155 272 : SipTransport::removeStateListener(uintptr_t lid)
156 : {
157 272 : std::lock_guard lock(stateListenersMutex_);
158 272 : auto it = stateListeners_.find(lid);
159 272 : if (it != stateListeners_.end()) {
160 10 : stateListeners_.erase(it);
161 10 : return true;
162 : }
163 262 : return false;
164 272 : }
165 :
166 : uint16_t
167 304 : SipTransport::getTlsMtu()
168 : {
169 304 : return 1232; /* Hardcoded yes (it's the IPv6 value).
170 : * This method is broken by definition.
171 : * A MTU should not be defined at this layer.
172 : * And a correct value should come from the underlying transport itself,
173 : * not from a constant…
174 : */
175 : }
176 :
177 31 : SipTransportBroker::SipTransportBroker(pjsip_endpoint* endpt)
178 31 : : endpt_(endpt)
179 31 : {}
180 :
181 31 : SipTransportBroker::~SipTransportBroker()
182 : {
183 31 : shutdown();
184 :
185 31 : udpTransports_.clear();
186 31 : transports_.clear();
187 :
188 124 : JAMI_DEBUG("Destroying SipTransportBroker@{}…", fmt::ptr(this));
189 31 : }
190 :
191 : void
192 336 : SipTransportBroker::transportStateChanged(pjsip_transport* tp,
193 : pjsip_transport_state state,
194 : const pjsip_transport_state_info* info)
195 : {
196 1344 : JAMI_DEBUG("PJSIP transport@{} {} → {}", fmt::ptr(tp), tp->info, SipTransport::stateToStr(state));
197 :
198 : // First ensure that this transport is handled by us
199 : // and remove it from any mapping if destroy pending or done.
200 :
201 336 : std::shared_ptr<SipTransport> sipTransport;
202 336 : std::lock_guard lock(transportMapMutex_);
203 336 : auto key = transports_.find(tp);
204 336 : if (key == transports_.end())
205 0 : return;
206 :
207 336 : sipTransport = key->second.lock();
208 :
209 336 : if (!isDestroying_ && state == PJSIP_TP_STATE_DESTROY) {
210 : // maps cleanup
211 632 : JAMI_DEBUG("Unmap PJSIP transport@{} {{SipTransport@{}}}", fmt::ptr(tp), fmt::ptr(sipTransport.get()));
212 158 : transports_.erase(key);
213 :
214 : // If UDP
215 158 : const auto type = tp->key.type;
216 158 : if (type == PJSIP_TRANSPORT_UDP or type == PJSIP_TRANSPORT_UDP6) {
217 0 : const auto updKey = std::find_if(udpTransports_.cbegin(),
218 : udpTransports_.cend(),
219 0 : [tp](const std::pair<dhtnet::IpAddr, pjsip_transport*>& pair) {
220 0 : return pair.second == tp;
221 : });
222 0 : if (updKey != udpTransports_.cend())
223 0 : udpTransports_.erase(updKey);
224 : }
225 : }
226 :
227 : // Propagate the event to the appropriate transport
228 : // Note the SipTransport may not be in our mappings if marked as dead
229 336 : if (sipTransport)
230 102 : sipTransport->stateCallback(state, info);
231 336 : }
232 :
233 : std::shared_ptr<SipTransport>
234 100 : SipTransportBroker::addTransport(pjsip_transport* t)
235 : {
236 100 : if (t) {
237 100 : std::lock_guard lock(transportMapMutex_);
238 :
239 100 : auto key = transports_.find(t);
240 100 : if (key != transports_.end()) {
241 100 : if (auto sipTr = key->second.lock())
242 100 : return sipTr;
243 : }
244 :
245 0 : auto sipTr = std::make_shared<SipTransport>(t);
246 0 : if (key != transports_.end())
247 0 : key->second = sipTr;
248 : else
249 0 : transports_.emplace(std::make_pair(t, sipTr));
250 0 : return sipTr;
251 100 : }
252 :
253 0 : return nullptr;
254 : }
255 :
256 : void
257 62 : SipTransportBroker::shutdown()
258 : {
259 62 : std::unique_lock lock(transportMapMutex_);
260 62 : isDestroying_ = true;
261 110 : for (auto& t : transports_) {
262 48 : if (auto transport = t.second.lock()) {
263 0 : pjsip_transport_shutdown(transport->get());
264 48 : }
265 : }
266 62 : }
267 :
268 : std::shared_ptr<SipTransport>
269 13 : SipTransportBroker::getUdpTransport(const dhtnet::IpAddr& ipAddress)
270 : {
271 13 : std::lock_guard lock(transportMapMutex_);
272 13 : auto itp = udpTransports_.find(ipAddress);
273 13 : if (itp != udpTransports_.end()) {
274 7 : auto it = transports_.find(itp->second);
275 7 : if (it != transports_.end()) {
276 7 : if (auto spt = it->second.lock()) {
277 16 : JAMI_DEBUG("Reusing transport {}", ipAddress.toString(true));
278 4 : return spt;
279 : } else {
280 : // Transport still exists but have not been destroyed yet.
281 12 : JAMI_WARNING("Recycling transport {}", ipAddress.toString(true));
282 3 : auto ret = std::make_shared<SipTransport>(itp->second);
283 3 : it->second = ret;
284 3 : return ret;
285 10 : }
286 : } else {
287 0 : JAMI_WARNING("Cleaning up UDP transport {}", ipAddress.toString(true));
288 0 : udpTransports_.erase(itp);
289 : }
290 : }
291 6 : auto ret = createUdpTransport(ipAddress);
292 6 : if (ret) {
293 6 : udpTransports_[ipAddress] = ret->get();
294 6 : transports_[ret->get()] = ret;
295 : }
296 6 : return ret;
297 13 : }
298 :
299 : std::shared_ptr<SipTransport>
300 6 : SipTransportBroker::createUdpTransport(const dhtnet::IpAddr& ipAddress)
301 : {
302 6 : RETURN_IF_FAIL(ipAddress, nullptr, "Unable to determine IP address for this transport");
303 :
304 : pjsip_udp_transport_cfg pj_cfg;
305 6 : pjsip_udp_transport_cfg_default(&pj_cfg, ipAddress.getFamily());
306 6 : pj_cfg.bind_addr = ipAddress;
307 6 : pjsip_transport* transport = nullptr;
308 6 : if (pj_status_t status = pjsip_udp_transport_start2(endpt_, &pj_cfg, &transport)) {
309 0 : JAMI_ERROR("pjsip_udp_transport_start2 failed with error {:d}: {:s}", status, sip_utils::sip_strerror(status));
310 0 : JAMI_ERROR("UDP IPv{} Transport did not start on {}", ipAddress.isIpv4() ? "4" : "6", ipAddress.toString(true));
311 0 : return nullptr;
312 : }
313 :
314 24 : JAMI_DEBUG("Created UDP transport on address {}", ipAddress.toString(true));
315 6 : return std::make_shared<SipTransport>(transport);
316 : }
317 :
318 : std::shared_ptr<TlsListener>
319 0 : SipTransportBroker::getTlsListener(const dhtnet::IpAddr& ipAddress, const pjsip_tls_setting* settings)
320 : {
321 0 : RETURN_IF_FAIL(settings, nullptr, "TLS settings not specified");
322 0 : RETURN_IF_FAIL(ipAddress, nullptr, "Unable to determine IP address for this transport");
323 0 : JAMI_DEBUG("Creating TLS listener on {:s}…", ipAddress.toString(true));
324 :
325 0 : pjsip_tpfactory* listener = nullptr;
326 0 : const pj_status_t status = pjsip_tls_transport_start2(endpt_, settings, ipAddress.pjPtr(), nullptr, 1, &listener);
327 0 : if (status != PJ_SUCCESS) {
328 0 : JAMI_ERROR("TLS listener did not start: {}", sip_utils::sip_strerror(status));
329 0 : return nullptr;
330 : }
331 0 : return std::make_shared<TlsListener>(listener);
332 : }
333 :
334 : std::shared_ptr<SipTransport>
335 0 : SipTransportBroker::getTlsTransport(const std::shared_ptr<TlsListener>& l,
336 : const dhtnet::IpAddr& remote,
337 : const std::string& remote_name)
338 : {
339 0 : if (!l || !remote)
340 0 : return nullptr;
341 0 : dhtnet::IpAddr remoteAddr {remote};
342 0 : if (remoteAddr.getPort() == 0)
343 0 : remoteAddr.setPort(pjsip_transport_get_default_port_for_type(l->get()->type));
344 :
345 0 : JAMI_DEBUG("Get new TLS transport to {}", remoteAddr.toString(true));
346 : pjsip_tpselector sel;
347 0 : sel.type = PJSIP_TPSELECTOR_LISTENER;
348 0 : sel.u.listener = l->get();
349 0 : sel.disable_connection_reuse = PJ_FALSE;
350 :
351 : pjsip_tx_data tx_data;
352 0 : tx_data.dest_info.name = pj_str_t {(char*) remote_name.data(), (pj_ssize_t) remote_name.size()};
353 :
354 0 : pjsip_transport* transport = nullptr;
355 0 : pj_status_t status = pjsip_endpt_acquire_transport2(endpt_,
356 0 : l->get()->type,
357 0 : remoteAddr.pjPtr(),
358 0 : remoteAddr.getLength(),
359 : &sel,
360 0 : remote_name.empty() ? nullptr : &tx_data,
361 : &transport);
362 :
363 0 : if (!transport || status != PJ_SUCCESS) {
364 0 : JAMI_ERROR("Unable to get new TLS transport: {}", sip_utils::sip_strerror(status));
365 0 : return nullptr;
366 : }
367 0 : auto ret = std::make_shared<SipTransport>(transport, l);
368 0 : pjsip_transport_dec_ref(transport);
369 : {
370 0 : std::lock_guard lock(transportMapMutex_);
371 0 : transports_[ret->get()] = ret;
372 0 : }
373 0 : return ret;
374 0 : }
375 :
376 : std::shared_ptr<SipTransport>
377 176 : SipTransportBroker::getChanneledTransport(const std::shared_ptr<SIPAccountBase>& account,
378 : const std::shared_ptr<dhtnet::ChannelSocket>& socket,
379 : onShutdownCb&& cb)
380 : {
381 176 : if (!socket)
382 0 : return {};
383 176 : auto sips_tr = std::make_unique<tls::ChanneledSIPTransport>(endpt_, socket, std::move(cb));
384 176 : auto tr = sips_tr->getTransportBase();
385 176 : auto sip_tr = std::make_shared<SipTransport>(tr, socket->peerCertificate());
386 175 : sip_tr->setDeviceId(socket->deviceId().toString());
387 176 : sip_tr->setAccount(account);
388 :
389 : {
390 176 : std::lock_guard lock(transportMapMutex_);
391 : // we do not check for key existence as we've just created it
392 : // (member of new SipIceTransport instance)
393 176 : transports_.emplace(tr, sip_tr);
394 176 : }
395 :
396 176 : sips_tr->start();
397 176 : sips_tr.release(); // managed by PJSIP now
398 176 : return sip_tr;
399 176 : }
400 :
401 : } // namespace jami
|