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