diff --git a/tools/common.cpp b/tools/common.cpp index af6207ce1aece00acfab17e7a0542306a930f287..b6550abff7fc0618fce070a8af35ef05f1b61cf8 100644 --- a/tools/common.cpp +++ b/tools/common.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2023 Savoir-faire Linux Inc. + * Copyright (C) 2023 Savoir-faire Linux Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,6 @@ #include "ice_transport.h" #include <opendht/crypto.h> -#include <iostream> #include <string> #include <filesystem> #include <unistd.h> @@ -37,11 +36,10 @@ loadIdentity(const std::filesystem::path& path) std::filesystem::create_directory(path); } try { - for (const auto& path: std::filesystem::directory_iterator(path)) { + for (const auto& path : std::filesystem::directory_iterator(path)) { auto p = path.path(); if (p.extension() == ".pem") { - auto privateKey = std::make_unique<dht::crypto::PrivateKey>( - fileutils::loadFile(p)); + auto privateKey = std::make_unique<dht::crypto::PrivateKey>(fileutils::loadFile(p)); auto certificate = std::make_unique<dht::crypto::Certificate>( fileutils::loadFile(p.replace_extension(".crt"))); return dht::crypto::Identity(std::move(privateKey), std::move(certificate)); @@ -53,18 +51,23 @@ loadIdentity(const std::filesystem::path& path) auto ca = dht::crypto::generateIdentity("ca"); auto id = dht::crypto::generateIdentity("dhtnc", ca); + fmt::print("Generated new identity: {}\n", id.first->getPublicKey().getId()); dht::crypto::saveIdentity(id, path / "id"); return id; } std::unique_ptr<ConnectionManager::Config> connectionManagerConfig(const std::filesystem::path& path, - dht::crypto::Identity identity, - const std::string& bootstrap, - std::shared_ptr<Logger> logger, - tls::CertificateStore& certStore, - std::shared_ptr<asio::io_context> ioContext, - IceTransportFactory& iceFactory) + dht::crypto::Identity identity, + const std::string& bootstrap, + std::shared_ptr<Logger> logger, + std::shared_ptr<tls::CertificateStore> certStore, + std::shared_ptr<asio::io_context> ioContext, + std::shared_ptr<IceTransportFactory> iceFactory, + const std::string& turn_host, + const std::string& turn_user, + const std::string& turn_pass, + const std::string& turn_realm) { std::filesystem::create_directories(path / "certstore"); @@ -81,7 +84,7 @@ connectionManagerConfig(const std::filesystem::path& path, }; dhtContext.certificateStore = [&](const dht::InfoHash& pk_id) { std::vector<std::shared_ptr<dht::crypto::Certificate>> ret; - if (auto cert = certStore.getCertificate(pk_id.toString())) + if (auto cert = certStore->getCertificate(pk_id.toString())) ret.emplace_back(std::move(cert)); return ret; }; @@ -95,15 +98,23 @@ connectionManagerConfig(const std::filesystem::path& path, config->dht = runner; config->id = identity; config->ioContext = ioContext; - config->certStore = &certStore; - config->factory = &iceFactory; + config->certStore = certStore; + config->factory = iceFactory; config->cachePath = path; config->logger = logger; + if (!turn_host.empty()) + config->turnEnabled = true; + config->turnServer = turn_host; + config->turnServerUserName = turn_user; + config->turnServerPwd = turn_pass; + config->turnServerRealm = turn_realm; + return std::move(config); } -template <typename T> -void readFromPipe(std::shared_ptr<ChannelSocket> socket, T input, Buffer buffer) +template<typename T> +void +readFromPipe(std::shared_ptr<ChannelSocket> socket, T input, Buffer buffer) { asio::async_read(*input, asio::buffer(*buffer), @@ -112,26 +123,28 @@ void readFromPipe(std::shared_ptr<ChannelSocket> socket, T input, Buffer buffer) if (!error) { // Process the data received in the buffer std::error_code ec; - // print the data to stdout + // Write the data to the socket socket->write(buffer->data(), bytesRead, ec); if (!ec) { // Continue reading more data readFromPipe(socket, input, buffer); } else { - fmt::print(stderr, "Error writing to socket: {}\n", ec.message()); - // logger->error("Error writing to socket: {}", ec.message()); + fmt::print(stderr, "Error writing to socket: {}\n", ec.message()); } - } else if(error != asio::error::eof) { + } else if (error == asio::error::eof) { + // Connection closed cleanly by peer. + socket->shutdown(); + }else{ fmt::print(stderr, "Error reading from stdin: {}\n", error.message()); - // logger->error("Error reading from stdin: {}", error.message()); } }); } -template void readFromPipe(std::shared_ptr<ChannelSocket> socket, std::shared_ptr<asio::posix::stream_descriptor> input, Buffer buffer); -template void readFromPipe(std::shared_ptr<ChannelSocket> socket, std::shared_ptr<asio::ip::tcp::socket> input, Buffer buffer); - - - +template void readFromPipe(std::shared_ptr<ChannelSocket> socket, + std::shared_ptr<asio::posix::stream_descriptor> input, + Buffer buffer); +template void readFromPipe(std::shared_ptr<ChannelSocket> socket, + std::shared_ptr<asio::ip::tcp::socket> input, + Buffer buffer); } // namespace dhtnet \ No newline at end of file diff --git a/tools/common.h b/tools/common.h index 94d06d262ca3f2a528a044875d4fc865c3066a6e..3e143b5b865b481b69e21c959832358d3c667fbf 100644 --- a/tools/common.h +++ b/tools/common.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2023 Savoir-faire Linux Inc. + * Copyright (C) 2023 Savoir-faire Linux Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ namespace dhtnet { using Buffer = std::shared_ptr<std::vector<uint8_t>>; +constexpr size_t BUFFER_SIZE = 64 * 1024; /** * Attempt to retrieve the identity from the .ssh directory, and if none is found, generate a new @@ -31,16 +32,21 @@ using Buffer = std::shared_ptr<std::vector<uint8_t>>; */ dht::crypto::Identity loadIdentity(const std::filesystem::path& path); // add certstore to the config -std::unique_ptr<ConnectionManager::Config> connectionManagerConfig(const std::filesystem::path& path, - dht::crypto::Identity identity, - const std::string& bootstrap, - std::shared_ptr<Logger> logger, - tls::CertificateStore& certStore, - std::shared_ptr<asio::io_context> ioContext, - IceTransportFactory& iceFactory); +std::unique_ptr<ConnectionManager::Config> connectionManagerConfig( + const std::filesystem::path& path, + dht::crypto::Identity identity, + const std::string& bootstrap, + std::shared_ptr<Logger> logger, + std::shared_ptr<tls::CertificateStore> certStore, + std::shared_ptr<asio::io_context> ioContext, + std::shared_ptr<dhtnet::IceTransportFactory> iceFactory, + const std::string& turn_host ="", + const std::string& turn_user="", + const std::string& turn_pass="", + const std::string& turn_realm=""); // add ioContext to readFromStdin -template <typename T> +template<typename T> void readFromPipe(std::shared_ptr<ChannelSocket> socket, T input, Buffer buffer); } // namespace dhtnet \ No newline at end of file diff --git a/tools/dnc/dnc.cpp b/tools/dnc/dnc.cpp index d059f22d1fdb12f69d4d0f3299b24de94fa5066f..7f174640975ec98ae65e28593e3b27e502a4b647 100644 --- a/tools/dnc/dnc.cpp +++ b/tools/dnc/dnc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2023 Savoir-faire Linux Inc. + * Copyright (C) 2023 Savoir-faire Linux Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,7 +27,6 @@ #include <fcntl.h> #include <unistd.h> -#include <iostream> #include <chrono> #include <string> #include <string_view> @@ -53,16 +52,19 @@ Dnc::parseName(const std::string_view name) return std::make_pair(ip_add, port); } - // Build a server Dnc::Dnc(const std::filesystem::path& path, dht::crypto::Identity identity, - const std::string& bootstrap) + const std::string& bootstrap, + const std::string& turn_host, + const std::string& turn_user, + const std::string& turn_pass, + const std::string& turn_realm) : logger(dht::log::getStdLogger()) - , certStore(path / "certstore", logger) - + // , certStore(std::shared_ptr<tls::CertificateStore>(path / "certstore", logger)) + , ioContext(std::make_shared<asio::io_context>()) { - ioContext = std::make_shared<asio::io_context>(); + auto certStore = std::make_shared<tls::CertificateStore>(path / "certstore", logger); ioContextRunner = std::thread([context = ioContext, logger = logger] { try { auto work = asio::make_work_guard(*context); @@ -73,7 +75,17 @@ Dnc::Dnc(const std::filesystem::path& path, } }); - auto config = connectionManagerConfig(path, identity, bootstrap, logger, certStore, ioContext, iceFactory); + auto config = connectionManagerConfig(path, + identity, + bootstrap, + logger, + certStore, + ioContext, + iceFactory, + turn_host, + turn_user, + turn_pass, + turn_realm); // create a connection manager connectionManager = std::make_unique<ConnectionManager>(std::move(config)); @@ -113,11 +125,13 @@ Dnc::Dnc(const std::filesystem::path& path, // Create a TCP socket auto socket = std::make_shared<asio::ip::tcp::socket>(*ioContext); + socket->open(asio::ip::tcp::v4()); + socket->set_option(asio::socket_base::keep_alive(true)); asio::async_connect( *socket, endpoints, [this, socket, mtlxSocket](const std::error_code& error, - const asio::ip::tcp::endpoint& ep) { + const asio::ip::tcp::endpoint& ep) { if (!error) { if (logger) logger->debug("Connected!"); @@ -137,8 +151,7 @@ Dnc::Dnc(const std::filesystem::path& path, return size; }); // Create a buffer to read data into - auto buffer = std::make_shared<std::vector<uint8_t>>(65536); - + auto buffer = std::make_shared<std::vector<uint8_t>>(BUFFER_SIZE); readFromPipe(mtlxSocket, socket, buffer); } else { if (logger) @@ -158,45 +171,46 @@ Dnc::Dnc(const std::filesystem::path& path, dht::crypto::Identity identity, const std::string& bootstrap, dht::InfoHash peer_id, - const std::string& remote_host, - int remote_port) - : Dnc(path, identity, bootstrap) + const std::string& remote_host, + int remote_port, + const std::string& turn_host, + const std::string& turn_user, + const std::string& turn_pass, + const std::string& turn_realm) + : Dnc(path, identity, bootstrap,turn_host,turn_user,turn_pass, turn_realm) { std::condition_variable cv; auto name = fmt::format("nc://{:s}:{:d}", remote_host, remote_port); - connectionManager->connectDevice(peer_id, - name, - [&](std::shared_ptr<ChannelSocket> socket, - const dht::InfoHash&) { - if (socket) { - socket->setOnRecv( - [this, socket](const uint8_t* data, size_t size) { - std::cout.write((const char*) data, size); - std::cout.flush(); - return size; - }); - // Create a buffer to read data into - auto buffer = std::make_shared<std::vector<uint8_t>>(65536); - - // Create a shared_ptr to the stream_descriptor - auto stdinPipe = std::make_shared<asio::posix::stream_descriptor>(*ioContext, - ::dup(STDIN_FILENO)); - readFromPipe(socket, stdinPipe, buffer); - - socket->onShutdown([this]() { - if (logger) - logger->error("Exit program"); - std::exit(EXIT_FAILURE); - }); - } - }); + connectionManager->connectDevice( + peer_id, name, [&](std::shared_ptr<ChannelSocket> socket, const dht::InfoHash&) { + if (socket) { + socket->setOnRecv([this, socket](const uint8_t* data, size_t size) { + std::cout.write((const char*) data, size); + std::cout.flush(); + return size; + }); + // Create a buffer to read data into + auto buffer = std::make_shared<std::vector<uint8_t>>(BUFFER_SIZE); + + // Create a shared_ptr to the stream_descriptor + auto stdinPipe = std::make_shared<asio::posix::stream_descriptor>(*ioContext, + ::dup( + STDIN_FILENO)); + readFromPipe(socket, stdinPipe, buffer); + + socket->onShutdown([this]() { + if (logger) + logger->debug("Exit program"); + ioContext->stop(); + }); + } + }); - connectionManager->onConnectionReady([&](const DeviceId&, - const std::string& name, - std::shared_ptr<ChannelSocket> mtlxSocket) { - if (logger) - logger->debug("Connected!"); - }); + connectionManager->onConnectionReady( + [&](const DeviceId&, const std::string& name, std::shared_ptr<ChannelSocket> mtlxSocket) { + if (logger) + logger->debug("Connected!"); + }); } void @@ -205,7 +219,6 @@ Dnc::run() ioContext->run(); } - Dnc::~Dnc() { ioContext->stop(); diff --git a/tools/dnc/dnc.h b/tools/dnc/dnc.h index 620f940cb0be53163553cac60ce88524f5ddf80f..b9545c151aff6468df3e1f53f3a7e06d833b46b6 100644 --- a/tools/dnc/dnc.h +++ b/tools/dnc/dnc.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2023 Savoir-faire Linux Inc. + * Copyright (C) 2023 Savoir-faire Linux Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,22 +34,30 @@ public: // Build a server Dnc(const std::filesystem::path& path, dht::crypto::Identity identity, - const std::string& bootstrap); + const std::string& bootstrap, + const std::string& turn_host, + const std::string& turn_user, + const std::string& turn_pass, + const std::string& turn_realm); // Build a client Dnc(const std::filesystem::path& path, dht::crypto::Identity identity, const std::string& bootstrap, dht::InfoHash peer_id, const std::string& remote_host, - int remote_port); + int remote_port, + const std::string& turn_host = "", + const std::string& turn_user = "", + const std::string& turn_pass = "", + const std::string& turn_realm = ""); ~Dnc(); void run(); private: std::unique_ptr<ConnectionManager> connectionManager; std::shared_ptr<Logger> logger; - tls::CertificateStore certStore; - IceTransportFactory iceFactory; + std::shared_ptr<tls::CertificateStore> certStore; + std::shared_ptr<IceTransportFactory> iceFactory; std::shared_ptr<asio::io_context> ioContext; std::thread ioContextRunner; diff --git a/tools/dnc/main.cpp b/tools/dnc/main.cpp index 677477f2c3bb27c2d6c2153a569d1c97c2a0a8ad..b6f4cbea16c028465722195154f127ae76f2fd7b 100644 --- a/tools/dnc/main.cpp +++ b/tools/dnc/main.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2023 Savoir-faire Linux Inc. + * Copyright (C) 2023 Savoir-faire Linux Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,31 +34,36 @@ struct dhtnc_params bool help {false}; bool version {false}; bool listen {false}; - bool verbose {false}; std::filesystem::path path {}; std::string bootstrap {}; std::string remote_host {}; in_port_t remote_port {}; dht::InfoHash peer_id {}; + std::string turn_host {}; + std::string turn_user {}; + std::string turn_pass {}; + std::string turn_realm {}; }; -static const constexpr struct option long_options[] - = {{"help", no_argument, nullptr, 'h'}, - {"version", no_argument, nullptr, 'V'}, - {"verbose", no_argument, nullptr, 'v'}, - {"port", required_argument, nullptr, 'p'}, - {"ip", required_argument, nullptr, 'i'}, - {"listen", no_argument, nullptr, 'l'}, - {"bootstrap", required_argument, nullptr, 'b'}, - {"id_path", required_argument, nullptr, 'I'}, - {nullptr, 0, nullptr, 0}}; +static const constexpr struct option long_options[] = {{"help", no_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'V'}, + {"port", required_argument, nullptr, 'p'}, + {"ip", required_argument, nullptr, 'i'}, + {"listen", no_argument, nullptr, 'l'}, + {"bootstrap", required_argument, nullptr, 'b'}, + {"id_path", required_argument, nullptr, 'I'}, + {"turn_host", required_argument, nullptr, 't'}, + {"turn_user", required_argument, nullptr, 'u'}, + {"turn_pass", required_argument, nullptr, 'w'}, + {"turn_realm", required_argument, nullptr, 'r'}, + {nullptr, 0, nullptr, 0}}; dhtnc_params parse_args(int argc, char** argv) { dhtnc_params params; int opt; - while ((opt = getopt_long(argc, argv, "hvVlI:b:p:i:", long_options, nullptr)) != -1) { + while ((opt = getopt_long(argc, argv, "hVlw:r:u:t:I:b:p:i:", long_options, nullptr)) != -1) { // fmt::print("opt: {} {}\n", opt, optarg); switch (opt) { case 'h': @@ -67,9 +72,6 @@ parse_args(int argc, char** argv) case 'V': params.version = true; break; - case 'v': - params.verbose = true; - break; case 'p': params.remote_port = std::stoi(optarg); break; @@ -85,6 +87,18 @@ parse_args(int argc, char** argv) case 'I': params.path = optarg; break; + case 't': + params.turn_host = optarg; + break; + case 'u': + params.turn_user = optarg; + break; + case 'w': + params.turn_pass = optarg; + break; + case 'r': + params.turn_realm = optarg; + break; default: std::cerr << "Invalid option" << std::endl; exit(EXIT_FAILURE); @@ -111,6 +125,14 @@ parse_args(int argc, char** argv) params.bootstrap = "bootstrap.jami.net"; if (params.path.empty()) params.path = std::filesystem::path(getenv("HOME")) / ".dhtnet"; + if (params.turn_host.empty()) + params.turn_host = "turn.jami.net"; + if (params.turn_user.empty()) + params.turn_user = "ring"; + if (params.turn_pass.empty()) + params.turn_pass = "ring"; + if (params.turn_realm.empty()) + params.turn_realm = "ring"; return params; } @@ -124,8 +146,7 @@ setSipLogLevel() } pj_log_set_level(level); - pj_log_set_log_func([](int level, const char* data, int /*len*/) { - }); + pj_log_set_log_func([](int level, const char* data, int /*len*/) {}); } int @@ -140,14 +161,20 @@ main(int argc, char** argv) std::unique_ptr<dhtnet::Dnc> dhtnc; if (params.listen) { // create dnc instance - dhtnc = std::make_unique<dhtnet::Dnc>(params.path, identity, params.bootstrap); + dhtnc = std::make_unique<dhtnet::Dnc>(params.path, identity, params.bootstrap, params.turn_host, params.turn_user, params.turn_pass, params.turn_realm); } else { dhtnc = std::make_unique<dhtnet::Dnc>(params.path, identity, params.bootstrap, params.peer_id, params.remote_host, - params.remote_port); + params.remote_port, + params.turn_host, + params.turn_user, + params.turn_pass, + params.turn_realm + ); } dhtnc->run(); + return EXIT_SUCCESS; }