diff --git a/tools/dhtnode.cpp b/tools/dhtnode.cpp index 346b816ae4938aaba046d6aeb74eb05d386ebfa4..62caf441c2d14d1007d874242a1c09faa3d1b0fd 100644 --- a/tools/dhtnode.cpp +++ b/tools/dhtnode.cpp @@ -59,194 +59,202 @@ void print_node_info(const DhtRunner& dht, const dht_params& params) { int main(int argc, char **argv) { - auto params = parseArgs(argc, argv); - if (params.help) { - print_usage(); - return 0; - } - - // TODO: remove with GnuTLS >= 3.3 - int rc = gnutls_global_init(); - if (rc != GNUTLS_E_SUCCESS) - throw std::runtime_error(std::string("Error initializing GnuTLS: ")+gnutls_strerror(rc)); - - dht::crypto::Identity crt {}; - if (params.generate_identity) { - auto ca_tmp = dht::crypto::generateIdentity("DHT Node CA"); - crt = dht::crypto::generateIdentity("DHT Node", ca_tmp); - } - DhtRunner dht; - dht.run(params.port, crt, true, params.is_bootstrap_node); + try { + auto params = parseArgs(argc, argv); + if (params.help) { + print_usage(); + return 0; + } - if (not params.bootstrap.empty()) { - std::cout << "Bootstrap: " << params.bootstrap[0] << ":" << params.bootstrap[1] << std::endl; - dht.bootstrap(params.bootstrap[0].c_str(), params.bootstrap[1].c_str()); - } + // TODO: remove with GnuTLS >= 3.3 + int rc = gnutls_global_init(); + if (rc != GNUTLS_E_SUCCESS) + throw std::runtime_error(std::string("Error initializing GnuTLS: ")+gnutls_strerror(rc)); - print_node_info(dht, params); - std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl << std::endl; - - bool do_log {false}; - while (true) - { - std::cout << ">> "; - std::string line; - std::getline(std::cin, line); - std::istringstream iss(line); - std::string op, idstr, value; - iss >> op >> idstr; - - if (std::cin.eof() || op == "x" || op == "q" || op == "exit" || op == "quit") { - break; - } else if (op == "h" || op == "help") { - std::cout << "OpenDht command line interface (CLI)" << std::endl; - std::cout << "Possible commands:" << std::endl; - std::cout << " h, help Print this help message." << std::endl; - std::cout << " q, quit Quit the program." << std::endl; - std::cout << " log Print the full DHT log." << std::endl; - - std::cout << std::endl << "Node information:" << std::endl; - std::cout << " ll Print basic information and stats about the current node." << std::endl; - std::cout << " ls Print basic information about current searches." << std::endl; - std::cout << " ld Print basic information about currenty stored values on this node." << std::endl; - std::cout << " lr Print the full current routing table of this node" << std::endl; - - std::cout << std::endl << "Operations on the DHT:" << std::endl; - std::cout << " g [key] Get values at [key]." << std::endl; - std::cout << " l [key] Listen for value changes at [key]." << std::endl; - std::cout << " p [key] [str] Put string value at [key]." << std::endl; - std::cout << " s [key] [str] Put string value at [key], signed with our generated private key." << std::endl; - std::cout << " e [key] [dest] [str] Put string value at [key], encrypted for [dest] with its public key (if found)." << std::endl; - std::cout << std::endl; - continue; - } else if (op == "ll") { - print_node_info(dht, params); - unsigned good4, dubious4, cached4, incoming4; - unsigned good6, dubious6, cached6, incoming6; - dht.getNodesStats(AF_INET, &good4, &dubious4, &cached4, &incoming4); - dht.getNodesStats(AF_INET6, &good6, &dubious6, &cached6, &incoming6); - std::cout << "IPv4 nodes : " << good4 << " good, " << dubious4 << " dubious, " << incoming4 << " incoming." << std::endl; - std::cout << "IPv6 nodes : " << good6 << " good, " << dubious6 << " dubious, " << incoming6 << " incoming." << std::endl; - continue; - } else if (op == "lr") { - std::cout << "IPv4 routing table:" << std::endl; - std::cout << dht.getRoutingTablesLog(AF_INET) << std::endl; - std::cout << "IPv6 routing table:" << std::endl; - std::cout << dht.getRoutingTablesLog(AF_INET6) << std::endl; - continue; - } else if (op == "ld") { - std::cout << dht.getStorageLog() << std::endl; - continue; - } else if (op == "ls") { - std::cout << "Searches:" << std::endl; - std::cout << dht.getSearchesLog() << std::endl; - continue; - } else if (op == "la") { - std::cout << "Reported public addresses:" << std::endl; - auto addrs = dht.getPublicAddressStr(); - for (const auto& addr : addrs) - std::cout << addr << std::endl; - continue; - } else if (op == "log") { - do_log = !do_log; - if (do_log) - enableLogging(dht); - else - disableLogging(dht); - continue; + dht::crypto::Identity crt {}; + if (params.generate_identity) { + auto ca_tmp = dht::crypto::generateIdentity("DHT Node CA"); + crt = dht::crypto::generateIdentity("DHT Node", ca_tmp); } + + dht.run(params.port, crt, true, params.is_bootstrap_node); - if (op.empty()) - continue; + if (params.log) + enableLogging(dht); - dht::InfoHash id {idstr}; - static const std::set<std::string> VALID_OPS {"g", "l", "p", "s", "e", "a"}; - if (VALID_OPS.find(op) == VALID_OPS.cend()) { - std::cout << "Unknown command: " << op << std::endl; - std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl; - continue; - } - static constexpr dht::InfoHash INVALID_ID {}; - if (id == INVALID_ID) { - std::cout << "Syntax error: invalid InfoHash." << std::endl; - continue; + if (not params.bootstrap.empty()) { + std::cout << "Bootstrap: " << params.bootstrap[0] << ":" << params.bootstrap[1] << std::endl; + dht.bootstrap(params.bootstrap[0].c_str(), params.bootstrap[1].c_str()); } - auto start = std::chrono::high_resolution_clock::now(); - if (op == "g") { - dht.get(id, [start](std::shared_ptr<Value> value) { - auto now = std::chrono::high_resolution_clock::now(); - std::cout << "Get: found value (after " << print_dt(now-start) << "s)" << std::endl; - std::cout << "\t" << *value << std::endl; - return true; - }, [start](bool ok) { - auto end = std::chrono::high_resolution_clock::now(); - std::cout << "Get: " << (ok ? "completed" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl; - }); - } - else if (op == "l") { - std::cout << id << std::endl; - dht.listen(id, [](std::shared_ptr<Value> value) { - std::cout << "Listen: found value:" << std::endl; - std::cout << "\t" << *value << std::endl; - return true; - }); - } - else if (op == "p") { - std::string v; - iss >> v; - dht.put(id, dht::Value { - dht::ValueType::USER_DATA.id, - std::vector<uint8_t> {v.begin(), v.end()} - }, [start](bool ok) { - auto end = std::chrono::high_resolution_clock::now(); - std::cout << "Put: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl; - }); - } - else if (op == "s") { - if (not params.generate_identity) { - print_id_req(); + print_node_info(dht, params); + std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl << std::endl; + + while (true) + { + std::cout << ">> "; + std::string line; + std::getline(std::cin, line); + std::istringstream iss(line); + std::string op, idstr, value; + iss >> op >> idstr; + + if (std::cin.eof() || op == "x" || op == "q" || op == "exit" || op == "quit") { + break; + } else if (op == "h" || op == "help") { + std::cout << "OpenDht command line interface (CLI)" << std::endl; + std::cout << "Possible commands:" << std::endl; + std::cout << " h, help Print this help message." << std::endl; + std::cout << " q, quit Quit the program." << std::endl; + std::cout << " log Print the full DHT log." << std::endl; + + std::cout << std::endl << "Node information:" << std::endl; + std::cout << " ll Print basic information and stats about the current node." << std::endl; + std::cout << " ls Print basic information about current searches." << std::endl; + std::cout << " ld Print basic information about currenty stored values on this node." << std::endl; + std::cout << " lr Print the full current routing table of this node" << std::endl; + + std::cout << std::endl << "Operations on the DHT:" << std::endl; + std::cout << " g [key] Get values at [key]." << std::endl; + std::cout << " l [key] Listen for value changes at [key]." << std::endl; + std::cout << " p [key] [str] Put string value at [key]." << std::endl; + std::cout << " s [key] [str] Put string value at [key], signed with our generated private key." << std::endl; + std::cout << " e [key] [dest] [str] Put string value at [key], encrypted for [dest] with its public key (if found)." << std::endl; + std::cout << std::endl; + continue; + } else if (op == "ll") { + print_node_info(dht, params); + unsigned good4, dubious4, cached4, incoming4; + unsigned good6, dubious6, cached6, incoming6; + dht.getNodesStats(AF_INET, &good4, &dubious4, &cached4, &incoming4); + dht.getNodesStats(AF_INET6, &good6, &dubious6, &cached6, &incoming6); + std::cout << "IPv4 nodes : " << good4 << " good, " << dubious4 << " dubious, " << incoming4 << " incoming." << std::endl; + std::cout << "IPv6 nodes : " << good6 << " good, " << dubious6 << " dubious, " << incoming6 << " incoming." << std::endl; + continue; + } else if (op == "lr") { + std::cout << "IPv4 routing table:" << std::endl; + std::cout << dht.getRoutingTablesLog(AF_INET) << std::endl; + std::cout << "IPv6 routing table:" << std::endl; + std::cout << dht.getRoutingTablesLog(AF_INET6) << std::endl; + continue; + } else if (op == "ld") { + std::cout << dht.getStorageLog() << std::endl; + continue; + } else if (op == "ls") { + std::cout << "Searches:" << std::endl; + std::cout << dht.getSearchesLog() << std::endl; + continue; + } else if (op == "la") { + std::cout << "Reported public addresses:" << std::endl; + auto addrs = dht.getPublicAddressStr(); + for (const auto& addr : addrs) + std::cout << addr << std::endl; + continue; + } else if (op == "log") { + params.log = !params.log; + if (params.log) + enableLogging(dht); + else + disableLogging(dht); continue; } - std::string v; - iss >> v; - dht.putSigned(id, dht::Value { - dht::ValueType::USER_DATA.id, - std::vector<uint8_t> {v.begin(), v.end()} - }, [start](bool ok) { - auto end = std::chrono::high_resolution_clock::now(); - std::cout << "Put signed: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl; - }); - } - else if (op == "e") { - if (not params.generate_identity) { - print_id_req(); + + if (op.empty()) + continue; + + dht::InfoHash id {idstr}; + static const std::set<std::string> VALID_OPS {"g", "l", "p", "s", "e", "a"}; + if (VALID_OPS.find(op) == VALID_OPS.cend()) { + std::cout << "Unknown command: " << op << std::endl; + std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl; continue; } - std::string tostr; - std::string v; - iss >> tostr >> v; - dht.putEncrypted(id, InfoHash(tostr), dht::Value { - dht::ValueType::USER_DATA.id, - std::vector<uint8_t> {v.begin(), v.end()} - }, [start](bool ok) { - auto end = std::chrono::high_resolution_clock::now(); - std::cout << "Put encrypted: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl; - }); - } - else if (op == "a") { - in_port_t port; - iss >> port; - dht.put(id, dht::Value {dht::IpServiceAnnouncement::TYPE.id, dht::IpServiceAnnouncement(port)}, [start](bool ok) { - auto end = std::chrono::high_resolution_clock::now(); - std::cout << "Announce: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl; - }); + static constexpr dht::InfoHash INVALID_ID {}; + if (id == INVALID_ID) { + std::cout << "Syntax error: invalid InfoHash." << std::endl; + continue; + } + + auto start = std::chrono::high_resolution_clock::now(); + if (op == "g") { + dht.get(id, [start](std::shared_ptr<Value> value) { + auto now = std::chrono::high_resolution_clock::now(); + std::cout << "Get: found value (after " << print_dt(now-start) << "s)" << std::endl; + std::cout << "\t" << *value << std::endl; + return true; + }, [start](bool ok) { + auto end = std::chrono::high_resolution_clock::now(); + std::cout << "Get: " << (ok ? "completed" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl; + }); + } + else if (op == "l") { + std::cout << id << std::endl; + dht.listen(id, [](std::shared_ptr<Value> value) { + std::cout << "Listen: found value:" << std::endl; + std::cout << "\t" << *value << std::endl; + return true; + }); + } + else if (op == "p") { + std::string v; + iss >> v; + dht.put(id, dht::Value { + dht::ValueType::USER_DATA.id, + std::vector<uint8_t> {v.begin(), v.end()} + }, [start](bool ok) { + auto end = std::chrono::high_resolution_clock::now(); + std::cout << "Put: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl; + }); + } + else if (op == "s") { + if (not params.generate_identity) { + print_id_req(); + continue; + } + std::string v; + iss >> v; + dht.putSigned(id, dht::Value { + dht::ValueType::USER_DATA.id, + std::vector<uint8_t> {v.begin(), v.end()} + }, [start](bool ok) { + auto end = std::chrono::high_resolution_clock::now(); + std::cout << "Put signed: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl; + }); + } + else if (op == "e") { + if (not params.generate_identity) { + print_id_req(); + continue; + } + std::string tostr; + std::string v; + iss >> tostr >> v; + dht.putEncrypted(id, InfoHash(tostr), dht::Value { + dht::ValueType::USER_DATA.id, + std::vector<uint8_t> {v.begin(), v.end()} + }, [start](bool ok) { + auto end = std::chrono::high_resolution_clock::now(); + std::cout << "Put encrypted: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl; + }); + } + else if (op == "a") { + in_port_t port; + iss >> port; + dht.put(id, dht::Value {dht::IpServiceAnnouncement::TYPE.id, dht::IpServiceAnnouncement(port)}, [start](bool ok) { + auto end = std::chrono::high_resolution_clock::now(); + std::cout << "Announce: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl; + }); + } } + + std::cout << std::endl << "Stopping node..." << std::endl; + } catch(const std::exception&e) { + std::cout << std::endl << e.what() << std::endl; } - std::cout << std::endl << "Stopping node..." << std::endl; dht.join(); gnutls_global_deinit(); + return 0; } diff --git a/tools/tools_common.h b/tools/tools_common.h index 83cb8b8e0ede2b3c1a2f2eeb885f3f514348a3df..e107188a918764cf509101bac4916c4547891684 100644 --- a/tools/tools_common.h +++ b/tools/tools_common.h @@ -128,6 +128,7 @@ static const constexpr in_port_t DHT_DEFAULT_PORT = 4222; struct dht_params { bool help {false}; // print help and exit + bool log {false}; in_port_t port {DHT_DEFAULT_PORT}; bool is_bootstrap_node {false}; bool generate_identity {false}; @@ -139,6 +140,7 @@ static const struct option long_options[] = { {"port", required_argument, nullptr, 'p'}, {"bootstrap", optional_argument, nullptr, 'b'}, {"identity", no_argument , nullptr, 'i'}, + {"verbose", no_argument , nullptr, 'v'}, {nullptr, 0, nullptr, 0} }; @@ -146,7 +148,7 @@ dht_params parseArgs(int argc, char **argv) { dht_params params; int opt; - while ((opt = getopt_long(argc, argv, ":hip:b:", long_options, nullptr)) != -1) { + while ((opt = getopt_long(argc, argv, ":hivp:b:", long_options, nullptr)) != -1) { switch (opt) { case 'p': { int port_arg = atoi(optarg); @@ -168,6 +170,9 @@ parseArgs(int argc, char **argv) { case 'h': params.help = true; break; + case 'v': + params.log = true; + break; case 'i': params.generate_identity = true; break;