blob: a9590ca92866d3533d1853e871b25cafb41398e6 [file] [log] [blame]
Amnae61e1562024-06-25 09:50:29 -04001/*
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
36namespace dhtnet {
37using namespace std::literals::chrono_literals;
38using clock = std::chrono::high_resolution_clock;
39
40
41struct 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
52std::unique_ptr<ConnectionHandler>
53setupHandler(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
116void
117print_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
128static void
129setSipLogLevel()
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
141using namespace std::literals::chrono_literals;
142int
143main(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}