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 : #pragma once
18 :
19 : #include <dhtnet/ip_utils.h>
20 : #ifdef HAVE_CONFIG_H
21 : #include "config.h"
22 : #endif
23 :
24 : #include "noncopyable.h"
25 : #include "logger.h"
26 :
27 : #include <pjsip.h>
28 : #include <pjnath/stun_config.h>
29 :
30 : #include <atomic>
31 : #include <functional>
32 : #include <mutex>
33 : #include <map>
34 : #include <string>
35 : #include <memory>
36 :
37 : // OpenDHT
38 : namespace dht {
39 : namespace crypto {
40 : struct Certificate;
41 : }
42 : } // namespace dht
43 :
44 : namespace dhtnet {
45 : class ChannelSocket;
46 : } // namespace dhtnet
47 :
48 : namespace jami {
49 : class SIPAccountBase;
50 : using onShutdownCb = std::function<void(void)>;
51 :
52 : struct TlsListener
53 : {
54 : TlsListener() {}
55 0 : TlsListener(pjsip_tpfactory* f)
56 0 : : listener(f)
57 0 : {}
58 0 : virtual ~TlsListener()
59 0 : {
60 0 : JAMI_LOG("Destroying listener");
61 0 : listener->destroy(listener);
62 0 : }
63 0 : pjsip_tpfactory* get() { return listener; }
64 :
65 : private:
66 : NON_COPYABLE(TlsListener);
67 : pjsip_tpfactory* listener {nullptr};
68 : };
69 :
70 : struct TlsInfos
71 : {
72 : pj_ssl_cipher cipher {PJ_TLS_UNKNOWN_CIPHER};
73 : pj_ssl_sock_proto proto {PJ_SSL_SOCK_PROTO_DEFAULT};
74 : pj_ssl_cert_verify_flag_t verifyStatus {};
75 : std::shared_ptr<dht::crypto::Certificate> peerCert {};
76 : };
77 :
78 : using SipTransportStateCallback = std::function<void(pjsip_transport_state, const pjsip_transport_state_info*)>;
79 :
80 : /**
81 : * SIP transport wraps pjsip_transport.
82 : */
83 : class SipTransport
84 : {
85 : public:
86 : SipTransport(pjsip_transport*);
87 : SipTransport(pjsip_transport*, const std::shared_ptr<TlsListener>&);
88 : // If the SipTransport is a channeled transport, we are already connected to the peer,
89 : // so, we can directly set tlsInfos_.peerCert and avoid any copy
90 : SipTransport(pjsip_transport* t, const std::shared_ptr<dht::crypto::Certificate>& peerCertficate);
91 :
92 : ~SipTransport();
93 :
94 : static const char* stateToStr(pjsip_transport_state state);
95 :
96 : void stateCallback(pjsip_transport_state state, const pjsip_transport_state_info* info);
97 :
98 1102 : pjsip_transport* get() { return transport_.get(); }
99 :
100 : void addStateListener(uintptr_t lid, const SipTransportStateCallback& cb);
101 : bool removeStateListener(uintptr_t lid);
102 :
103 698 : bool isSecure() const { return PJSIP_TRANSPORT_IS_SECURE(transport_); }
104 :
105 153 : const TlsInfos& getTlsInfos() const { return tlsInfos_; }
106 :
107 : static bool isAlive(pjsip_transport_state state);
108 :
109 : /** Only makes sense for connection-oriented transports */
110 11 : bool isConnected() const noexcept { return connected_; }
111 : // For transports whose pjsip side never reports PJSIP_TP_STATE_CONNECTED.
112 216 : void setConnected() noexcept { connected_ = true; }
113 :
114 216 : inline void setDeviceId(const std::string& deviceId) { deviceId_ = deviceId; }
115 315 : inline std::string_view deviceId() const { return deviceId_; }
116 216 : inline void setPeerAccountId(const std::string& peerId) { peerAccountId_ = peerId; }
117 96 : inline std::string_view peerAccountId() const { return peerAccountId_; }
118 216 : inline void setAccount(const std::shared_ptr<SIPAccountBase>& account) { account_ = account; }
119 118 : inline const std::weak_ptr<SIPAccountBase>& getAccount() const { return account_; }
120 :
121 : uint16_t getTlsMtu();
122 :
123 : private:
124 : NON_COPYABLE(SipTransport);
125 :
126 : static void deleteTransport(pjsip_transport* t);
127 : struct TransportDeleter
128 : {
129 243 : void operator()(pjsip_transport* t) const noexcept { deleteTransport(t); }
130 : };
131 :
132 : std::unique_ptr<pjsip_transport, TransportDeleter> transport_;
133 : std::shared_ptr<TlsListener> tlsListener_;
134 : std::mutex stateListenersMutex_;
135 : std::map<uintptr_t, SipTransportStateCallback> stateListeners_;
136 : std::weak_ptr<SIPAccountBase> account_ {};
137 :
138 : std::atomic_bool connected_ {false};
139 : std::string deviceId_ {};
140 : std::string peerAccountId_ {};
141 : TlsInfos tlsInfos_;
142 : };
143 :
144 : class IceTransport;
145 : namespace tls {
146 : struct TlsParams;
147 : };
148 :
149 : /**
150 : * Manages the transports and receive callbacks from PJSIP
151 : */
152 : class SipTransportBroker
153 : {
154 : public:
155 : SipTransportBroker(pjsip_endpoint* endpt);
156 : ~SipTransportBroker();
157 :
158 : std::shared_ptr<SipTransport> getUdpTransport(const dhtnet::IpAddr&);
159 :
160 : std::shared_ptr<TlsListener> getTlsListener(const dhtnet::IpAddr&, const pjsip_tls_setting*);
161 :
162 : std::shared_ptr<SipTransport> getTlsTransport(const std::shared_ptr<TlsListener>&,
163 : const dhtnet::IpAddr& remote,
164 : const std::string& remote_name = {});
165 :
166 : std::shared_ptr<SipTransport> addTransport(pjsip_transport*);
167 :
168 : std::shared_ptr<SipTransport> getChanneledTransport(const std::shared_ptr<SIPAccountBase>& account,
169 : const std::shared_ptr<dhtnet::ChannelSocket>& socket,
170 : onShutdownCb&& cb);
171 :
172 : /**
173 : * Start graceful shutdown procedure for all transports
174 : */
175 : void shutdown();
176 :
177 : void transportStateChanged(pjsip_transport*, pjsip_transport_state, const pjsip_transport_state_info*);
178 :
179 : private:
180 : NON_COPYABLE(SipTransportBroker);
181 :
182 : /**
183 : * Create SIP UDP transport from account's setting
184 : * @param account The account for which a transport must be created.
185 : * @param IP protocol version to use, can be pj_AF_INET() or pj_AF_INET6()
186 : * @return a pointer to the new transport
187 : */
188 : std::shared_ptr<SipTransport> createUdpTransport(const dhtnet::IpAddr&);
189 :
190 : /**
191 : * List of transports so we can bubble the events up.
192 : */
193 : std::map<pjsip_transport*, std::weak_ptr<SipTransport>> transports_ {};
194 : std::mutex transportMapMutex_ {};
195 :
196 : /**
197 : * Transports are stored in this map in order to retrieve them in case
198 : * several accounts would share the same port number.
199 : */
200 : std::map<dhtnet::IpAddr, pjsip_transport*> udpTransports_;
201 :
202 : pjsip_endpoint* endpt_;
203 : std::atomic_bool isDestroying_ {false};
204 : };
205 :
206 : } // namespace jami
|