blob: 37c8b229e0ae21e54464e7101145f8b12f927683 [file] [log] [blame]
Amna38768302023-08-21 11:51:56 -04001/*
Amna2f3539b2023-09-18 13:59:22 -04002 * Copyright (C) 2023 Savoir-faire Linux Inc.
Amna38768302023-08-21 11:51:56 -04003 *
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 "dnc.h"
18#include "certstore.h"
19#include "connectionmanager.h"
20#include "fileutils.h"
21#include "../common.h"
22
23#include <opendht/log.h>
24#include <opendht/crypto.h>
25#include <asio.hpp>
26
27#include <fcntl.h>
28#include <unistd.h>
29
Amna38768302023-08-21 11:51:56 -040030#include <chrono>
31#include <string>
32#include <string_view>
33#include <filesystem>
34#include <memory>
35
36namespace dhtnet {
37std::pair<std::string, std::string>
38Dnc::parseName(const std::string_view name)
39{
40 // Find the position of the first ':' character after "nc//"
41 size_t ip_add_start = name.find("nc//") + 6; // Adding 5 to skip "nc//"
42 size_t colonPos = name.find(':', ip_add_start);
43
44 if (colonPos == std::string::npos) {
45 // Return an empty pair if ':' is not found
46 return std::make_pair("", "");
47 }
48
49 std::string ip_add(name.substr(ip_add_start, colonPos - ip_add_start));
50 std::string port(name.substr(colonPos + 1));
51
52 return std::make_pair(ip_add, port);
53}
54
Amna38768302023-08-21 11:51:56 -040055// Build a server
Adrien Béraudecde63f2023-08-26 18:11:21 -040056Dnc::Dnc(const std::filesystem::path& path,
57 dht::crypto::Identity identity,
Amna2f3539b2023-09-18 13:59:22 -040058 const std::string& bootstrap,
59 const std::string& turn_host,
60 const std::string& turn_user,
61 const std::string& turn_pass,
62 const std::string& turn_realm)
Amna38768302023-08-21 11:51:56 -040063 : logger(dht::log::getStdLogger())
Amnaa5452cf2024-01-22 16:07:24 -050064 , ioContext(std::make_shared<asio::io_context>()),
65 iceFactory(std::make_shared<IceTransportFactory>(logger))
Amna38768302023-08-21 11:51:56 -040066{
Amna2f3539b2023-09-18 13:59:22 -040067 auto certStore = std::make_shared<tls::CertificateStore>(path / "certstore", logger);
Amna38768302023-08-21 11:51:56 -040068 ioContextRunner = std::thread([context = ioContext, logger = logger] {
69 try {
70 auto work = asio::make_work_guard(*context);
71 context->run();
72 } catch (const std::exception& ex) {
73 if (logger)
74 logger->error("Error in ioContextRunner: {}", ex.what());
75 }
76 });
77
Amna2f3539b2023-09-18 13:59:22 -040078 auto config = connectionManagerConfig(path,
79 identity,
80 bootstrap,
81 logger,
82 certStore,
83 ioContext,
84 iceFactory,
85 turn_host,
86 turn_user,
87 turn_pass,
88 turn_realm);
Amna38768302023-08-21 11:51:56 -040089 // create a connection manager
Adrien Béraudc1cac452023-08-22 20:32:36 -040090 connectionManager = std::make_unique<ConnectionManager>(std::move(config));
Amna38768302023-08-21 11:51:56 -040091
92 connectionManager->onDhtConnected(identity.first->getPublicKey());
93 connectionManager->onICERequest([this](const dht::Hash<32>&) { // handle ICE request
94 if (logger)
95 logger->debug("ICE request received");
96 return true;
97 });
98
99 std::mutex mtx;
100 std::unique_lock<std::mutex> lk {mtx};
101
102 connectionManager->onChannelRequest(
103 [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string& name) {
104 // handle channel request
105 if (logger)
Adrien Béraudecde63f2023-08-26 18:11:21 -0400106 logger->debug("Channel request received: {}", name);
Amna38768302023-08-21 11:51:56 -0400107 return true;
108 });
109
110 connectionManager->onConnectionReady([&](const DeviceId&,
111 const std::string& name,
112 std::shared_ptr<ChannelSocket> mtlxSocket) {
113 if (name.empty()) {
114 // Handle the empty input case here
115 return;
116 }
117 try {
118 auto parsedName = parseName(name);
Adrien Béraudecde63f2023-08-26 18:11:21 -0400119 if (logger)
120 logger->debug("Connecting to {}:{}", parsedName.first, parsedName.second);
121
Amna38768302023-08-21 11:51:56 -0400122 asio::ip::tcp::resolver resolver(*ioContext);
123 asio::ip::tcp::resolver::results_type endpoints = resolver.resolve(parsedName.first,
124 parsedName.second);
125
126 // Create a TCP socket
127 auto socket = std::make_shared<asio::ip::tcp::socket>(*ioContext);
Amna2f3539b2023-09-18 13:59:22 -0400128 socket->open(asio::ip::tcp::v4());
129 socket->set_option(asio::socket_base::keep_alive(true));
Amna38768302023-08-21 11:51:56 -0400130 asio::async_connect(
131 *socket,
132 endpoints,
133 [this, socket, mtlxSocket](const std::error_code& error,
Amna2f3539b2023-09-18 13:59:22 -0400134 const asio::ip::tcp::endpoint& ep) {
Amna38768302023-08-21 11:51:56 -0400135 if (!error) {
136 if (logger)
137 logger->debug("Connected!");
138 mtlxSocket->setOnRecv([socket, this](const uint8_t* data, size_t size) {
139 auto data_copy = std::make_shared<std::vector<uint8_t>>(data,
140 data + size);
141 asio::async_write(*socket,
142 asio::buffer(*data_copy),
143 [data_copy, this](const std::error_code& error,
144 std::size_t bytesWritten) {
145 if (error) {
146 if (logger)
147 logger->error("Write error: {}",
148 error.message());
149 }
150 });
151 return size;
152 });
153 // Create a buffer to read data into
Amna2f3539b2023-09-18 13:59:22 -0400154 auto buffer = std::make_shared<std::vector<uint8_t>>(BUFFER_SIZE);
Amna38768302023-08-21 11:51:56 -0400155 readFromPipe(mtlxSocket, socket, buffer);
156 } else {
157 if (logger)
158 logger->error("Connection error: {}", error.message());
Adrien Béraudecde63f2023-08-26 18:11:21 -0400159 mtlxSocket->shutdown();
Amna38768302023-08-21 11:51:56 -0400160 }
161 });
162
163 } catch (std::exception& e) {
164 if (logger)
165 logger->error("Exception: {}", e.what());
166 }
167 });
168}
169// Build a client
Adrien Béraudecde63f2023-08-26 18:11:21 -0400170Dnc::Dnc(const std::filesystem::path& path,
171 dht::crypto::Identity identity,
172 const std::string& bootstrap,
Amna38768302023-08-21 11:51:56 -0400173 dht::InfoHash peer_id,
Amna2f3539b2023-09-18 13:59:22 -0400174 const std::string& remote_host,
175 int remote_port,
176 const std::string& turn_host,
177 const std::string& turn_user,
178 const std::string& turn_pass,
179 const std::string& turn_realm)
180 : Dnc(path, identity, bootstrap,turn_host,turn_user,turn_pass, turn_realm)
Amna38768302023-08-21 11:51:56 -0400181{
182 std::condition_variable cv;
Adrien Béraudecde63f2023-08-26 18:11:21 -0400183 auto name = fmt::format("nc://{:s}:{:d}", remote_host, remote_port);
Amna2f3539b2023-09-18 13:59:22 -0400184 connectionManager->connectDevice(
185 peer_id, name, [&](std::shared_ptr<ChannelSocket> socket, const dht::InfoHash&) {
186 if (socket) {
187 socket->setOnRecv([this, socket](const uint8_t* data, size_t size) {
188 std::cout.write((const char*) data, size);
189 std::cout.flush();
190 return size;
191 });
192 // Create a buffer to read data into
193 auto buffer = std::make_shared<std::vector<uint8_t>>(BUFFER_SIZE);
Amna38768302023-08-21 11:51:56 -0400194
Amna2f3539b2023-09-18 13:59:22 -0400195 // Create a shared_ptr to the stream_descriptor
196 auto stdinPipe = std::make_shared<asio::posix::stream_descriptor>(*ioContext,
197 ::dup(
198 STDIN_FILENO));
199 readFromPipe(socket, stdinPipe, buffer);
Amna38768302023-08-21 11:51:56 -0400200
Amna2f3539b2023-09-18 13:59:22 -0400201 socket->onShutdown([this]() {
202 if (logger)
203 logger->debug("Exit program");
204 ioContext->stop();
205 });
206 }
207 });
Amna38768302023-08-21 11:51:56 -0400208
Amna2f3539b2023-09-18 13:59:22 -0400209 connectionManager->onConnectionReady(
210 [&](const DeviceId&, const std::string& name, std::shared_ptr<ChannelSocket> mtlxSocket) {
211 if (logger)
212 logger->debug("Connected!");
213 });
Amna38768302023-08-21 11:51:56 -0400214}
215
216void
217Dnc::run()
218{
219 ioContext->run();
220}
221
Amna38768302023-08-21 11:51:56 -0400222Dnc::~Dnc()
223{
224 ioContext->stop();
225 ioContextRunner.join();
226}
227} // namespace dhtnet