diff --git a/.gitignore b/.gitignore index 25a06b8..574f77a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build/ *.swp compile_commands.json +.ycm_extra_conf.py diff --git a/src/main.cpp b/src/main.cpp index 0b19c79..792abe6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,27 +4,81 @@ #include "upnperrors.h" #include #include +#include +#include +#include +#include + +#if !defined(NDEBUG) +# define KUU_VERBOSE +#endif namespace { + enum Protocol { + ProtocolUDP, + ProtocolTCP, + ProtocolOther + }; + class UPNPUrlsResource { public: - UPNPUrlsResource () : - m_initialized(false) + enum IGDReply { + IGDValid = 1, + IGDNotConnected = 2, + IGDUpnpNotSureIGD = 3, + IGDNotSure, + IGDNone + }; + + explicit UPNPUrlsResource (struct UPNPDev* parDevList) : + m_lanaddr(64, ' '), + m_igd_reply(IGDNone) { + const auto i = UPNP_GetValidIGD(parDevList, &m_urls, &m_data, &m_lanaddr[0], m_lanaddr.size()); + + switch (i) { + case 1: + case 2: + case 3: + m_igd_reply = static_cast(i); + break; + default: + m_igd_reply = IGDNotSure; + } + + const auto idx = m_lanaddr.find('\0'); + if (idx != m_lanaddr.npos) + m_lanaddr.resize(idx); } ~UPNPUrlsResource() noexcept { - if (m_initialized) { - m_initialized = false; + if (IGDNone != m_igd_reply) { + m_igd_reply = IGDNone; FreeUPNPUrls(&m_urls); } } - struct UPNPUrls* urls() { return &m_urls; } + const struct UPNPUrls* urls() const { return &m_urls; } + const struct IGDdatas* data() const { return &m_data; } + IGDReply igd_reply() const { return m_igd_reply; } + const std::string& lanaddr() const { return m_lanaddr; } private: + std::string m_lanaddr; struct UPNPUrls m_urls; - bool m_initialized; + struct IGDdatas m_data; + IGDReply m_igd_reply; + }; + + struct Redirection { + std::string internal_client; + std::string remote_host; + std::string desc; + Protocol protocol; + uint32_t duration; + uint16_t internal_port; + uint16_t external_port; + bool enabled; }; using UPNPDevsPtr = std::unique_ptr; @@ -36,6 +90,83 @@ namespace { } return retval; } + + bool is_enabled (const std::string& parEnabled) { + if (parEnabled.size() == 4) { + if (strncasecmp("true", parEnabled.c_str(), 4) == 0) + return true; + } + else if (parEnabled.size() == 2) { + if (strncasecmp("on", parEnabled.c_str(), 2) == 0) + return true; + } + else if (parEnabled.size() == 3) { + if (strncasecmp("yes", parEnabled.c_str(), 3) == 0) + return true; + } + if (parEnabled == "1") + return true; + return false; + } + + std::vector get_redirections (const UPNPUrlsResource& parResource) { + char intClient[40]; + char intPort[6]; + char extPort[6]; + char protocol[4]; + char desc[80]; + char enabled[6]; + char rHost[64]; + char duration[16]; + int r; + + int z = 0; + std::vector retval; + do { + rHost[0] = '\0'; + enabled[0] = '\0'; + duration[0] = '\0'; + desc[0] = '\0'; + extPort[0] = '\0'; + intPort[0] = '\0'; + intClient[0] = '\0'; + + r = UPNP_GetGenericPortMappingEntry( + parResource.urls()->controlURL, + parResource.data()->first.servicetype, + std::to_string(z).c_str(), + extPort, + intClient, + intPort, + protocol, + desc, + enabled, + rHost, + duration + ); + + //if (r) + // printf("GetGenericPortMappingEntry() returned %d (%s)\n", + // r, strupnperror(r)); + + if (not r) { + Redirection redir; + redir.protocol = (std::string(protocol) == "TCP" ? ProtocolTCP : (std::string(protocol) == "UDP" ? ProtocolUDP : ProtocolOther)); + redir.internal_client = intClient; + redir.remote_host = rHost; + redir.desc = desc; + redir.duration = static_cast(stoul(std::string(duration))); + redir.internal_port = static_cast(stoul(std::string(intPort))); + redir.external_port = static_cast(stoul(std::string(extPort))); + redir.enabled = is_enabled(std::string(enabled)); + + retval.push_back(redir); + } + + ++z; + } while (not r); + return retval; + } } //unnamed namespace int main() { @@ -62,9 +193,33 @@ int main() { return 1; } +#if defined(KUU_VERBOSE) std::cout << "Found: " << devs.front()->descURL << ", " << devs.front()->st << '\n'; - //struct IGDdatas data; + UPNPUrlsResource urls(devlist.get()); + switch (urls.igd_reply()) { + case UPNPUrlsResource::IGDValid: + std::cout << "Found valid IGD: " << urls.urls()->controlURL << '\n'; + break; + case UPNPUrlsResource::IGDNotConnected: + std::cout << "Found a (not connected?) IGD: " << urls.urls()->controlURL << '\n'; + break; + case UPNPUrlsResource::IGDUpnpNotSureIGD: + std::cout << "UPnP device found. Is it an IGD? " << urls.urls()->controlURL << '\n'; + break; + case UPNPUrlsResource::IGDNotSure: + std::cout << "Found device (igd?): " << urls.urls()->controlURL << '\n'; + } + + std::cout << "Local LAN ip address: " << urls.lanaddr() << '\n'; +#endif + + const auto redirs = get_redirections(urls); + for (auto& redir : redirs) { + std::cout << redir.protocol << ", " << redir.remote_host << ":" << redir.external_port << " --> " << + redir.internal_client << ":" << redir.internal_port << ", " << redir.duration << ", " << + redir.enabled << R"(, ")" << redir.desc << "\"\n"; + } return 0; }