| #include "upnp/upnp_control.h" |
| #include "upnp/upnp_context.h" |
| #include "string_utils.h" |
| #include <asio/executor_work_guard.hpp> |
| #include <opendht/log.h> |
| |
| #include <readline/readline.h> |
| #include <readline/history.h> |
| |
| void |
| print_help() |
| { |
| fmt::print("Commands:\n" |
| " help, h, ?\n" |
| " quit, exit, q, x\n" |
| " ip\n" |
| " open <port> <protocol>\n"); |
| } |
| |
| std::string to_lower(std::string_view str_v) { |
| std::string str(str_v); |
| std::transform(str.begin(), str.end(), str.begin(), |
| [](unsigned char c){ return std::tolower(c); } |
| ); |
| return str; |
| } |
| |
| int |
| main(int argc, char** argv) |
| { |
| auto ioContext = std::make_shared<asio::io_context>(); |
| std::shared_ptr<dht::log::Logger> logger = dht::log::getStdLogger(); |
| auto upnpContext = std::make_shared<dhtnet::upnp::UPnPContext>(ioContext, logger); |
| |
| auto ioContextRunner = std::make_shared<std::thread>([context = ioContext]() { |
| try { |
| auto work = asio::make_work_guard(*context); |
| context->run(); |
| } catch (const std::exception& ex) { |
| // print the error; |
| } |
| }); |
| |
| auto controller = std::make_shared<dhtnet::upnp::Controller>(upnpContext); |
| std::set<std::shared_ptr<dhtnet::upnp::Mapping>> mappings; |
| |
| while (true) { |
| char* l = readline("> "); |
| if (not l) |
| break; |
| std::string_view line{l}; |
| if (line.empty()) |
| continue; |
| add_history(l); |
| auto args = dhtnet::split_string(line, ' '); |
| auto command = args[0]; |
| if (command == "quit" || command == "exit" || command == "q" || command == "x") |
| break; |
| else if (command == "help" || command == "h" || command == "?") { |
| print_help(); |
| } |
| else if (command == "ip") { |
| fmt::print("{}\n", controller->getExternalIP().toString()); |
| } else if (command == "open") { |
| if (args.size() < 3) { |
| fmt::print("Usage: open <port> <protocol>\n"); |
| continue; |
| } |
| auto protocol = to_lower(args[2]) == "udp" ? dhtnet::upnp::PortType::UDP : dhtnet::upnp::PortType::TCP; |
| mappings.emplace(controller->reserveMapping(dhtnet::to_int<in_port_t>(args[1]), protocol)); |
| } else if (command == "close") { |
| if (args.size() < 2) { |
| fmt::print("Usage: close <port>\n"); |
| continue; |
| } |
| auto port = dhtnet::to_int<in_port_t>(args[1]); |
| for (auto it = mappings.begin(); it != mappings.end(); ) { |
| if ((*it)->getExternalPort() == port) { |
| controller->releaseMapping(**it); |
| it = mappings.erase(it); |
| } else { |
| ++it; |
| } |
| } |
| } else { |
| fmt::print("Unknown command: {}\n", command); |
| } |
| } |
| fmt::print("Stopping..."); |
| for (auto c: mappings) |
| controller->releaseMapping(*c); |
| mappings.clear(); |
| |
| ioContext->stop(); |
| ioContextRunner->join(); |
| } |