blob: e57d1d420e4751117b3e858d6e96b97ccf7c6d25 [file] [log] [blame]
Adrien Béraud612b55b2023-05-29 10:42:04 -04001/*
2 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
3 *
Adrien Béraudcb753622023-07-17 22:32:49 -04004 * This program is free software: you can redistribute it and/or modify
Adrien Béraud612b55b2023-05-29 10:42:04 -04005 * it under the terms of the GNU General Public License as published by
Adrien Béraudcb753622023-07-17 22:32:49 -04006 * the Free Software Foundation, either version 3 of the License, or
Adrien Béraud612b55b2023-05-29 10:42:04 -04007 * (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
Adrien Béraudcb753622023-07-17 22:32:49 -040011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Adrien Béraud612b55b2023-05-29 10:42:04 -040012 * 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 {
Adrien Béraud9132a812023-07-21 11:20:40 -040033struct Logger;
Adrien Béraud612b55b2023-05-29 10:42:04 -040034}
35}
36
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040037namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040038
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>&)>;
Adrien Béraudc5b971d2023-06-13 19:41:25 -040051using ChannelReadyCb = std::function<void(bool)>;
Adrien Béraud612b55b2023-05-29 10:42:04 -040052using 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:
Adrien Béraud5636f7c2023-09-14 14:34:57 -040082 MultiplexedSocket(std::shared_ptr<asio::io_context> ctx, const DeviceId& deviceId, std::unique_ptr<TlsSocketEndpoint> endpoint, std::shared_ptr<dht::log::Logger> logger = {});
Adrien Béraud612b55b2023-05-29 10:42:04 -040083 ~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();
Adrien Béraud1aaaa962024-09-26 12:41:58 -0400126 bool isRunning() const;
Adrien Béraud612b55b2023-05-29 10:42:04 -0400127
128 /**
129 * This will wait that eventLoop is stopped and stop it if necessary
130 */
131 void join();
132
133 /**
134 * Will trigger that callback when shutdown() is called
135 */
136 void onShutdown(OnShutdownCb&& cb);
137
138 /**
ovari123a15c6882024-09-17 18:34:20 -0400139 * Get information from socket (channels opened)
Adrien Béraud612b55b2023-05-29 10:42:04 -0400140 */
141 void monitor() const;
142
143 const std::shared_ptr<Logger>& logger();
144
145 /**
Amna31791e52023-08-03 12:40:57 -0400146 * Get the list of channels
147 */
148 std::vector<std::map<std::string, std::string>> getChannelList() const;
149
150 /**
Adrien Béraud612b55b2023-05-29 10:42:04 -0400151 * Send a beacon on the socket and close if no response come
152 * @param timeout
153 */
154 void sendBeacon(const std::chrono::milliseconds& timeout = SEND_BEACON_TIMEOUT);
155
156 /**
157 * Get peer's certificate
158 */
159 std::shared_ptr<dht::crypto::Certificate> peerCertificate() const;
160
161 IpAddr getLocalAddress() const;
162 IpAddr getRemoteAddress() const;
163
164 void eraseChannel(uint16_t channel);
165
Adrien Béraudafa8e282023-09-24 12:53:20 -0400166 TlsSocketEndpoint* endpoint();
167
Adrien Béraud6b6a5d32023-08-15 15:53:33 -0400168#ifdef DHTNET_TESTABLE
Adrien Béraud612b55b2023-05-29 10:42:04 -0400169 /**
170 * Check if we can send beacon on the socket
171 */
172 bool canSendBeacon() const;
173
174 /**
175 * Decide if yes or not we answer to beacon
176 * @param value New value
177 */
178 void answerToBeacon(bool value);
179
180 /**
181 * Change version sent to the peer
182 */
183 void setVersion(int version);
184
185 /**
186 * Set a callback to detect beacon messages
187 */
188 void setOnBeaconCb(const std::function<void(bool)>& cb);
189
190 /**
191 * Set a callback to detect version messages
192 */
193 void setOnVersionCb(const std::function<void(int)>& cb);
194
195 /**
196 * Send the version
197 */
198 void sendVersion();
199#endif
200
201private:
202 class Impl;
203 std::unique_ptr<Impl> pimpl_;
204};
205
206class ChannelSocketInterface : public GenericSocket<uint8_t>
207{
208public:
209 using SocketType = GenericSocket<uint8_t>;
210
211 virtual DeviceId deviceId() const = 0;
212 virtual std::string name() const = 0;
213 virtual uint16_t channel() const = 0;
214 /**
215 * Triggered when a specific channel is ready
216 * Used by ConnectionManager::connectDevice()
217 */
218 virtual void onReady(ChannelReadyCb&& cb) = 0;
219 /**
220 * Will trigger that callback when shutdown() is called
221 */
222 virtual void onShutdown(OnShutdownCb&& cb) = 0;
223
224 virtual void onRecv(std::vector<uint8_t>&& pkt) = 0;
225};
226
227class ChannelSocketTest : public ChannelSocketInterface
228{
229public:
230 ChannelSocketTest(std::shared_ptr<asio::io_context> ctx, const DeviceId& deviceId, const std::string& name, const uint16_t& channel);
231 ~ChannelSocketTest();
232
233 static void link(const std::shared_ptr<ChannelSocketTest>& socket1,
234 const std::shared_ptr<ChannelSocketTest>& socket2);
235
236 DeviceId deviceId() const override;
237 std::string name() const override;
238 uint16_t channel() const override;
239
240 bool isReliable() const override { return true; };
241 bool isInitiator() const override { return true; };
242 int maxPayload() const override { return 0; };
243
244 void shutdown() override;
245
246 std::size_t read(ValueType* buf, std::size_t len, std::error_code& ec) override;
247 std::size_t write(const ValueType* buf, std::size_t len, std::error_code& ec) override;
248 int waitForData(std::chrono::milliseconds timeout, std::error_code&) const override;
249 void setOnRecv(RecvCb&&) override;
250 void onRecv(std::vector<uint8_t>&& pkt) override;
251
252 /**
253 * Triggered when a specific channel is ready
254 * Used by ConnectionManager::connectDevice()
255 */
256 void onReady(ChannelReadyCb&& cb) override;
257 /**
258 * Will trigger that callback when shutdown() is called
259 */
260 void onShutdown(OnShutdownCb&& cb) override;
261
262 std::vector<uint8_t> rx_buf {};
263 mutable std::mutex mutex {};
264 mutable std::condition_variable cv {};
265 GenericSocket<uint8_t>::RecvCb cb {};
266
267private:
268 const DeviceId pimpl_deviceId;
269 const std::string pimpl_name;
270 const uint16_t pimpl_channel;
271 asio::io_context& ioCtx_;
272 std::weak_ptr<ChannelSocketTest> remote;
273 OnShutdownCb shutdownCb_ {[&] {
274 }};
275 std::atomic_bool isShutdown_ {false};
276};
277
278/**
279 * Represents a channel of the multiplexed socket (channel, name)
280 */
281class ChannelSocket : public ChannelSocketInterface
282{
283public:
284 ChannelSocket(std::weak_ptr<MultiplexedSocket> endpoint,
285 const std::string& name,
286 const uint16_t& channel,
287 bool isInitiator = false,
288 std::function<void()> rmFromMxSockCb = {});
289 ~ChannelSocket();
290
291 DeviceId deviceId() const override;
292 std::string name() const override;
293 uint16_t channel() const override;
294 bool isReliable() const override;
295 bool isInitiator() const override;
296 int maxPayload() const override;
297 /**
298 * Like shutdown, but don't send any packet on the socket.
299 * Used by Multiplexed Socket when the TLS endpoint is already shutting down
300 */
301 void stop();
302
303 /**
304 * This will send an empty buffer as a packet (equivalent to EOF)
305 * Will trigger onShutdown's callback
306 */
307 void shutdown() override;
308
Adrien Béraudc5b971d2023-06-13 19:41:25 -0400309 void ready(bool accepted);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400310 /**
311 * Triggered when a specific channel is ready
312 * Used by ConnectionManager::connectDevice()
313 */
314 void onReady(ChannelReadyCb&& cb) override;
315 /**
316 * Will trigger that callback when shutdown() is called
317 */
318 void onShutdown(OnShutdownCb&& cb) override;
319
320 std::size_t read(ValueType* buf, std::size_t len, std::error_code& ec) override;
321 /**
322 * @note len should be < UINT8_MAX, else you will get ec = EMSGSIZE
323 */
324 std::size_t write(const ValueType* buf, std::size_t len, std::error_code& ec) override;
325 int waitForData(std::chrono::milliseconds timeout, std::error_code&) const override;
326
327 /**
328 * set a callback when receiving data
329 * @note: this callback should take a little time and not block
330 * but you can move it in a thread
331 */
332 void setOnRecv(RecvCb&&) override;
333
334 void onRecv(std::vector<uint8_t>&& pkt) override;
335
336 /**
337 * Send a beacon on the socket and close if no response come
338 * @param timeout
339 */
340 void sendBeacon(const std::chrono::milliseconds& timeout = SEND_BEACON_TIMEOUT);
341
342 /**
343 * Get peer's certificate
344 */
345 std::shared_ptr<dht::crypto::Certificate> peerCertificate() const;
346
Adrien Béraud6b6a5d32023-08-15 15:53:33 -0400347#ifdef DHTNET_TESTABLE
Adrien Béraud612b55b2023-05-29 10:42:04 -0400348 std::shared_ptr<MultiplexedSocket> underlyingSocket() const;
349#endif
350
351 // Note: When a channel is accepted, it can receives data ASAP and when finished will be removed
352 // however, onAccept is it's own thread due to the callbacks. In this case, the channel must be
353 // deleted in the onAccept.
354 void answered();
355 bool isAnswered() const;
356 void removable();
357 bool isRemovable() const;
358
359 IpAddr getLocalAddress() const;
360 IpAddr getRemoteAddress() const;
361
362private:
363 class Impl;
364 std::unique_ptr<Impl> pimpl_;
365};
366
Sébastien Blin464bdff2023-07-19 08:02:53 -0400367} // namespace dhtnet
Adrien Béraud612b55b2023-05-29 10:42:04 -0400368
Adrien Béraud1ae60aa2023-07-07 09:55:09 -0400369MSGPACK_ADD_ENUM(dhtnet::ChannelRequestState);