blob: d059f22d1fdb12f69d4d0f3299b24de94fa5066f [file] [log] [blame]
Amna38768302023-08-21 11:51:56 -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#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
30#include <iostream>
31#include <chrono>
32#include <string>
33#include <string_view>
34#include <filesystem>
35#include <memory>
36
37namespace dhtnet {
38std::pair<std::string, std::string>
39Dnc::parseName(const std::string_view name)
40{
41 // Find the position of the first ':' character after "nc//"
42 size_t ip_add_start = name.find("nc//") + 6; // Adding 5 to skip "nc//"
43 size_t colonPos = name.find(':', ip_add_start);
44
45 if (colonPos == std::string::npos) {
46 // Return an empty pair if ':' is not found
47 return std::make_pair("", "");
48 }
49
50 std::string ip_add(name.substr(ip_add_start, colonPos - ip_add_start));
51 std::string port(name.substr(colonPos + 1));
52
53 return std::make_pair(ip_add, port);
54}
55
56
57// Build a server
Adrien Béraudecde63f2023-08-26 18:11:21 -040058Dnc::Dnc(const std::filesystem::path& path,
59 dht::crypto::Identity identity,
60 const std::string& bootstrap)
Amna38768302023-08-21 11:51:56 -040061 : logger(dht::log::getStdLogger())
Adrien Béraudecde63f2023-08-26 18:11:21 -040062 , certStore(path / "certstore", logger)
Amna38768302023-08-21 11:51:56 -040063
64{
65 ioContext = std::make_shared<asio::io_context>();
66 ioContextRunner = std::thread([context = ioContext, logger = logger] {
67 try {
68 auto work = asio::make_work_guard(*context);
69 context->run();
70 } catch (const std::exception& ex) {
71 if (logger)
72 logger->error("Error in ioContextRunner: {}", ex.what());
73 }
74 });
75
Adrien Béraudecde63f2023-08-26 18:11:21 -040076 auto config = connectionManagerConfig(path, identity, bootstrap, logger, certStore, ioContext, iceFactory);
Amna38768302023-08-21 11:51:56 -040077 // create a connection manager
Adrien Béraudc1cac452023-08-22 20:32:36 -040078 connectionManager = std::make_unique<ConnectionManager>(std::move(config));
Amna38768302023-08-21 11:51:56 -040079
80 connectionManager->onDhtConnected(identity.first->getPublicKey());
81 connectionManager->onICERequest([this](const dht::Hash<32>&) { // handle ICE request
82 if (logger)
83 logger->debug("ICE request received");
84 return true;
85 });
86
87 std::mutex mtx;
88 std::unique_lock<std::mutex> lk {mtx};
89
90 connectionManager->onChannelRequest(
91 [&](const std::shared_ptr<dht::crypto::Certificate>&, const std::string& name) {
92 // handle channel request
93 if (logger)
Adrien Béraudecde63f2023-08-26 18:11:21 -040094 logger->debug("Channel request received: {}", name);
Amna38768302023-08-21 11:51:56 -040095 return true;
96 });
97
98 connectionManager->onConnectionReady([&](const DeviceId&,
99 const std::string& name,
100 std::shared_ptr<ChannelSocket> mtlxSocket) {
101 if (name.empty()) {
102 // Handle the empty input case here
103 return;
104 }
105 try {
106 auto parsedName = parseName(name);
Adrien Béraudecde63f2023-08-26 18:11:21 -0400107 if (logger)
108 logger->debug("Connecting to {}:{}", parsedName.first, parsedName.second);
109
Amna38768302023-08-21 11:51:56 -0400110 asio::ip::tcp::resolver resolver(*ioContext);
111 asio::ip::tcp::resolver::results_type endpoints = resolver.resolve(parsedName.first,
112 parsedName.second);
113
114 // Create a TCP socket
115 auto socket = std::make_shared<asio::ip::tcp::socket>(*ioContext);
116 asio::async_connect(
117 *socket,
118 endpoints,
119 [this, socket, mtlxSocket](const std::error_code& error,
120 const asio::ip::tcp::endpoint& ep) {
121 if (!error) {
122 if (logger)
123 logger->debug("Connected!");
124 mtlxSocket->setOnRecv([socket, this](const uint8_t* data, size_t size) {
125 auto data_copy = std::make_shared<std::vector<uint8_t>>(data,
126 data + size);
127 asio::async_write(*socket,
128 asio::buffer(*data_copy),
129 [data_copy, this](const std::error_code& error,
130 std::size_t bytesWritten) {
131 if (error) {
132 if (logger)
133 logger->error("Write error: {}",
134 error.message());
135 }
136 });
137 return size;
138 });
139 // Create a buffer to read data into
140 auto buffer = std::make_shared<std::vector<uint8_t>>(65536);
141
142 readFromPipe(mtlxSocket, socket, buffer);
143 } else {
144 if (logger)
145 logger->error("Connection error: {}", error.message());
Adrien Béraudecde63f2023-08-26 18:11:21 -0400146 mtlxSocket->shutdown();
Amna38768302023-08-21 11:51:56 -0400147 }
148 });
149
150 } catch (std::exception& e) {
151 if (logger)
152 logger->error("Exception: {}", e.what());
153 }
154 });
155}
156// Build a client
Adrien Béraudecde63f2023-08-26 18:11:21 -0400157Dnc::Dnc(const std::filesystem::path& path,
158 dht::crypto::Identity identity,
159 const std::string& bootstrap,
Amna38768302023-08-21 11:51:56 -0400160 dht::InfoHash peer_id,
Adrien Béraudecde63f2023-08-26 18:11:21 -0400161 const std::string& remote_host,
162 int remote_port)
163 : Dnc(path, identity, bootstrap)
Amna38768302023-08-21 11:51:56 -0400164{
165 std::condition_variable cv;
Adrien Béraudecde63f2023-08-26 18:11:21 -0400166 auto name = fmt::format("nc://{:s}:{:d}", remote_host, remote_port);
Amna38768302023-08-21 11:51:56 -0400167 connectionManager->connectDevice(peer_id,
168 name,
169 [&](std::shared_ptr<ChannelSocket> socket,
170 const dht::InfoHash&) {
171 if (socket) {
172 socket->setOnRecv(
173 [this, socket](const uint8_t* data, size_t size) {
174 std::cout.write((const char*) data, size);
175 std::cout.flush();
176 return size;
177 });
178 // Create a buffer to read data into
179 auto buffer = std::make_shared<std::vector<uint8_t>>(65536);
180
181 // Create a shared_ptr to the stream_descriptor
182 auto stdinPipe = std::make_shared<asio::posix::stream_descriptor>(*ioContext,
183 ::dup(STDIN_FILENO));
184 readFromPipe(socket, stdinPipe, buffer);
185
186 socket->onShutdown([this]() {
187 if (logger)
188 logger->error("Exit program");
189 std::exit(EXIT_FAILURE);
190 });
191 }
192 });
193
194 connectionManager->onConnectionReady([&](const DeviceId&,
195 const std::string& name,
196 std::shared_ptr<ChannelSocket> mtlxSocket) {
197 if (logger)
198 logger->debug("Connected!");
199 });
200}
201
202void
203Dnc::run()
204{
205 ioContext->run();
206}
207
208
209Dnc::~Dnc()
210{
211 ioContext->stop();
212 ioContextRunner.join();
213}
214} // namespace dhtnet