tool/dnc: update dnc

Change-Id: I3c790221ed78af80f88a2bb54bf2319b27235be6
diff --git a/tools/common.cpp b/tools/common.cpp
index af6207c..b6550ab 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 @@
         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 @@
 
     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 @@
     };
     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 @@
     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 @@
                          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 94d06d2..3e143b5 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 @@
  */
 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 d059f22..7f17464 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 @@
     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 @@
         }
     });
 
-    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 @@
 
             // 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 @@
                             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 @@
          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);
+    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);
+                // 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);
-                                            });
-                                         }
-                                     });
+                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 @@
     ioContext->run();
 }
 
-
 Dnc::~Dnc()
 {
     ioContext->stop();
diff --git a/tools/dnc/dnc.h b/tools/dnc/dnc.h
index 620f940..b9545c1 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 @@
     // 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 677477f..b6f4cbe 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 @@
     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 @@
         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 @@
         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 @@
         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 @@
     }
 
     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 @@
     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;
 }