blob: 7c4febff98144065cabcb43f00a2830ba70ddc06 [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"
François-Simon Fauteux-Chapleau826f0ba2024-05-29 15:22:21 -040017 " open <port> <protocol>\n"
18 " close <port>\n"
19 " mappings\n");
20}
21
22void
23print_mappings(std::shared_ptr<dhtnet::upnp::UPnPContext> upnpContext)
24{
25 for (auto const& igdInfo : upnpContext->getIgdsInfo()) {
26 fmt::print("\nIGD: \"{}\" [local IP: {} - public IP: {}]\n",
27 igdInfo.uid,
28 igdInfo.localIp.toString(),
29 igdInfo.publicIp.toString());
30
31 if (igdInfo.mappingInfoList.empty())
32 continue;
33
34 static const char *format = "{:>8} {:>12} {:>12} {:>8} {:>8} {:>16} {:>16} {}\n";
35 fmt::print(format, "Protocol", "ExternalPort", "InternalPort", "Duration",
36 "Enabled?", "InternalClient", "RemoteHost", "Description");
37 for (auto const& mappingInfo : igdInfo.mappingInfoList) {
38 fmt::print(format,
39 mappingInfo.protocol,
40 mappingInfo.externalPort,
41 mappingInfo.internalPort,
42 mappingInfo.leaseDuration,
43 mappingInfo.enabled,
44 mappingInfo.internalClient,
45 mappingInfo.remoteHost.empty() ? "any" : mappingInfo.remoteHost,
46 mappingInfo.description);
47 }
48 }
Adrien Béraudb75ab9d2023-08-23 09:26:58 -040049}
50
51std::string to_lower(std::string_view str_v) {
52 std::string str(str_v);
53 std::transform(str.begin(), str.end(), str.begin(),
54 [](unsigned char c){ return std::tolower(c); }
55 );
56 return str;
57}
58
59int
60main(int argc, char** argv)
61{
62 auto ioContext = std::make_shared<asio::io_context>();
63 std::shared_ptr<dht::log::Logger> logger = dht::log::getStdLogger();
64 auto upnpContext = std::make_shared<dhtnet::upnp::UPnPContext>(ioContext, logger);
65
66 auto ioContextRunner = std::make_shared<std::thread>([context = ioContext]() {
67 try {
68 auto work = asio::make_work_guard(*context);
69 context->run();
70 } catch (const std::exception& ex) {
71 // print the error;
72 }
73 });
74
75 auto controller = std::make_shared<dhtnet::upnp::Controller>(upnpContext);
76 std::set<std::shared_ptr<dhtnet::upnp::Mapping>> mappings;
77
78 while (true) {
79 char* l = readline("> ");
80 if (not l)
81 break;
82 std::string_view line{l};
83 if (line.empty())
84 continue;
85 add_history(l);
86 auto args = dhtnet::split_string(line, ' ');
87 auto command = args[0];
88 if (command == "quit" || command == "exit" || command == "q" || command == "x")
89 break;
90 else if (command == "help" || command == "h" || command == "?") {
91 print_help();
92 }
93 else if (command == "ip") {
94 fmt::print("{}\n", controller->getExternalIP().toString());
95 } else if (command == "open") {
96 if (args.size() < 3) {
97 fmt::print("Usage: open <port> <protocol>\n");
98 continue;
99 }
100 auto protocol = to_lower(args[2]) == "udp" ? dhtnet::upnp::PortType::UDP : dhtnet::upnp::PortType::TCP;
101 mappings.emplace(controller->reserveMapping(dhtnet::to_int<in_port_t>(args[1]), protocol));
102 } else if (command == "close") {
103 if (args.size() < 2) {
104 fmt::print("Usage: close <port>\n");
105 continue;
106 }
107 auto port = dhtnet::to_int<in_port_t>(args[1]);
108 for (auto it = mappings.begin(); it != mappings.end(); ) {
109 if ((*it)->getExternalPort() == port) {
110 controller->releaseMapping(**it);
111 it = mappings.erase(it);
112 } else {
113 ++it;
114 }
115 }
François-Simon Fauteux-Chapleau826f0ba2024-05-29 15:22:21 -0400116 } else if (command == "mappings") {
117 print_mappings(upnpContext);
François-Simon Fauteux-Chapleaufd29c1d2024-05-30 16:48:26 -0400118 } else if (command == "restart") {
119 upnpContext->restart();
Adrien Béraudb75ab9d2023-08-23 09:26:58 -0400120 } else {
121 fmt::print("Unknown command: {}\n", command);
122 }
123 }
François-Simon Fauteux-Chapleau826f0ba2024-05-29 15:22:21 -0400124 fmt::print("Stopping...\n");
Adrien Béraudb75ab9d2023-08-23 09:26:58 -0400125 for (auto c: mappings)
126 controller->releaseMapping(*c);
127 mappings.clear();
128
129 ioContext->stop();
130 ioContextRunner->join();
131}