mirror of
https://bitbucket.org/King_DuckZ/keepupnpup.git
synced 2025-01-19 21:36:36 +00:00
Refactor code into separate files.
This commit is contained in:
parent
be95a0f959
commit
308e364201
5 changed files with 483 additions and 263 deletions
|
@ -23,6 +23,7 @@ endif()
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME}
|
add_executable(${PROJECT_NAME}
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
|
src/upnp.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME}
|
target_include_directories(${PROJECT_NAME}
|
||||||
|
@ -45,4 +46,4 @@ else()
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 11)
|
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 14)
|
||||||
|
|
287
src/main.cpp
287
src/main.cpp
|
@ -15,287 +15,50 @@
|
||||||
* along with "keepupnpup". If not, see <http://www.gnu.org/licenses/>.
|
* along with "keepupnpup". If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "upnp.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "miniupnpc.h"
|
|
||||||
#include "upnpcommands.h"
|
|
||||||
#include "upnperrors.h"
|
|
||||||
#include "enum.h"
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <ciso646>
|
#include <ciso646>
|
||||||
#include <cstdint>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#if !defined(NDEBUG)
|
|
||||||
# define KUU_VERBOSE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
BETTER_ENUM (Protocol, uint8_t,
|
|
||||||
UDP,
|
|
||||||
TCP,
|
|
||||||
Other
|
|
||||||
);
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class UPNPUrlsResource {
|
bool has_mapping (const std::vector<kuu::Redirection>& parRedirs, const std::string& parAddr, uint16_t parPort, kuu::Protocol parProtocol) {
|
||||||
public:
|
|
||||||
enum IGDReply {
|
|
||||||
IGDValid = 1,
|
|
||||||
IGDNotConnected = 2,
|
|
||||||
IGDUpnpNotSureIGD = 3,
|
|
||||||
IGDNotSure,
|
|
||||||
IGDNone
|
|
||||||
};
|
|
||||||
|
|
||||||
explicit UPNPUrlsResource (struct UPNPDev* parDevList) :
|
|
||||||
m_lanaddr(),
|
|
||||||
m_externaladdr(),
|
|
||||||
m_igd_reply(IGDNone)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::array<char, 64> lanaddr;
|
|
||||||
const auto i = UPNP_GetValidIGD(parDevList, &m_urls, &m_data, lanaddr.data(), lanaddr.size());
|
|
||||||
|
|
||||||
switch (i) {
|
|
||||||
case 1:
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
m_igd_reply = static_cast<IGDReply>(i);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
m_igd_reply = IGDNotSure;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_lanaddr = lanaddr.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
std::array<char, 64> externaladdr;
|
|
||||||
const auto r = UPNP_GetExternalIPAddress(m_urls.controlURL, m_data.first.servicetype, externaladdr.data());
|
|
||||||
if (UPNPCOMMAND_SUCCESS != r)
|
|
||||||
m_externaladdr = externaladdr.data();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~UPNPUrlsResource() noexcept {
|
|
||||||
if (IGDNone != m_igd_reply) {
|
|
||||||
m_igd_reply = IGDNone;
|
|
||||||
FreeUPNPUrls(&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; }
|
|
||||||
const std::string& externaladdr() const { return m_externaladdr; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string m_lanaddr;
|
|
||||||
std::string m_externaladdr;
|
|
||||||
struct UPNPUrls m_urls;
|
|
||||||
struct IGDdatas m_data;
|
|
||||||
IGDReply m_igd_reply;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Redirection {
|
|
||||||
Redirection() : protocol(Protocol::Other) {}
|
|
||||||
|
|
||||||
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<struct UPNPDev, void(*)(struct UPNPDev*)>;
|
|
||||||
|
|
||||||
std::vector<struct UPNPDev*> dev_list_to_vector (struct UPNPDev* parList) {
|
|
||||||
std::vector<struct UPNPDev*> retval;
|
|
||||||
for (auto dev = parList; dev; dev = dev->pNext) {
|
|
||||||
retval.push_back(dev);
|
|
||||||
}
|
|
||||||
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<Redirection> 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<Redirection> 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;
|
|
||||||
better_enums::optional<Protocol> proto = Protocol::_from_string_nothrow(protocol);
|
|
||||||
redir.protocol = (proto ? *proto : +Protocol::Other);
|
|
||||||
redir.internal_client = intClient;
|
|
||||||
redir.remote_host = rHost;
|
|
||||||
redir.desc = desc;
|
|
||||||
redir.duration = static_cast<uint32_t>(stoul(std::string(duration)));
|
|
||||||
redir.internal_port = static_cast<uint16_t>(stoul(std::string(intPort)));
|
|
||||||
redir.external_port = static_cast<uint16_t>(stoul(std::string(extPort)));
|
|
||||||
redir.enabled = is_enabled(std::string(enabled));
|
|
||||||
|
|
||||||
retval.push_back(redir);
|
|
||||||
}
|
|
||||||
|
|
||||||
++z;
|
|
||||||
} while (not r);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_port_mapping (
|
|
||||||
const UPNPUrlsResource& parResource,
|
|
||||||
uint16_t parExtPort,
|
|
||||||
uint16_t parIntPort,
|
|
||||||
const std::string& parAddr,
|
|
||||||
const std::string& parDesc,
|
|
||||||
uint32_t parDuration,
|
|
||||||
Protocol parProtocol
|
|
||||||
) {
|
|
||||||
const auto r = UPNP_AddPortMapping(
|
|
||||||
parResource.urls()->controlURL,
|
|
||||||
parResource.data()->first.servicetype,
|
|
||||||
std::to_string(parExtPort).c_str(),
|
|
||||||
std::to_string(parIntPort).c_str(),
|
|
||||||
parAddr.c_str(),
|
|
||||||
parDesc.c_str(),
|
|
||||||
parProtocol._to_string(),
|
|
||||||
0,
|
|
||||||
std::to_string(parDuration).c_str()
|
|
||||||
);
|
|
||||||
//if (UPNPCOMMAND_SUCCESS != r)
|
|
||||||
//printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
|
|
||||||
//eport, iport, iaddr, r, strupnperror(r));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool has_mapping (const std::vector<Redirection>& parRedirs, const std::string& parAddr, uint16_t parPort, Protocol parProtocol) {
|
|
||||||
for (auto& redir : parRedirs) {
|
for (auto& redir : parRedirs) {
|
||||||
if (redir.protocol == parProtocol and redir.internal_port == parPort and redir.internal_client == parAddr)
|
if (redir.protocol == parProtocol and redir.internal_port == parPort and redir.internal_client == parAddr)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void runapp() {
|
||||||
|
kuu::UPNP upnp;
|
||||||
|
|
||||||
|
const auto redirs = upnp.redirections();
|
||||||
|
|
||||||
|
if (not has_mapping(redirs, upnp.lanaddr(), 6881, kuu::Protocol::TCP))
|
||||||
|
upnp.add_port_mapping(6881, 6881, upnp.lanaddr(), "RoutArm redir", 0, kuu::Protocol::TCP);
|
||||||
|
if (not has_mapping(redirs, upnp.lanaddr(), 49164, kuu::Protocol::TCP))
|
||||||
|
upnp.add_port_mapping(49164, 49164, upnp.lanaddr(), "RoutArm redir", 0, kuu::Protocol::TCP);
|
||||||
|
if (not has_mapping(redirs, upnp.lanaddr(), 49164, kuu::Protocol::UDP))
|
||||||
|
upnp.add_port_mapping(49164, 49164, upnp.lanaddr(), "RoutArm redir", 0, kuu::Protocol::UDP);
|
||||||
|
|
||||||
|
//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";
|
||||||
|
//}
|
||||||
|
}
|
||||||
} //unnamed namespace
|
} //unnamed namespace
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
int error;
|
try {
|
||||||
UPNPDevsPtr devlist(nullptr, &freeUPNPDevlist);
|
runapp();
|
||||||
|
|
||||||
devlist.reset(upnpDiscover(
|
|
||||||
2000,
|
|
||||||
nullptr, //multicastif
|
|
||||||
nullptr, //minissdpdpath
|
|
||||||
UPNP_LOCAL_PORT_ANY, //localport
|
|
||||||
0, //ipv6
|
|
||||||
2, //ttl
|
|
||||||
&error
|
|
||||||
));
|
|
||||||
|
|
||||||
const auto devs = dev_list_to_vector(devlist.get());
|
|
||||||
if (devs.empty()) {
|
|
||||||
std::cerr << "No UPNP devices found\n";
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
else if (devs.size() > 1) {
|
catch (const kuu::Exception& e) {
|
||||||
std::cerr << "More than one UPNP device found (" << devs.size() << "), aborting\n";
|
std::cerr << e.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
UPNPUrlsResource urls(devlist.get());
|
|
||||||
|
|
||||||
#if defined(KUU_VERBOSE)
|
|
||||||
std::cout << "Found: " << devs.front()->descURL << ", " << devs.front()->st << '\n';
|
|
||||||
|
|
||||||
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';
|
|
||||||
std::cout << "External ip address: " << urls.externaladdr() << '\n';
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const auto redirs = get_redirections(urls);
|
|
||||||
|
|
||||||
if (not has_mapping(redirs, urls.lanaddr(), 6881, Protocol::TCP))
|
|
||||||
add_port_mapping(urls, 6881, 6881, urls.lanaddr(), "RoutArm redir", 0, Protocol::TCP);
|
|
||||||
if (not has_mapping(redirs, urls.lanaddr(), 49164, Protocol::TCP))
|
|
||||||
add_port_mapping(urls, 49164, 49164, urls.lanaddr(), "RoutArm redir", 0, Protocol::TCP);
|
|
||||||
if (not has_mapping(redirs, urls.lanaddr(), 49164, Protocol::UDP))
|
|
||||||
add_port_mapping(urls, 49164, 49164, urls.lanaddr(), "RoutArm redir", 0, Protocol::UDP);
|
|
||||||
|
|
||||||
//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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
43
src/redirection.hpp
Normal file
43
src/redirection.hpp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/* Copyright 2016, Michele Santullo
|
||||||
|
* This file is part of "keepupnpup".
|
||||||
|
*
|
||||||
|
* "keepupnpup" is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* "keepupnpup" is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with "keepupnpup". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "enum.h"
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace kuu {
|
||||||
|
BETTER_ENUM (Protocol, uint8_t,
|
||||||
|
UDP,
|
||||||
|
TCP,
|
||||||
|
Other
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Redirection {
|
||||||
|
Redirection() : protocol(Protocol::Other) {}
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
} //namespace kuu
|
330
src/upnp.cpp
Normal file
330
src/upnp.cpp
Normal file
|
@ -0,0 +1,330 @@
|
||||||
|
/* Copyright 2016, Michele Santullo
|
||||||
|
* This file is part of "keepupnpup".
|
||||||
|
*
|
||||||
|
* "keepupnpup" is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* "keepupnpup" is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with "keepupnpup". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "upnp.hpp"
|
||||||
|
#include "miniupnpc.h"
|
||||||
|
#include "upnpcommands.h"
|
||||||
|
#include "upnperrors.h"
|
||||||
|
#include <array>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
|
#include <ciso646>
|
||||||
|
#include <strings.h>
|
||||||
|
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
# define KUU_VERBOSE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(KUU_VERBOSE)
|
||||||
|
# include <iostream>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace kuu {
|
||||||
|
namespace {
|
||||||
|
struct UPNPUrlsWithInitFlag {
|
||||||
|
UPNPUrlsWithInitFlag() : initialized(false) {}
|
||||||
|
struct UPNPUrls urls;
|
||||||
|
bool initialized;
|
||||||
|
|
||||||
|
operator struct UPNPUrls* () { return &urls; }
|
||||||
|
UPNPUrlsWithInitFlag* operator-> () { return this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
//see: http://stackoverflow.com/questions/24611215/one-liner-for-raii-on-non-pointer
|
||||||
|
struct UPNPUrlDeleter {
|
||||||
|
typedef UPNPUrlsWithInitFlag pointer;
|
||||||
|
void operator() (UPNPUrlsWithInitFlag& parUrls) {
|
||||||
|
if (parUrls.initialized) {
|
||||||
|
parUrls.initialized = false;
|
||||||
|
FreeUPNPUrls(&parUrls.urls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using UPNPUrlsPtr = std::unique_ptr<UPNPUrlsWithInitFlag, UPNPUrlDeleter>;
|
||||||
|
using UPNPDevsPtr = std::unique_ptr<struct UPNPDev, void(*)(struct UPNPDev*)>;
|
||||||
|
|
||||||
|
std::vector<struct UPNPDev*> dev_list_to_vector (struct UPNPDev* parList) {
|
||||||
|
std::vector<struct UPNPDev*> retval;
|
||||||
|
for (auto dev = parList; dev; dev = dev->pNext) {
|
||||||
|
retval.push_back(dev);
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string build_exception_msg (int parError, const char* parMessageIntro, const char* parMessage) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << parMessageIntro << " (" << parError << ")";
|
||||||
|
if (parMessage)
|
||||||
|
oss << ": \"" << parMessage << '"';
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(KUU_VERBOSE)
|
||||||
|
void print_devices (
|
||||||
|
const std::vector<struct UPNPDev*>& parDevs,
|
||||||
|
struct UPNPUrls* parUrls,
|
||||||
|
UPNP::IGDReply parIGDReply,
|
||||||
|
const std::string& parLanAddr,
|
||||||
|
const std::string& parExternalAddr
|
||||||
|
) {
|
||||||
|
std::cout << "Found: " << parDevs.front()->descURL << ", " << parDevs.front()->st << '\n';
|
||||||
|
|
||||||
|
switch (parIGDReply) {
|
||||||
|
case UPNP::IGDValid:
|
||||||
|
std::cout << "Found valid IGD: " << parUrls->controlURL << '\n';
|
||||||
|
break;
|
||||||
|
case UPNP::IGDNotConnected:
|
||||||
|
std::cout << "Found a (not connected?) IGD: " << parUrls->controlURL << '\n';
|
||||||
|
break;
|
||||||
|
case UPNP::IGDUpnpNotSureIGD:
|
||||||
|
std::cout << "UPnP device found. Is it an IGD? " << parUrls->controlURL << '\n';
|
||||||
|
break;
|
||||||
|
case UPNP::IGDNotSure:
|
||||||
|
std::cout << "Found device (igd?): " << parUrls->controlURL << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Local LAN ip address: " << parLanAddr << '\n';
|
||||||
|
std::cout << "External ip address: " << parExternalAddr << '\n';
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} //unnamed namespace
|
||||||
|
|
||||||
|
struct UPNP::LocalData {
|
||||||
|
|
||||||
|
LocalData() :
|
||||||
|
lanaddr(),
|
||||||
|
externaladdr(),
|
||||||
|
igd_reply(IGDNone),
|
||||||
|
devlist(nullptr, &freeUPNPDevlist)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string lanaddr;
|
||||||
|
std::string externaladdr;
|
||||||
|
struct IGDdatas data;
|
||||||
|
IGDReply igd_reply;
|
||||||
|
UPNPDevsPtr devlist;
|
||||||
|
UPNPUrlsPtr urls;
|
||||||
|
};
|
||||||
|
|
||||||
|
UPNP::UPNP() :
|
||||||
|
m_local_data(std::make_unique<LocalData>())
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
UPNPDevsPtr devlist(
|
||||||
|
upnpDiscover(
|
||||||
|
2000,
|
||||||
|
nullptr, //multicastif
|
||||||
|
nullptr, //minissdpdpath
|
||||||
|
UPNP_LOCAL_PORT_ANY, //localport
|
||||||
|
0, //ipv6
|
||||||
|
2, //ttl
|
||||||
|
&error
|
||||||
|
),
|
||||||
|
&freeUPNPDevlist
|
||||||
|
);
|
||||||
|
if (error)
|
||||||
|
throw UPNPException(error, "Error initializing upnpc in upnpDiscover()", nullptr);
|
||||||
|
|
||||||
|
const auto devs = dev_list_to_vector(devlist.get());
|
||||||
|
if (devs.empty())
|
||||||
|
throw ScanException("No UPNP devices found");
|
||||||
|
else if (devs.size() > 1)
|
||||||
|
throw ScanException("More than one UPNP device found (" + std::to_string(devs.size()) + ")");
|
||||||
|
|
||||||
|
UPNPUrlsPtr urls;
|
||||||
|
{
|
||||||
|
std::array<char, 64> lanaddr;
|
||||||
|
const auto i = UPNP_GetValidIGD(
|
||||||
|
devlist.get(),
|
||||||
|
urls.get(),
|
||||||
|
&m_local_data->data,
|
||||||
|
lanaddr.data(),
|
||||||
|
lanaddr.size()
|
||||||
|
);
|
||||||
|
|
||||||
|
switch (i) {
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
m_local_data->igd_reply = static_cast<IGDReply>(i);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
m_local_data->igd_reply = IGDNotSure;
|
||||||
|
}
|
||||||
|
urls->initialized = true;
|
||||||
|
|
||||||
|
m_local_data->lanaddr = lanaddr.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::array<char, 64> externaladdr;
|
||||||
|
const auto r = UPNP_GetExternalIPAddress(urls->urls.controlURL, m_local_data->data.first.servicetype, externaladdr.data());
|
||||||
|
if (UPNPCOMMAND_SUCCESS != r)
|
||||||
|
m_local_data->externaladdr = externaladdr.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(KUU_VERBOSE)
|
||||||
|
print_devices(devs, urls.get(), m_local_data->igd_reply, m_local_data->lanaddr, m_local_data->externaladdr);
|
||||||
|
#endif
|
||||||
|
m_local_data->urls.swap(urls);
|
||||||
|
m_local_data->devlist.swap(devlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
UPNP::~UPNP() noexcept = default;
|
||||||
|
|
||||||
|
UPNP::IGDReply UPNP::igd_reply() const {
|
||||||
|
return m_local_data->igd_reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& UPNP::lanaddr() const {
|
||||||
|
return m_local_data->lanaddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& UPNP::externaladdr() const {
|
||||||
|
return m_local_data->externaladdr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Redirection> UPNP::redirections() const {
|
||||||
|
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<Redirection> 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(
|
||||||
|
m_local_data->urls->urls.controlURL,
|
||||||
|
m_local_data->data.first.servicetype,
|
||||||
|
std::to_string(z).c_str(),
|
||||||
|
extPort,
|
||||||
|
intClient,
|
||||||
|
intPort,
|
||||||
|
protocol,
|
||||||
|
desc,
|
||||||
|
enabled,
|
||||||
|
rHost,
|
||||||
|
duration
|
||||||
|
);
|
||||||
|
|
||||||
|
if (not r) {
|
||||||
|
Redirection redir;
|
||||||
|
better_enums::optional<Protocol> proto = Protocol::_from_string_nothrow(protocol);
|
||||||
|
redir.protocol = (proto ? *proto : +Protocol::Other);
|
||||||
|
redir.internal_client = intClient;
|
||||||
|
redir.remote_host = rHost;
|
||||||
|
redir.desc = desc;
|
||||||
|
redir.duration = static_cast<uint32_t>(stoul(std::string(duration)));
|
||||||
|
redir.internal_port = static_cast<uint16_t>(stoul(std::string(intPort)));
|
||||||
|
redir.external_port = static_cast<uint16_t>(stoul(std::string(extPort)));
|
||||||
|
redir.enabled = is_enabled(std::string(enabled));
|
||||||
|
|
||||||
|
retval.push_back(redir);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw UPNPException(r, "Error in invocation of UPNP_GetGenericPortMappingEntry()", strupnperror(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
++z;
|
||||||
|
} while (not r);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPNP::add_port_mapping (
|
||||||
|
uint16_t parExtPort,
|
||||||
|
uint16_t parIntPort,
|
||||||
|
const std::string& parAddr,
|
||||||
|
const std::string& parDesc,
|
||||||
|
uint32_t parDuration,
|
||||||
|
Protocol parProtocol
|
||||||
|
) {
|
||||||
|
const auto r = UPNP_AddPortMapping(
|
||||||
|
m_local_data->urls->urls.controlURL,
|
||||||
|
m_local_data->data.first.servicetype,
|
||||||
|
std::to_string(parExtPort).c_str(),
|
||||||
|
std::to_string(parIntPort).c_str(),
|
||||||
|
parAddr.c_str(),
|
||||||
|
parDesc.c_str(),
|
||||||
|
parProtocol._to_string(),
|
||||||
|
0,
|
||||||
|
std::to_string(parDuration).c_str()
|
||||||
|
);
|
||||||
|
if (UPNPCOMMAND_SUCCESS != r) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Error in UPNP_AddPortMapping(\"" <<
|
||||||
|
m_local_data->urls->urls.controlURL << "\", \"" <<
|
||||||
|
m_local_data->data.first.servicetype << "\", " <<
|
||||||
|
parExtPort << ", " <<
|
||||||
|
parIntPort << ", \"" <<
|
||||||
|
parAddr << "\", \"" <<
|
||||||
|
parDesc << "\", " <<
|
||||||
|
parProtocol._to_string() << ")";
|
||||||
|
throw UPNPException(r, oss.str().c_str(), strupnperror(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UPNPException::UPNPException (int parError, const char* parMessageIntro, const char* parMessage) :
|
||||||
|
Exception(build_exception_msg(parError, parMessageIntro, parMessage)),
|
||||||
|
m_error(parError)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanException::ScanException (const std::string& parMessage) :
|
||||||
|
Exception(parMessage)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanException::ScanException (const char* parMessage) :
|
||||||
|
Exception(parMessage)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
} //namespace kuu
|
83
src/upnp.hpp
Normal file
83
src/upnp.hpp
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/* Copyright 2016, Michele Santullo
|
||||||
|
* This file is part of "keepupnpup".
|
||||||
|
*
|
||||||
|
* "keepupnpup" is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* "keepupnpup" is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with "keepupnpup". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "redirection.hpp"
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace kuu {
|
||||||
|
class UPNP {
|
||||||
|
public:
|
||||||
|
enum IGDReply {
|
||||||
|
IGDValid = 1,
|
||||||
|
IGDNotConnected = 2,
|
||||||
|
IGDUpnpNotSureIGD = 3,
|
||||||
|
IGDNotSure,
|
||||||
|
IGDNone
|
||||||
|
};
|
||||||
|
|
||||||
|
UPNP();
|
||||||
|
~UPNP() noexcept;
|
||||||
|
|
||||||
|
IGDReply igd_reply() const;
|
||||||
|
const std::string& lanaddr() const;
|
||||||
|
const std::string& externaladdr() const;
|
||||||
|
std::vector<Redirection> redirections() const;
|
||||||
|
|
||||||
|
void add_port_mapping (
|
||||||
|
uint16_t parExtPort,
|
||||||
|
uint16_t parIntPort,
|
||||||
|
const std::string& parAddr,
|
||||||
|
const std::string& parDesc,
|
||||||
|
uint32_t parDuration,
|
||||||
|
Protocol parProtocol
|
||||||
|
);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct LocalData;
|
||||||
|
|
||||||
|
std::unique_ptr<LocalData> m_local_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Exception : public std::runtime_error {
|
||||||
|
protected:
|
||||||
|
explicit Exception (const char* parMessage) : std::runtime_error(parMessage) {}
|
||||||
|
explicit Exception (const std::string& parMessage) : std::runtime_error(parMessage) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class UPNPException : public Exception {
|
||||||
|
public:
|
||||||
|
UPNPException (int parError, const char* parMessageIntro, const char* parMessage);
|
||||||
|
virtual ~UPNPException() noexcept = default;
|
||||||
|
|
||||||
|
int error_code() const { return m_error; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScanException : public Exception {
|
||||||
|
public:
|
||||||
|
explicit ScanException (const char* parMessage);
|
||||||
|
explicit ScanException (const std::string& parMessage);
|
||||||
|
};
|
||||||
|
} //namespace kuu
|
Loading…
Add table
Reference in a new issue