blob: dddc7b1a6878f735ac9206dd717f8b231d2e9922 [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
58Dnc::Dnc(dht::crypto::Identity identity,
59 const std::string& bootstrap_ip_add,
60 const std::string& bootstrap_port)
61 : logger(dht::log::getStdLogger())
62 , certStore(std::string(getenv("HOME")) + "/.dhtnetTools/certstore", logger)
63
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
76 auto config = connectionManagerConfig(identity, bootstrap_ip_add, bootstrap_port, logger, certStore, ioContext, iceFactory);
77 // create a connection manager
78 connectionManager = std::make_unique<ConnectionManager>(move(config));
79
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)
94 logger->debug("Channel request received");
95 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);
107 asio::ip::tcp::resolver resolver(*ioContext);
108 asio::ip::tcp::resolver::results_type endpoints = resolver.resolve(parsedName.first,
109 parsedName.second);
110
111 // Create a TCP socket
112 auto socket = std::make_shared<asio::ip::tcp::socket>(*ioContext);
113 asio::async_connect(
114 *socket,
115 endpoints,
116 [this, socket, mtlxSocket](const std::error_code& error,
117 const asio::ip::tcp::endpoint& ep) {
118 if (!error) {
119 if (logger)
120 logger->debug("Connected!");
121 mtlxSocket->setOnRecv([socket, this](const uint8_t* data, size_t size) {
122 auto data_copy = std::make_shared<std::vector<uint8_t>>(data,
123 data + size);
124 asio::async_write(*socket,
125 asio::buffer(*data_copy),
126 [data_copy, this](const std::error_code& error,
127 std::size_t bytesWritten) {
128 if (error) {
129 if (logger)
130 logger->error("Write error: {}",
131 error.message());
132 }
133 });
134 return size;
135 });
136 // Create a buffer to read data into
137 auto buffer = std::make_shared<std::vector<uint8_t>>(65536);
138
139 readFromPipe(mtlxSocket, socket, buffer);
140 } else {
141 if (logger)
142 logger->error("Connection error: {}", error.message());
143 }
144 });
145
146 } catch (std::exception& e) {
147 if (logger)
148 logger->error("Exception: {}", e.what());
149 }
150 });
151}
152// Build a client
153Dnc::Dnc(dht::crypto::Identity identity,
154 const std::string& bootstrap_ip_add,
155 const std::string& bootstrap_port,
156 dht::InfoHash peer_id,
157 int port,
158 const std::string& ip_add)
159 : Dnc(identity, bootstrap_ip_add, bootstrap_port)
160{
161 std::condition_variable cv;
162 auto name = fmt::format("nc://{:s}:{:d}", ip_add, port);
163 connectionManager->connectDevice(peer_id,
164 name,
165 [&](std::shared_ptr<ChannelSocket> socket,
166 const dht::InfoHash&) {
167 if (socket) {
168 socket->setOnRecv(
169 [this, socket](const uint8_t* data, size_t size) {
170 std::cout.write((const char*) data, size);
171 std::cout.flush();
172 return size;
173 });
174 // Create a buffer to read data into
175 auto buffer = std::make_shared<std::vector<uint8_t>>(65536);
176
177 // Create a shared_ptr to the stream_descriptor
178 auto stdinPipe = std::make_shared<asio::posix::stream_descriptor>(*ioContext,
179 ::dup(STDIN_FILENO));
180 readFromPipe(socket, stdinPipe, buffer);
181
182 socket->onShutdown([this]() {
183 if (logger)
184 logger->error("Exit program");
185 std::exit(EXIT_FAILURE);
186 });
187 }
188 });
189
190 connectionManager->onConnectionReady([&](const DeviceId&,
191 const std::string& name,
192 std::shared_ptr<ChannelSocket> mtlxSocket) {
193 if (logger)
194 logger->debug("Connected!");
195 });
196}
197
198void
199Dnc::run()
200{
201 ioContext->run();
202}
203
204
205Dnc::~Dnc()
206{
207 ioContext->stop();
208 ioContextRunner.join();
209}
210} // namespace dhtnet