blob: 586898b15156d8e04a2d13310cd89bcd722953a8 [file] [log] [blame]
Adrien BĂ©raudb75ab9d2023-08-23 09:26:58 -04001#include "upnp/upnp_control.h"
2#include "upnp/upnp_context.h"
3#include "string_utils.h"
4#include <asio/executor_work_guard.hpp>
5#include <opendht/log.h>
6
7#include <readline/readline.h>
8#include <readline/history.h>
9
10void
11print_help()
12{
13 fmt::print("Commands:\n"
14 " help, h, ?\n"
15 " quit, exit, q, x\n"
16 " ip\n"
17 " open <port> <protocol>\n");
18}
19
20std::string to_lower(std::string_view str_v) {
21 std::string str(str_v);
22 std::transform(str.begin(), str.end(), str.begin(),
23 [](unsigned char c){ return std::tolower(c); }
24 );
25 return str;
26}
27
28int
29main(int argc, char** argv)
30{
31 auto ioContext = std::make_shared<asio::io_context>();
32 std::shared_ptr<dht::log::Logger> logger = dht::log::getStdLogger();
33 auto upnpContext = std::make_shared<dhtnet::upnp::UPnPContext>(ioContext, logger);
34
35 auto ioContextRunner = std::make_shared<std::thread>([context = ioContext]() {
36 try {
37 auto work = asio::make_work_guard(*context);
38 context->run();
39 } catch (const std::exception& ex) {
40 // print the error;
41 }
42 });
43
44 auto controller = std::make_shared<dhtnet::upnp::Controller>(upnpContext);
45 std::set<std::shared_ptr<dhtnet::upnp::Mapping>> mappings;
46
47 while (true) {
48 char* l = readline("> ");
49 if (not l)
50 break;
51 std::string_view line{l};
52 if (line.empty())
53 continue;
54 add_history(l);
55 auto args = dhtnet::split_string(line, ' ');
56 auto command = args[0];
57 if (command == "quit" || command == "exit" || command == "q" || command == "x")
58 break;
59 else if (command == "help" || command == "h" || command == "?") {
60 print_help();
61 }
62 else if (command == "ip") {
63 fmt::print("{}\n", controller->getExternalIP().toString());
64 } else if (command == "open") {
65 if (args.size() < 3) {
66 fmt::print("Usage: open <port> <protocol>\n");
67 continue;
68 }
69 auto protocol = to_lower(args[2]) == "udp" ? dhtnet::upnp::PortType::UDP : dhtnet::upnp::PortType::TCP;
70 mappings.emplace(controller->reserveMapping(dhtnet::to_int<in_port_t>(args[1]), protocol));
71 } else if (command == "close") {
72 if (args.size() < 2) {
73 fmt::print("Usage: close <port>\n");
74 continue;
75 }
76 auto port = dhtnet::to_int<in_port_t>(args[1]);
77 for (auto it = mappings.begin(); it != mappings.end(); ) {
78 if ((*it)->getExternalPort() == port) {
79 controller->releaseMapping(**it);
80 it = mappings.erase(it);
81 } else {
82 ++it;
83 }
84 }
85 } else {
86 fmt::print("Unknown command: {}\n", command);
87 }
88 }
89 fmt::print("Stopping...");
90 for (auto c: mappings)
91 controller->releaseMapping(*c);
92 mappings.clear();
93
94 ioContext->stop();
95 ioContextRunner->join();
96}