blob: d8e6e163873a3afb9bc2808e6f9d4ba046067649 [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 "ip_utils.h"
20#include "generic_io.h"
21
22#include <opendht/default_types.h>
23#include <condition_variable>
24
25#include <cstdint>
26
27namespace asio {
28class io_context;
29}
30
31namespace dht {
32namespace log {
33class Logger;
34}
35}
36
37namespace jami {
38
39using Logger = dht::log::Logger;
40class IceTransport;
41class ChannelSocket;
42class TlsSocketEndpoint;
43
44using DeviceId = dht::PkId;
45using OnConnectionRequestCb
46 = std::function<bool(const std::shared_ptr<dht::crypto::Certificate>& /* peer */,
47 const uint16_t& /* id */,
48 const std::string& /* name */)>;
49using OnConnectionReadyCb
50 = std::function<void(const DeviceId& /* deviceId */, const std::shared_ptr<ChannelSocket>&)>;
51using ChannelReadyCb = std::function<void(void)>;
52using OnShutdownCb = std::function<void(void)>;
53
54static constexpr auto SEND_BEACON_TIMEOUT = std::chrono::milliseconds(3000);
55static constexpr uint16_t CONTROL_CHANNEL {0};
56static constexpr uint16_t PROTOCOL_CHANNEL {0xffff};
57
58enum class ChannelRequestState {
59 REQUEST,
60 ACCEPT,
61 DECLINE,
62};
63
64/**
65 * That msgpack structure is used to request a new channel (id, name)
66 * Transmitted over the TLS socket
67 */
68struct ChannelRequest
69{
70 std::string name {};
71 uint16_t channel {0};
72 ChannelRequestState state {ChannelRequestState::REQUEST};
73 MSGPACK_DEFINE(name, channel, state)
74};
75
76/**
77 * A socket divided in channels over a TLS session
78 */
79class MultiplexedSocket : public std::enable_shared_from_this<MultiplexedSocket>
80{
81public:
82 MultiplexedSocket(std::shared_ptr<asio::io_context> ctx, const DeviceId& deviceId, std::unique_ptr<TlsSocketEndpoint> endpoint);
83 ~MultiplexedSocket();
84 std::shared_ptr<ChannelSocket> addChannel(const std::string& name);
85
86 std::shared_ptr<MultiplexedSocket> shared()
87 {
88 return std::static_pointer_cast<MultiplexedSocket>(shared_from_this());
89 }
90 std::shared_ptr<MultiplexedSocket const> shared() const
91 {
92 return std::static_pointer_cast<MultiplexedSocket const>(shared_from_this());
93 }
94 std::weak_ptr<MultiplexedSocket> weak()
95 {
96 return std::static_pointer_cast<MultiplexedSocket>(shared_from_this());
97 }
98 std::weak_ptr<MultiplexedSocket const> weak() const
99 {
100 return std::static_pointer_cast<MultiplexedSocket const>(shared_from_this());
101 }
102
103 DeviceId deviceId() const;
104 bool isReliable() const;
105 bool isInitiator() const;
106 int maxPayload() const;
107
108 /**
109 * Will be triggered when a new channel is ready
110 */
111 void setOnReady(OnConnectionReadyCb&& cb);
112 /**
113 * Will be triggered when the peer asks for a new channel
114 */
115 void setOnRequest(OnConnectionRequestCb&& cb);
116
117 std::size_t write(const uint16_t& channel,
118 const uint8_t* buf,
119 std::size_t len,
120 std::error_code& ec);
121
122 /**
123 * This will close all channels and send a TLS EOF on the main socket.
124 */
125 void shutdown();
126
127 /**
128 * This will wait that eventLoop is stopped and stop it if necessary
129 */
130 void join();
131
132 /**
133 * Will trigger that callback when shutdown() is called
134 */
135 void onShutdown(OnShutdownCb&& cb);
136
137 /**
138 * Get informations from socket (channels opened)
139 */
140 void monitor() const;
141
142 const std::shared_ptr<Logger>& logger();
143
144 /**
145 * Send a beacon on the socket and close if no response come
146 * @param timeout
147 */
148 void sendBeacon(const std::chrono::milliseconds& timeout = SEND_BEACON_TIMEOUT);
149
150 /**
151 * Get peer's certificate
152 */
153 std::shared_ptr<dht::crypto::Certificate> peerCertificate() const;
154
155 IpAddr getLocalAddress() const;
156 IpAddr getRemoteAddress() const;
157
158 void eraseChannel(uint16_t channel);
159
160#ifdef LIBJAMI_TESTABLE
161 /**
162 * Check if we can send beacon on the socket
163 */
164 bool canSendBeacon() const;
165
166 /**
167 * Decide if yes or not we answer to beacon
168 * @param value New value
169 */
170 void answerToBeacon(bool value);
171
172 /**
173 * Change version sent to the peer
174 */
175 void setVersion(int version);
176
177 /**
178 * Set a callback to detect beacon messages
179 */
180 void setOnBeaconCb(const std::function<void(bool)>& cb);
181
182 /**
183 * Set a callback to detect version messages
184 */
185 void setOnVersionCb(const std::function<void(int)>& cb);
186
187 /**
188 * Send the version
189 */
190 void sendVersion();
191#endif
192
193private:
194 class Impl;
195 std::unique_ptr<Impl> pimpl_;
196};
197
198class ChannelSocketInterface : public GenericSocket<uint8_t>
199{
200public:
201 using SocketType = GenericSocket<uint8_t>;
202
203 virtual DeviceId deviceId() const = 0;
204 virtual std::string name() const = 0;
205 virtual uint16_t channel() const = 0;
206 /**
207 * Triggered when a specific channel is ready
208 * Used by ConnectionManager::connectDevice()
209 */
210 virtual void onReady(ChannelReadyCb&& cb) = 0;
211 /**
212 * Will trigger that callback when shutdown() is called
213 */
214 virtual void onShutdown(OnShutdownCb&& cb) = 0;
215
216 virtual void onRecv(std::vector<uint8_t>&& pkt) = 0;
217};
218
219class ChannelSocketTest : public ChannelSocketInterface
220{
221public:
222 ChannelSocketTest(std::shared_ptr<asio::io_context> ctx, const DeviceId& deviceId, const std::string& name, const uint16_t& channel);
223 ~ChannelSocketTest();
224
225 static void link(const std::shared_ptr<ChannelSocketTest>& socket1,
226 const std::shared_ptr<ChannelSocketTest>& socket2);
227
228 DeviceId deviceId() const override;
229 std::string name() const override;
230 uint16_t channel() const override;
231
232 bool isReliable() const override { return true; };
233 bool isInitiator() const override { return true; };
234 int maxPayload() const override { return 0; };
235
236 void shutdown() override;
237
238 std::size_t read(ValueType* buf, std::size_t len, std::error_code& ec) override;
239 std::size_t write(const ValueType* buf, std::size_t len, std::error_code& ec) override;
240 int waitForData(std::chrono::milliseconds timeout, std::error_code&) const override;
241 void setOnRecv(RecvCb&&) override;
242 void onRecv(std::vector<uint8_t>&& pkt) override;
243
244 /**
245 * Triggered when a specific channel is ready
246 * Used by ConnectionManager::connectDevice()
247 */
248 void onReady(ChannelReadyCb&& cb) override;
249 /**
250 * Will trigger that callback when shutdown() is called
251 */
252 void onShutdown(OnShutdownCb&& cb) override;
253
254 std::vector<uint8_t> rx_buf {};
255 mutable std::mutex mutex {};
256 mutable std::condition_variable cv {};
257 GenericSocket<uint8_t>::RecvCb cb {};
258
259private:
260 const DeviceId pimpl_deviceId;
261 const std::string pimpl_name;
262 const uint16_t pimpl_channel;
263 asio::io_context& ioCtx_;
264 std::weak_ptr<ChannelSocketTest> remote;
265 OnShutdownCb shutdownCb_ {[&] {
266 }};
267 std::atomic_bool isShutdown_ {false};
268};
269
270/**
271 * Represents a channel of the multiplexed socket (channel, name)
272 */
273class ChannelSocket : public ChannelSocketInterface
274{
275public:
276 ChannelSocket(std::weak_ptr<MultiplexedSocket> endpoint,
277 const std::string& name,
278 const uint16_t& channel,
279 bool isInitiator = false,
280 std::function<void()> rmFromMxSockCb = {});
281 ~ChannelSocket();
282
283 DeviceId deviceId() const override;
284 std::string name() const override;
285 uint16_t channel() const override;
286 bool isReliable() const override;
287 bool isInitiator() const override;
288 int maxPayload() const override;
289 /**
290 * Like shutdown, but don't send any packet on the socket.
291 * Used by Multiplexed Socket when the TLS endpoint is already shutting down
292 */
293 void stop();
294
295 /**
296 * This will send an empty buffer as a packet (equivalent to EOF)
297 * Will trigger onShutdown's callback
298 */
299 void shutdown() override;
300
301 void ready();
302 /**
303 * Triggered when a specific channel is ready
304 * Used by ConnectionManager::connectDevice()
305 */
306 void onReady(ChannelReadyCb&& cb) override;
307 /**
308 * Will trigger that callback when shutdown() is called
309 */
310 void onShutdown(OnShutdownCb&& cb) override;
311
312 std::size_t read(ValueType* buf, std::size_t len, std::error_code& ec) override;
313 /**
314 * @note len should be < UINT8_MAX, else you will get ec = EMSGSIZE
315 */
316 std::size_t write(const ValueType* buf, std::size_t len, std::error_code& ec) override;
317 int waitForData(std::chrono::milliseconds timeout, std::error_code&) const override;
318
319 /**
320 * set a callback when receiving data
321 * @note: this callback should take a little time and not block
322 * but you can move it in a thread
323 */
324 void setOnRecv(RecvCb&&) override;
325
326 void onRecv(std::vector<uint8_t>&& pkt) override;
327
328 /**
329 * Send a beacon on the socket and close if no response come
330 * @param timeout
331 */
332 void sendBeacon(const std::chrono::milliseconds& timeout = SEND_BEACON_TIMEOUT);
333
334 /**
335 * Get peer's certificate
336 */
337 std::shared_ptr<dht::crypto::Certificate> peerCertificate() const;
338
339#ifdef LIBJAMI_TESTABLE
340 std::shared_ptr<MultiplexedSocket> underlyingSocket() const;
341#endif
342
343 // Note: When a channel is accepted, it can receives data ASAP and when finished will be removed
344 // however, onAccept is it's own thread due to the callbacks. In this case, the channel must be
345 // deleted in the onAccept.
346 void answered();
347 bool isAnswered() const;
348 void removable();
349 bool isRemovable() const;
350
351 IpAddr getLocalAddress() const;
352 IpAddr getRemoteAddress() const;
353
354private:
355 class Impl;
356 std::unique_ptr<Impl> pimpl_;
357};
358
359} // namespace jami
360
361MSGPACK_ADD_ENUM(jami::ChannelRequestState);