blob: 5d2db509ce9747959025d080b2124b791c2ece98 [file] [log] [blame]
Adrien BĂ©raud612b55b2023-05-29 10:42:04 -04001/*
2 * Copyright (C) 2004-2023 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 "ice_options.h"
20#include "multiplexed_socket.h"
21
22#include <opendht/dhtrunner.h>
23#include <opendht/infohash.h>
24#include <opendht/value.h>
25#include <opendht/default_types.h>
26#include <opendht/sockaddr.h>
27#include <opendht/logger.h>
28
29#include <memory>
30#include <vector>
31#include <string>
32
33namespace jami {
34
35class ChannelSocket;
36class ConnectionManager;
37namespace upnp {
38class Controller;
39}
40namespace tls {
41class CertificateStore;
42}
43
44/**
45 * A PeerConnectionRequest is a request which ask for an initial connection
46 * It contains the ICE request an ID and if it's an answer
47 * Transmitted via the UDP DHT
48 */
49struct PeerConnectionRequest : public dht::EncryptedValue<PeerConnectionRequest>
50{
51 static const constexpr dht::ValueType& TYPE = dht::ValueType::USER_DATA;
52 static constexpr const char* key_prefix = "peer:"; ///< base to compute the DHT listen key
53 dht::Value::Id id = dht::Value::INVALID_ID;
54 std::string ice_msg {};
55 bool isAnswer {false};
56 std::string connType {}; // Used for push notifications to know why we open a new connection
57 MSGPACK_DEFINE_MAP(id, ice_msg, isAnswer, connType)
58};
59
60/**
61 * Used to accept or not an incoming ICE connection (default accept)
62 */
63using onICERequestCallback = std::function<bool(const DeviceId&)>;
64/**
65 * Used to accept or decline an incoming channel request
66 */
67using ChannelRequestCallback = std::function<bool(const std::shared_ptr<dht::crypto::Certificate>&,
68 const std::string& /* name */)>;
69/**
70 * Used by connectDevice, when the socket is ready
71 */
72using ConnectCallback = std::function<void(const std::shared_ptr<ChannelSocket>&, const DeviceId&)>;
73/**
74 * Used when an incoming connection is ready
75 */
76using ConnectionReadyCallback = std::function<
77 void(const DeviceId&, const std::string& /* channel_name */, std::shared_ptr<ChannelSocket>)>;
78
79using iOSConnectedCallback
80 = std::function<bool(const std::string& /* connType */, dht::InfoHash /* peer_h */)>;
81
82/**
83 * Manages connections to other devices
84 * @note the account MUST be valid if ConnectionManager lives
85 */
86class ConnectionManager
87{
88public:
89 class Config;
90
91 ConnectionManager(std::shared_ptr<Config> config_);
92 ~ConnectionManager();
93
94 /**
95 * Open a new channel between the account's device and another device
96 * This method will send a message on the account's DHT, wait a reply
97 * and then, create a Tls socket with remote peer.
98 * @param deviceId Remote device
99 * @param name Name of the channel
100 * @param cb Callback called when socket is ready ready
101 * @param noNewSocket Do not negotiate a new socket if there is none
102 * @param forceNewSocket Negotiate a new socket even if there is one // todo group with previous
103 * (enum)
104 * @param connType Type of the connection
105 */
106 void connectDevice(const DeviceId& deviceId,
107 const std::string& name,
108 ConnectCallback cb,
109 bool noNewSocket = false,
110 bool forceNewSocket = false,
111 const std::string& connType = "");
112 void connectDevice(const std::shared_ptr<dht::crypto::Certificate>& cert,
113 const std::string& name,
114 ConnectCallback cb,
115 bool noNewSocket = false,
116 bool forceNewSocket = false,
117 const std::string& connType = "");
118
119 /**
120 * Check if we are already connecting to a device with a specific name
121 * @param deviceId Remote device
122 * @param name Name of the channel
123 * @return if connecting
124 * @note isConnecting is not true just after connectDevice() as connectDevice is full async
125 */
126 bool isConnecting(const DeviceId& deviceId, const std::string& name) const;
127
128 /**
129 * Close all connections with a current device
130 * @param peerUri Peer URI
131 */
132 void closeConnectionsWith(const std::string& peerUri);
133
134 /**
135 * Method to call to listen to incoming requests
136 * @param deviceId Account's device
137 */
138 void onDhtConnected(const dht::crypto::PublicKey& devicePk);
139
140 /**
141 * Add a callback to decline or accept incoming ICE connections
142 * @param cb Callback to trigger
143 */
144 void onICERequest(onICERequestCallback&& cb);
145
146 /**
147 * Trigger cb on incoming peer channel
148 * @param cb Callback to trigger
149 * @note The callback is used to validate
150 * if the incoming request is accepted or not.
151 */
152 void onChannelRequest(ChannelRequestCallback&& cb);
153
154 /**
155 * Trigger cb when connection with peer is ready
156 * @param cb Callback to trigger
157 */
158 void onConnectionReady(ConnectionReadyCallback&& cb);
159
160 /**
161 * Trigger cb when connection with peer is ready for iOS devices
162 * @param cb Callback to trigger
163 */
164 void oniOSConnected(iOSConnectedCallback&& cb);
165
166 /**
167 * @return the number of active sockets
168 */
169 std::size_t activeSockets() const;
170
171 /**
172 * Log informations for all sockets
173 */
174 void monitor() const;
175
176 /**
177 * Send beacon on peers supporting it
178 */
179 void connectivityChanged();
180
181 /**
182 * Create and return ICE options.
183 */
184 void getIceOptions(std::function<void(IceTransportOptions&&)> cb) noexcept;
185 IceTransportOptions getIceOptions() const noexcept;
186
187 /**
188 * Get the published IP address, fallbacks to NAT if family is unspecified
189 * Prefers the usage of IPv4 if possible.
190 */
191 IpAddr getPublishedIpAddress(uint16_t family = PF_UNSPEC) const;
192
193 /**
194 * Set published IP address according to given family
195 */
196 void setPublishedAddress(const IpAddr& ip_addr);
197
198 /**
199 * Store the local/public addresses used to register
200 */
201 void storeActiveIpAddress(std::function<void()>&& cb = {});
202
203 std::shared_ptr<Config> getConfig();
204
205private:
206 ConnectionManager() = delete;
207 class Impl;
208 std::shared_ptr<Impl> pimpl_;
209};
210
211struct ConnectionManager::Config
212{
213 /**
214 * Determine if STUN public address resolution is required to register this account. In this
215 * case a STUN server hostname must be specified.
216 */
217 bool stunEnabled {false};
218
219 /**
220 * The STUN server hostname (optional), used to provide the public IP address in case the
221 * softphone stay behind a NAT.
222 */
223 std::string stunServer {};
224
225 /**
226 * Determine if TURN public address resolution is required to register this account. In this
227 * case a TURN server hostname must be specified.
228 */
229 bool turnEnabled {false};
230
231 /**
232 * The TURN server hostname (optional), used to provide the public IP address in case the
233 * softphone stay behind a NAT.
234 */
235 std::string turnServer;
236 std::string turnServerUserName;
237 std::string turnServerPwd;
238 std::string turnServerRealm;
239
240 mutable std::mutex cachedTurnMutex {};
241 dht::SockAddr cacheTurnV4 {};
242 dht::SockAddr cacheTurnV6 {};
243
244 std::string cachePath {};
245
246 std::shared_ptr<asio::io_context> ioContext;
247 std::shared_ptr<dht::DhtRunner> dht;
248 dht::crypto::Identity id;
249
250 tls::CertificateStore* certStore;
251
252 /**
253 * UPnP IGD controller and the mutex to access it
254 */
255 bool upnpEnabled;
256 std::shared_ptr<jami::upnp::Controller> upnpCtrl;
257
258 std::shared_ptr<dht::log::Logger> logger;
259
260 /**
261 * returns whether or not UPnP is enabled and active
262 * ie: if it is able to make port mappings
263 */
264 bool getUPnPActive() const;
265};
266
267} // namespace jami