Amna | e61e156 | 2024-06-25 09:50:29 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2024 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 | |
| 18 | #include "connectionmanager.h" |
| 19 | #include "multiplexed_socket.h" |
| 20 | #include "certstore.h" |
| 21 | #include "string_utils.h" |
| 22 | |
| 23 | #include <opendht/log.h> |
| 24 | #include <opendht/utils.h> |
| 25 | #include <opendht/thread_pool.h> |
| 26 | |
| 27 | #include <asio/executor_work_guard.hpp> |
| 28 | #include <asio/io_context.hpp> |
| 29 | |
| 30 | #include <readline/readline.h> |
| 31 | #include <readline/history.h> |
| 32 | |
| 33 | #include <condition_variable> |
| 34 | #include<fmt/chrono.h> |
| 35 | |
| 36 | namespace dhtnet { |
| 37 | using namespace std::literals::chrono_literals; |
| 38 | using clock = std::chrono::high_resolution_clock; |
| 39 | |
| 40 | |
| 41 | struct ConnectionHandler |
| 42 | { |
| 43 | dht::crypto::Identity id; |
| 44 | std::shared_ptr<Logger> logger; |
| 45 | std::shared_ptr<tls::CertificateStore> certStore; |
| 46 | std::shared_ptr<dht::DhtRunner> dht; |
| 47 | std::shared_ptr<ConnectionManager> connectionManager; |
| 48 | std::shared_ptr<asio::io_context> ioContext; |
| 49 | std::shared_ptr<std::thread> ioContextRunner; |
| 50 | }; |
| 51 | |
| 52 | std::unique_ptr<ConnectionHandler> |
| 53 | setupHandler(const std::string& name, |
| 54 | std::shared_ptr<asio::io_context> ioContext, |
| 55 | std::shared_ptr<std::thread> ioContextRunner, |
| 56 | std::shared_ptr<IceTransportFactory> factory, |
| 57 | std::shared_ptr<Logger> logger) |
| 58 | { |
| 59 | auto h = std::make_unique<ConnectionHandler>(); |
| 60 | auto ca = dht::crypto::generateIdentity("ca"); |
| 61 | h->id = dht::crypto::generateIdentity(name, ca); |
| 62 | h->logger = logger; |
| 63 | h->certStore = std::make_shared<tls::CertificateStore>(name, h->logger); |
| 64 | h->ioContext = std::make_shared<asio::io_context>(); |
| 65 | h->ioContext = ioContext; |
| 66 | h->ioContextRunner = ioContextRunner; |
| 67 | |
| 68 | dht::DhtRunner::Config dhtConfig; |
| 69 | dhtConfig.dht_config.id = h->id; |
| 70 | dhtConfig.threaded = true; |
| 71 | dhtConfig.peer_discovery = true; |
| 72 | dhtConfig.peer_publish = true; |
| 73 | |
| 74 | dht::DhtRunner::Context dhtContext; |
| 75 | |
| 76 | dhtContext.identityAnnouncedCb = [](bool ok) { |
| 77 | fmt::print("{} Identity announced {}\n", clock::now().time_since_epoch().count(), ok); |
| 78 | }; |
| 79 | |
| 80 | dhtContext.publicAddressChangedCb = [](std::vector<dht::SockAddr> addr) { |
| 81 | if (addr.size() != 0) |
| 82 | fmt::print("{} Public address changed\n", clock::now().time_since_epoch().count()); |
| 83 | }; |
| 84 | |
| 85 | dhtContext.statusChangedCallback = [](dht::NodeStatus status4, dht::NodeStatus status6) { |
| 86 | fmt::print("{} Connectivity changed: IPv4: {}, IPv6: {}\n", clock::now().time_since_epoch().count(), dht::statusToStr(status4), dht::statusToStr(status6)); |
| 87 | }; |
| 88 | |
| 89 | |
| 90 | dhtContext.certificateStore = [c = h->certStore](const dht::InfoHash& pk_id) { |
| 91 | std::vector<std::shared_ptr<dht::crypto::Certificate>> ret; |
| 92 | if (auto cert = c->getCertificate(pk_id.toString())) |
| 93 | ret.emplace_back(std::move(cert)); |
| 94 | return ret; |
| 95 | }; |
| 96 | // dhtContext.logger = h->logger; |
| 97 | |
| 98 | h->dht = std::make_shared<dht::DhtRunner>(); |
| 99 | h->dht->run(dhtConfig, std::move(dhtContext)); |
| 100 | |
| 101 | auto config = std::make_shared<ConnectionManager::Config>(); |
| 102 | config->dht = h->dht; |
| 103 | config->id = h->id; |
| 104 | config->ioContext = h->ioContext; |
| 105 | config->factory = factory; |
| 106 | config->logger = logger; |
| 107 | config->certStore = h->certStore; |
| 108 | config->cachePath = std::filesystem::current_path() / "temp"; |
| 109 | |
| 110 | h->connectionManager = std::make_shared<ConnectionManager>(config); |
| 111 | h->connectionManager->onICERequest([](const DeviceId&) { return true; }); |
| 112 | fmt::print("Identity:{}\n", h->id.second->getId()); |
| 113 | return h; |
| 114 | } |
| 115 | |
| 116 | void |
| 117 | print_help() |
| 118 | { |
| 119 | fmt::print("Commands:\n"); |
| 120 | fmt::print(" help, h, ? - print this help\n"); |
| 121 | fmt::print(" quit, exit, q, x - exit the program\n"); |
| 122 | fmt::print(" connect <peer_id> - connect to a peer\n"); |
| 123 | fmt::print(" cc - connectivity changed\n"); |
| 124 | } |
| 125 | |
| 126 | } // namespace dhtnet |
| 127 | |
| 128 | static void |
| 129 | setSipLogLevel() |
| 130 | { |
| 131 | int level = 0; |
| 132 | if (char* envvar = getenv("SIPLOGLEVEL")) { |
| 133 | // From 0 (min) to 6 (max) |
| 134 | level = std::clamp(std::stoi(envvar), 0, 6); |
| 135 | } |
| 136 | |
| 137 | pj_log_set_level(level); |
| 138 | pj_log_set_log_func([](int level, const char* data, int /*len*/) {}); |
| 139 | } |
| 140 | |
| 141 | using namespace std::literals::chrono_literals; |
| 142 | int |
| 143 | main(int argc, char** argv) |
| 144 | { |
| 145 | setSipLogLevel(); |
| 146 | std::shared_ptr<dhtnet::Logger> logger; // = dht::log::getStdLogger(); |
| 147 | auto factory = std::make_shared<dhtnet::IceTransportFactory>(logger); |
| 148 | auto ioContext = std::make_shared<asio::io_context>(); |
| 149 | auto ioContextRunner = std::make_shared<std::thread>([context = ioContext]() { |
| 150 | try { |
| 151 | auto work = asio::make_work_guard(*context); |
| 152 | context->run(); |
| 153 | } catch (const std::exception& ex) { |
| 154 | fmt::print(stderr, "Exception: {}\n", ex.what()); |
| 155 | } |
| 156 | }); |
| 157 | |
| 158 | // Create a new DHTNet node |
| 159 | auto dhtnet = setupHandler("DHT", ioContext, ioContextRunner, factory, logger); |
| 160 | |
| 161 | dhtnet->connectionManager->onDhtConnected(dhtnet->id.first->getPublicKey()); |
| 162 | |
| 163 | // Set up a handler for incoming channel requests |
| 164 | dhtnet->connectionManager->onChannelRequest( |
| 165 | [](const std::shared_ptr<dht::crypto::Certificate>&, const std::string& name) { |
| 166 | fmt::print("Channel request received: {}\n", name); |
| 167 | return true; |
| 168 | }); |
| 169 | |
| 170 | while (true) { |
| 171 | char* l = readline("> "); |
| 172 | if (not l) |
| 173 | break; |
| 174 | std::string_view line {l}; |
| 175 | if (line.empty()) |
| 176 | continue; |
| 177 | add_history(l); |
| 178 | auto args = dhtnet::split_string(line, ' '); |
| 179 | auto command = args[0]; |
| 180 | if (command == "quit" || command == "exit" || command == "q" || command == "x") |
| 181 | break; |
| 182 | else if (command == "help" || command == "h" || command == "?") { |
| 183 | dhtnet::print_help(); |
| 184 | } else if (command == "connect") { |
| 185 | if (args.size() < 2) { |
| 186 | fmt::print("Usage: connect <peer_id>\n"); |
| 187 | continue; |
| 188 | } |
| 189 | std::condition_variable cv; |
| 190 | std::mutex mtx; |
| 191 | std::unique_lock lock {mtx}; |
| 192 | |
| 193 | bool ret = false; |
| 194 | dht::InfoHash peer_id(args[1]); |
| 195 | dhtnet->connectionManager |
| 196 | ->connectDevice(peer_id, |
| 197 | "channelName", |
| 198 | [&](std::shared_ptr<dhtnet::ChannelSocket> socket, |
| 199 | const dht::InfoHash&) { |
| 200 | if (socket) { |
| 201 | ret = true; |
| 202 | cv.notify_one(); |
| 203 | } |
| 204 | }); |
| 205 | if (cv.wait_for(lock, 10s, [&] { return ret; })) { |
| 206 | fmt::print("Connected to {}\n", peer_id); |
| 207 | } else { |
| 208 | fmt::print("Failed to connect to {}\n", peer_id); |
| 209 | } |
| 210 | } else if (command == "cc") { |
| 211 | dhtnet->dht->connectivityChanged(); |
| 212 | } else { |
| 213 | fmt::print("Unknown command: {}\n", command); |
| 214 | } |
| 215 | } |
| 216 | fmt::print("Stopping…\n"); |
| 217 | |
| 218 | ioContext->stop(); |
| 219 | ioContextRunner->join(); |
| 220 | } |