blob: 5dcfb0b9e5eb4082d7ebe14b9b573bcd2127b4a2 [file] [log] [blame]
Amna4a70f5c2023-09-14 17:32:05 -04001/*
2 * Copyright (C) 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#include "dsh.h"
18#include "../common.h"
19#include <opendht/log.h>
20#include <opendht/crypto.h>
21
22#include <asio/io_context.hpp>
23#include <sys/types.h>
24#include <sys/wait.h>
25namespace dhtnet {
26
27const int READ_END = 0;
28const int WRITE_END = 1;
29
30void
Adrien Béraud651a4c62023-09-20 14:17:08 -040031create_pipe(int apipe[2])
Amna4a70f5c2023-09-14 17:32:05 -040032{
Adrien Béraud651a4c62023-09-20 14:17:08 -040033#ifdef __APPLE__
34 if (pipe(apipe) < 0)
35 perror("pipe");
36
37 if (fcntl(apipe[0], F_SETFD, FD_CLOEXEC) < 0)
38 perror("unable to set pipe FD_CLOEXEC");
39
40 if (fcntl(apipe[1], F_SETFD, FD_CLOEXEC) < 0)
41 perror("unable to set pipe FD_CLOEXEC");
42#else
43 if (pipe2(apipe, O_CLOEXEC) == -1) {
Amna4a70f5c2023-09-14 17:32:05 -040044 perror("pipe2");
45 exit(EXIT_FAILURE);
46 }
Adrien Béraud651a4c62023-09-20 14:17:08 -040047#endif
Amna4a70f5c2023-09-14 17:32:05 -040048}
Adrien Béraud651a4c62023-09-20 14:17:08 -040049
Amna4a70f5c2023-09-14 17:32:05 -040050void
51child_proc(const int in_pipe[2],
52 const int out_pipe[2],
53 const int error_pipe[2],
54 const std::string& name)
55{
56 // close unused write end of input pipe and read end of output pipe
57 close(in_pipe[WRITE_END]);
58 close(out_pipe[READ_END]);
59 close(error_pipe[READ_END]);
60
61 // replace stdin with input pipe
62 if (dup2(in_pipe[READ_END], STDIN_FILENO) == -1) {
63 perror("dup2: error replacing stdin");
64 exit(EXIT_FAILURE);
65 }
66
67 // replace stdout with output pipe
68 if (dup2(out_pipe[WRITE_END], STDOUT_FILENO) == -1) {
69 perror("dup2: error replacing stdout");
70 exit(EXIT_FAILURE);
71 }
72 // replace stderr with error pipe
73 if (dup2(error_pipe[WRITE_END], STDERR_FILENO) == -1) {
74 perror("dup2: error replacing stderr");
75 exit(EXIT_FAILURE);
76 }
77
78 // prepare arguments
79 const char* args[] = {name.c_str(), NULL};
80 // execute subprocess
81 execvp(args[0], const_cast<char* const*>(args));
82
83 // if execv returns, an error occurred
84 perror("execvp");
85 exit(EXIT_FAILURE);
86}
87
Amnac75ffe92024-02-08 17:23:29 -050088dhtnet::Dsh::Dsh(dht::crypto::Identity identity,
Amna2b5b07f2024-01-22 17:04:36 -050089 const std::string& bootstrap,
90 const std::string& turn_host,
91 const std::string& turn_user,
92 const std::string& turn_pass,
Amna4325f0f2024-01-22 16:11:00 -050093 const std::string& turn_realm,
94 bool anonymous)
Amna5170f762024-08-16 11:49:56 -040095 :logger(dht::log::getStdLogger()),
96 ioContext(std::make_shared<asio::io_context>()),
Amna4325f0f2024-01-22 16:11:00 -050097 iceFactory(std::make_shared<IceTransportFactory>(logger)),
Amna0e5f0762024-05-06 15:40:14 -040098 certStore(std::make_shared<tls::CertificateStore>(cachePath()/"certstore", logger)),
Amna4325f0f2024-01-22 16:11:00 -050099 trustStore(std::make_shared<tls::TrustStore>(*certStore))
Amna4a70f5c2023-09-14 17:32:05 -0400100{
Amna4325f0f2024-01-22 16:11:00 -0500101 auto ca = identity.second->issuer;
102 trustStore->setCertificateStatus(ca->getId().toString(), tls::TrustStore::PermissionStatus::ALLOWED);
Amna4a70f5c2023-09-14 17:32:05 -0400103 // Build a server
Amnac75ffe92024-02-08 17:23:29 -0500104 auto config = connectionManagerConfig(identity,
Amna4a70f5c2023-09-14 17:32:05 -0400105 bootstrap,
106 logger,
107 certStore,
108 ioContext,
Amnaa5452cf2024-01-22 16:07:24 -0500109 iceFactory);
Amna4a70f5c2023-09-14 17:32:05 -0400110 // create a connection manager
111 connectionManager = std::make_unique<ConnectionManager>(std::move(config));
112
113 connectionManager->onDhtConnected(identity.first->getPublicKey());
Amna4325f0f2024-01-22 16:11:00 -0500114 connectionManager->onICERequest([this,identity,anonymous](const DeviceId& deviceId ) { // handle ICE request
115 return trustStore->isAllowed(*certStore->getCertificate(deviceId.toString()), anonymous);
Amna4a70f5c2023-09-14 17:32:05 -0400116 });
117
118 std::mutex mtx;
Adrien Béraud024c46f2024-03-02 23:53:18 -0500119 std::unique_lock lk {mtx};
Amna4a70f5c2023-09-14 17:32:05 -0400120
121 connectionManager->onChannelRequest(
122 [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string& name) {
123 // handle channel request
124 if (logger)
125 logger->debug("Channel request received");
126 return true;
127 });
128
129 connectionManager->onConnectionReady([&](const DeviceId&,
130 const std::string& name,
131 std::shared_ptr<ChannelSocket> socket) {
132 // handle connection ready
133 try {
134 // Create a pipe for communication with the subprocess
135 // create pipes
136 int in_pipe[2], out_pipe[2], error_pipe[2];
137 create_pipe(in_pipe);
138 create_pipe(out_pipe);
139 create_pipe(error_pipe);
140
141 ioContext->notify_fork(asio::io_context::fork_prepare);
142
143 // Fork to create a child process
144 pid_t pid = fork();
145 if (pid == -1) {
146 perror("fork");
147 return EXIT_FAILURE;
148 } else if (pid == 0) { // Child process
149 ioContext->notify_fork(asio::io_context::fork_child);
150 child_proc(in_pipe, out_pipe, error_pipe, name);
151 return EXIT_SUCCESS; // never reached
152 } else {
153 ioContext->notify_fork(asio::io_context::fork_parent);
154
155 // close unused read end of input pipe and write end of output pipe
156 close(in_pipe[READ_END]);
157 close(out_pipe[WRITE_END]);
158 close(error_pipe[WRITE_END]);
159
160 asio::io_context& ioContextRef = *ioContext;
161 // create stream descriptors
162 auto inStream
163 = std::make_shared<asio::posix::stream_descriptor>(ioContextRef.get_executor(),
164 in_pipe[WRITE_END]);
165 auto outStream
166 = std::make_shared<asio::posix::stream_descriptor>(ioContextRef.get_executor(),
167 out_pipe[READ_END]);
168 auto errorStream
169 = std::make_shared<asio::posix::stream_descriptor>(ioContextRef.get_executor(),
170 error_pipe[READ_END]);
171
172 if (socket) {
173 socket->setOnRecv([this, socket, inStream](const uint8_t* data, size_t size) {
174 auto data_copy = std::make_shared<std::vector<uint8_t>>(data, data + size);
175 // write on pipe to sub child
176 std::error_code ec;
177 asio::async_write(*inStream,
178 asio::buffer(*data_copy),
179 [data_copy, this](const std::error_code& error,
180 std::size_t bytesWritten) {
181 if (error) {
182 if (logger)
183 logger->error("Write error: {}",
184 error.message());
185 }
186 });
187 return size;
188 });
189
190 // read from pipe to sub child
191
192 // Create a buffer to read data into
193 auto buffer = std::make_shared<std::vector<uint8_t>>(BUFFER_SIZE);
194
195 // Create a shared_ptr to the stream_descriptor
196 readFromPipe(socket, outStream, buffer);
197 readFromPipe(socket, errorStream, buffer);
198
199 return EXIT_SUCCESS;
200 }
201 }
202
203 } catch (const std::exception& e) {
204 if (logger)
205 logger->error("Error: {}", e.what());
206 }
207 return 0;
208 });
209}
210
Amnac75ffe92024-02-08 17:23:29 -0500211dhtnet::Dsh::Dsh(dht::crypto::Identity identity,
Amna4a70f5c2023-09-14 17:32:05 -0400212 const std::string& bootstrap,
213 dht::InfoHash peer_id,
Amna2b5b07f2024-01-22 17:04:36 -0500214 const std::string& binary,
215 const std::string& turn_host,
216 const std::string& turn_user,
217 const std::string& turn_pass,
218 const std::string& turn_realm)
Amnac75ffe92024-02-08 17:23:29 -0500219 : Dsh(identity, bootstrap, turn_host, turn_user, turn_pass, turn_realm, false)
Amna4a70f5c2023-09-14 17:32:05 -0400220{
221 // Build a client
222 std::condition_variable cv;
223 connectionManager->connectDevice(
224 peer_id, binary, [&](std::shared_ptr<ChannelSocket> socket, const dht::InfoHash&) {
225 if (socket) {
226 socket->setOnRecv([this, socket](const uint8_t* data, size_t size) {
227 std::cout.write((const char*) data, size);
228 std::cout.flush();
229 return size;
230 });
231 // Create a buffer to read data into
232 auto buffer = std::make_shared<std::vector<uint8_t>>(BUFFER_SIZE);
233
234 // Create a shared_ptr to the stream_descriptor
235 auto stdinPipe = std::make_shared<asio::posix::stream_descriptor>(*ioContext,
236 ::dup(
237 STDIN_FILENO));
238 readFromPipe(socket, stdinPipe, buffer);
239
240 socket->onShutdown([this]() {
241 if (logger)
242 logger->debug("Exit program");
243 ioContext->stop();
244 });
245 }
246 });
247
248 connectionManager->onConnectionReady([&](const DeviceId&,
249 const std::string& name,
250 std::shared_ptr<ChannelSocket> socket_received) {
251 if (logger)
252 logger->debug("Connected!");
253 });
254}
255
256void
257dhtnet::Dsh::run()
258{
Amna14d54f22024-08-29 16:17:38 -0400259 auto work = asio::make_work_guard(*ioContext);
Amna4a70f5c2023-09-14 17:32:05 -0400260 ioContext->run();
261}
262
263dhtnet::Dsh::~Dsh()
264{
265 ioContext->stop();
Amna4a70f5c2023-09-14 17:32:05 -0400266}
267
268} // namespace dhtnet