mirror of
https://git.jami.net/savoirfairelinux/jami-daemon.git
synced 2025-08-12 22:09:25 +08:00
Revert "NAT-PMP: add initial support"
This reverts commit 150035a819
.
reasons of the revert:
- doesn't link on Mac
- doesn't build on IOS
- build and link on linux, but the shared library is not found in rpath
Change-Id: Ie2eb1fe587adea1b607fde727abe6c641762495b
Notes: libnatpmp is not used on Android
This commit is contained in:
14
configure.ac
14
configure.ac
@ -531,20 +531,6 @@ AS_IF([test "x$with_upnp" = "xyes"],
|
||||
AC_DEFINE([HAVE_LIBUPNP], 0, [Define if you have libupnp])])
|
||||
])
|
||||
|
||||
# LIBNATPMP
|
||||
dnl check for libnatpmp
|
||||
AC_ARG_WITH([natpmp], [AS_HELP_STRING([--without-natpmp],
|
||||
[disable support for NAT-PMP])], [], [with_natpmp=yes])
|
||||
|
||||
AS_IF([test "x$with_natpmp" != xno],
|
||||
[AC_CHECK_HEADER([natpmp.h],
|
||||
AC_CHECK_LIB([natpmp], [initnatpmp], [], [with_natpmp=no]),
|
||||
[AC_MSG_WARN([Unable to find the libnatpmp headers (you may need to install the dev package). You may use --without-natpmp to compile without NAT-PMP support.]);
|
||||
with_natpmp=no])
|
||||
],[])
|
||||
|
||||
AC_DEFINE_UNQUOTED([HAVE_LIBNATPMP], `if test "x$with_natpmp" != xno; then echo 1; else echo 0; fi`, [Define if you have libnatpmp])
|
||||
|
||||
AC_DEFINE_UNQUOTED([HAVE_SHM], `if test -z "${HAVE_LINUX_TRUE}" && test -z "${HAVE_ANDROID_FALSE}" ; then echo 1; else echo 0; fi`,
|
||||
[Define if you have shared memory support])
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
e50b1f68ce9254bb2f068ddc37417a3c417b80f7b3fb3d84e3e9af4a144d89e204ab993b54c01657335e855d0124a8fcbbf96ce78db7b9ae0b03b6eb79de2e09 libnatpmp-20150609.tar.gz
|
@ -1,21 +0,0 @@
|
||||
# libnatpmp
|
||||
NATPMP_VERSION := 20150609
|
||||
NATPMP_URL := http://miniupnp.free.fr/files/download.php?file=libnatpmp-$(NATPMP_VERSION).tar.gz
|
||||
|
||||
PKGS += natpmp
|
||||
ifeq ($(call need_pkg,'libnatpmp'),)
|
||||
PKGS_FOUND += natpmp
|
||||
endif
|
||||
|
||||
$(TARBALLS)/libnatpmp-$(NATPMP_VERSION).tar.gz:
|
||||
$(call download,$(NATPMP_URL))
|
||||
|
||||
.sum-natpmp: libnatpmp-$(NATPMP_VERSION).tar.gz
|
||||
|
||||
natpmp: libnatpmp-$(NATPMP_VERSION).tar.gz .sum-natpmp
|
||||
$(UNPACK)
|
||||
$(MOVE)
|
||||
|
||||
.natpmp: natpmp
|
||||
cd $< && $(MAKE) INSTALLPREFIX="$(PREFIX)" install
|
||||
touch $@
|
@ -38,10 +38,6 @@
|
||||
#include <upnp/upnptools.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_LIBNATPMP
|
||||
#include <natpmp.h>
|
||||
#endif
|
||||
|
||||
#include "logger.h"
|
||||
#include "ip_utils.h"
|
||||
#include "upnp_igd.h"
|
||||
@ -65,20 +61,6 @@ getUPnPContext()
|
||||
return context;
|
||||
}
|
||||
|
||||
/* UPnP error codes */
|
||||
constexpr static int INVALID_ARGS = 402;
|
||||
constexpr static int ARRAY_IDX_INVALID = 713;
|
||||
constexpr static int CONFLICT_IN_MAPPING = 718;
|
||||
|
||||
/* max number of times to retry mapping if it fails due to conflict;
|
||||
* there isn't much logic in picking this number... ideally not many ports should
|
||||
* be mapped in a system, so a few number of random port retries should work;
|
||||
* a high number of retries would indicate there might be some kind of bug or else
|
||||
* incompatibility with the router; we use it to prevent an infinite loop of
|
||||
* retrying to map the entry
|
||||
*/
|
||||
constexpr static unsigned MAX_RETRIES = 20;
|
||||
|
||||
#if HAVE_LIBUPNP
|
||||
|
||||
/* UPnP IGD definitions */
|
||||
@ -89,10 +71,23 @@ constexpr static const char * UPNP_WANCON_DEVICE = "urn:schemas-upnp-org:device:
|
||||
constexpr static const char * UPNP_WANIP_SERVICE = "urn:schemas-upnp-org:service:WANIPConnection:1";
|
||||
constexpr static const char * UPNP_WANPPP_SERVICE = "urn:schemas-upnp-org:service:WANPPPConnection:1";
|
||||
|
||||
/* UPnP error codes */
|
||||
constexpr static int INVALID_ARGS = 402;
|
||||
constexpr static const char * INVALID_ARGS_STR = "402";
|
||||
constexpr static int ARRAY_IDX_INVALID = 713;
|
||||
constexpr static const char * ARRAY_IDX_INVALID_STR = "713";
|
||||
constexpr static int CONFLICT_IN_MAPPING = 718;
|
||||
constexpr static const char * CONFLICT_IN_MAPPING_STR = "718";
|
||||
|
||||
/* max number of times to retry mapping if it fails due to conflict;
|
||||
* there isn't much logic in picking this number... ideally not many ports should
|
||||
* be mapped in a system, so a few number of random port retries should work;
|
||||
* a high number of retries would indicate there might be some kind of bug or else
|
||||
* incompatibility with the router; we use it to prevent an infinite loop of
|
||||
* retrying to map the entry
|
||||
*/
|
||||
constexpr static unsigned MAX_RETRIES = 20;
|
||||
|
||||
/*
|
||||
* Local prototypes
|
||||
*/
|
||||
@ -101,68 +96,18 @@ static std::string get_first_doc_item(IXML_Document*, const char*);
|
||||
static std::string get_first_element_item(IXML_Element*, const char*);
|
||||
static void checkResponseError(IXML_Document*);
|
||||
|
||||
#else
|
||||
static int
|
||||
cp_callback(Upnp_EventType event_type, void* event, void* user_data)
|
||||
{
|
||||
if (auto upnpContext = static_cast<UPnPContext*>(user_data))
|
||||
return upnpContext->handleUPnPEvents(event_type, event);
|
||||
|
||||
constexpr static int UPNP_E_SUCCESS = 0;
|
||||
|
||||
#endif // HAVE_LIBUPNP
|
||||
RING_WARN("UPnP callback without UPnPContext");
|
||||
return 0;
|
||||
}
|
||||
|
||||
UPnPContext::UPnPContext()
|
||||
{
|
||||
#if HAVE_LIBNATPMP
|
||||
pmpThread_ = std::thread([this]() {
|
||||
PMPIGD* pmp_igd = new PMPIGD();
|
||||
natpmp_t natpmp;
|
||||
bool found {false};
|
||||
|
||||
while (pmpRun_) {
|
||||
if (initnatpmp(&natpmp, 0, 0) < 0) {
|
||||
RING_ERR("NAT-PMP: can't initialize libnatpmp");
|
||||
std::unique_lock<std::mutex> lk(pmpMutex_);
|
||||
pmpCv_.wait_for(lk, std::chrono::minutes(1));
|
||||
} else {
|
||||
RING_DBG("NAT-PMP: initialized");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (pmpRun_) {
|
||||
std::unique_lock<std::mutex> lk(pmpMutex_);
|
||||
pmpCv_.wait_until(lk, pmp_igd->getRenewalTime(), [&] {
|
||||
return not pmpRun_ or pmp_igd->getRenewalTime() <= clock::now();
|
||||
});
|
||||
if (not pmpRun_) break;
|
||||
|
||||
auto now = clock::now();
|
||||
|
||||
if (pmp_igd->renewal_ < now) {
|
||||
PMPsearchForIGD(pmp_igd, natpmp, found);
|
||||
}
|
||||
if (found) {
|
||||
if (pmp_igd->clearAll_) {
|
||||
PMPdeleteAllPortMapping(*pmp_igd, natpmp, NATPMP_PROTOCOL_UDP);
|
||||
PMPdeleteAllPortMapping(*pmp_igd, natpmp, NATPMP_PROTOCOL_TCP);
|
||||
pmp_igd->clearAll_ = false;
|
||||
pmp_igd->toRemove_.clear();
|
||||
} else if (not pmp_igd->toRemove_.empty()) {
|
||||
for (auto& m : pmp_igd->toRemove_)
|
||||
PMPaddPortMapping(*pmp_igd, natpmp, m, true);
|
||||
pmp_igd->toRemove_.clear();
|
||||
}
|
||||
auto mapping = pmp_igd->getNextMappingToRenew();
|
||||
if (mapping and mapping->renewal_ < now)
|
||||
PMPaddPortMapping(*pmp_igd, natpmp, *mapping);
|
||||
}
|
||||
}
|
||||
if (not found) delete pmp_igd;
|
||||
closenatpmp(&natpmp);
|
||||
RING_DBG("NAT-PMP: ended");
|
||||
});
|
||||
clientRegistered_ = true;
|
||||
#endif
|
||||
|
||||
#if HAVE_LIBUPNP
|
||||
|
||||
int upnp_err;
|
||||
char* ip_address = nullptr;
|
||||
unsigned short port = 0;
|
||||
@ -210,39 +155,19 @@ UPnPContext::UPnPContext()
|
||||
* we will probably receive their advertisements either way
|
||||
*/
|
||||
searchForIGD();
|
||||
#endif
|
||||
}
|
||||
|
||||
UPnPContext::~UPnPContext()
|
||||
{
|
||||
/* make sure everything is unregistered, freed, and UpnpFinish() is called */
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(validIGDMutex_);
|
||||
for( auto const &it : validIGDs_) {
|
||||
#if HAVE_LIBUPNP
|
||||
if (auto igd = dynamic_cast<UPnPIGD*>(it.second.get()))
|
||||
removeMappingsByLocalIPAndDescription(*igd, Mapping::UPNP_DEFAULT_MAPPING_DESCRIPTION);
|
||||
#endif
|
||||
#if HAVE_LIBNATPMP
|
||||
if (auto pmp = dynamic_cast<PMPIGD*>(it.second.get())) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(pmpMutex_);
|
||||
pmp->clearAll();
|
||||
}
|
||||
pmpCv_.notify_all();
|
||||
}
|
||||
#endif
|
||||
removeMappingsByLocalIPAndDescription(it.second.get(), Mapping::UPNP_DEFAULT_MAPPING_DESCRIPTION);
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_LIBNATPMP
|
||||
pmpRun_ = false;
|
||||
pmpCv_.notify_all();
|
||||
if (pmpThread_.joinable())
|
||||
pmpThread_.join();
|
||||
#endif
|
||||
|
||||
#if HAVE_LIBUPNP
|
||||
if (clientRegistered_)
|
||||
UpnpUnRegisterClient( ctrlptHandle_ );
|
||||
|
||||
@ -252,7 +177,21 @@ UPnPContext::~UPnPContext()
|
||||
#ifndef _WIN32
|
||||
UpnpFinish();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
UPnPContext::searchForIGD()
|
||||
{
|
||||
if (not clientRegistered_) {
|
||||
RING_WARN("UPnP: Control Point not registered");
|
||||
return;
|
||||
}
|
||||
|
||||
/* send out search for both types, as some routers may possibly only reply to one */
|
||||
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_ROOT_DEVICE, this);
|
||||
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_IGD_DEVICE, this);
|
||||
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANIP_SERVICE, this);
|
||||
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANPPP_SERVICE, this);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -300,11 +239,7 @@ UPnPContext::chooseIGD_unlocked() const
|
||||
{
|
||||
if (validIGDs_.empty())
|
||||
return nullptr;
|
||||
auto natpmp = validIGDs_.find("NATPMP");
|
||||
if (natpmp == validIGDs_.end())
|
||||
return validIGDs_.begin()->second.get();
|
||||
else
|
||||
return natpmp->second.get();
|
||||
return validIGDs_.begin()->second.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -349,21 +284,11 @@ UPnPContext::addMapping(IGD* igd,
|
||||
}
|
||||
|
||||
/* mapping doesn't exist, so try to add it */
|
||||
RING_DBG("adding port mapping : %s", mapping.toString().c_str());
|
||||
RING_DBG("UPnP: adding port mapping : %s", mapping.toString().c_str());
|
||||
|
||||
#if HAVE_LIBUPNP
|
||||
auto upnp = dynamic_cast<const UPnPIGD*>(igd);
|
||||
if (not upnp or addPortMapping(*upnp, mapping, upnp_error))
|
||||
#endif
|
||||
{
|
||||
if(addPortMapping(igd, mapping, upnp_error)) {
|
||||
/* success; add it to global list */
|
||||
globalMappings->emplace(port_external, std::move(GlobalMapping{mapping}));
|
||||
#if HAVE_LIBNATPMP
|
||||
#if HAVE_LIBUPNP
|
||||
if (not upnp)
|
||||
#endif
|
||||
pmpCv_.notify_all();
|
||||
#endif
|
||||
return mapping;
|
||||
}
|
||||
return {};
|
||||
@ -379,17 +304,17 @@ generateRandomPort()
|
||||
/* define the range */
|
||||
static std::uniform_int_distribution<uint16_t> dist(Mapping::UPNP_PORT_MIN, Mapping::UPNP_PORT_MAX);
|
||||
|
||||
return dist(gen);
|
||||
return dist(gen);;
|
||||
}
|
||||
|
||||
/**
|
||||
* chooses a random port that is not yet used by the daemon for UPnP
|
||||
*/
|
||||
uint16_t
|
||||
UPnPContext::chooseRandomPort(const IGD& igd, PortType type)
|
||||
UPnPContext::chooseRandomPort(const IGD* igd, PortType type)
|
||||
{
|
||||
auto globalMappings = type == PortType::UDP ?
|
||||
&igd.udpMappings : &igd.tcpMappings;
|
||||
&igd->udpMappings : &igd->tcpMappings;
|
||||
|
||||
uint16_t port = generateRandomPort();
|
||||
|
||||
@ -438,7 +363,7 @@ UPnPContext::addAnyMapping(uint16_t port_desired,
|
||||
auto iter = globalMappings->find(port_desired);
|
||||
if (iter != globalMappings->end()) {
|
||||
/* port already used, we need a unique port */
|
||||
port_desired = chooseRandomPort(*igd, type);
|
||||
port_desired = chooseRandomPort(igd, type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -463,7 +388,7 @@ UPnPContext::addAnyMapping(uint16_t port_desired,
|
||||
RING_DBG("UPnP: mapping failed (conflicting entry? err = %d), trying with a different port.",
|
||||
upnp_error);
|
||||
/* TODO: make sure we don't try sellecting the same random port twice if it fails ? */
|
||||
port_desired = chooseRandomPort(*igd, type);
|
||||
port_desired = chooseRandomPort(igd, type);
|
||||
if (use_same_port)
|
||||
port_local = port_desired;
|
||||
mapping = addMapping(igd, port_desired, port_local, type, &upnp_error);
|
||||
@ -498,33 +423,21 @@ UPnPContext::removeMapping(const Mapping& mapping)
|
||||
auto iter = globalMappings->find(mapping.getPortExternal());
|
||||
if ( iter != globalMappings->end() ) {
|
||||
/* make sure its the same mapping */
|
||||
GlobalMapping& global_mapping = iter->second;
|
||||
if (mapping == global_mapping ) {
|
||||
GlobalMapping *global_mapping = &iter->second;
|
||||
if (mapping == *global_mapping ) {
|
||||
/* now check the users */
|
||||
if (global_mapping.users > 1) {
|
||||
if (global_mapping->users > 1) {
|
||||
/* more than one user, simply decrement the number */
|
||||
--(global_mapping.users);
|
||||
--(global_mapping->users);
|
||||
RING_DBG("UPnP: decrementing users of mapping: %s, %d users remaining",
|
||||
mapping.toString().c_str(), global_mapping.users);
|
||||
mapping.toString().c_str(), global_mapping->users);
|
||||
} else {
|
||||
/* no other users, can delete */
|
||||
RING_DBG("UPnP: removing port mapping : %s",
|
||||
mapping.toString().c_str());
|
||||
#if HAVE_LIBUPNP
|
||||
if (auto upnp = dynamic_cast<UPnPIGD*>(igd))
|
||||
deletePortMapping(*upnp,
|
||||
mapping.getPortExternalStr(),
|
||||
mapping.getTypeStr());
|
||||
#endif
|
||||
#if HAVE_LIBNATPMP
|
||||
if (auto pmp = dynamic_cast<PMPIGD*>(igd)) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(pmpMutex_);
|
||||
pmp->toRemove_.emplace_back(std::move(global_mapping));
|
||||
}
|
||||
pmpCv_.notify_all();
|
||||
}
|
||||
#endif
|
||||
deletePortMapping(igd,
|
||||
mapping.getPortExternalStr(),
|
||||
mapping.getTypeStr());
|
||||
globalMappings->erase(iter);
|
||||
}
|
||||
} else {
|
||||
@ -565,117 +478,6 @@ UPnPContext::getExternalIP() const
|
||||
return {};
|
||||
}
|
||||
|
||||
#if HAVE_LIBNATPMP
|
||||
|
||||
void
|
||||
UPnPContext::PMPsearchForIGD(PMPIGD* pmp_igd, natpmp_t& natpmp, bool& found)
|
||||
{
|
||||
if (sendpublicaddressrequest(&natpmp) < 0) {
|
||||
RING_ERR("NAT-PMP: can't send request");
|
||||
pmp_igd->renewal_ = clock::now() + std::chrono::minutes(1);
|
||||
return;
|
||||
}
|
||||
|
||||
while (pmpRun_) {
|
||||
natpmpresp_t response;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
||||
auto r = readnatpmpresponseorretry(&natpmp, &response);
|
||||
if (r < 0 && r != NATPMP_TRYAGAIN) {
|
||||
RING_WARN("NAT-PMP: can't find device");
|
||||
pmp_igd->renewal_ = clock::now() + std::chrono::minutes(5);
|
||||
break;
|
||||
}
|
||||
else if (r != NATPMP_TRYAGAIN) {
|
||||
pmp_igd->localIp = ip_utils::getLocalAddr(AF_INET);
|
||||
pmp_igd->publicIp = IpAddr(response.pnu.publicaddress.addr);
|
||||
if (not found) {
|
||||
found = true;
|
||||
RING_DBG("NAT-PMP: found new device");
|
||||
RING_DBG("NAT-PMP: got external IP: %s", pmp_igd->publicIp.toString().c_str());
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(validIGDMutex_);
|
||||
validIGDs_.emplace("NATPMP", std::unique_ptr<IGD>(pmp_igd));
|
||||
validIGDCondVar_.notify_all();
|
||||
for (const auto& l : igdListeners_)
|
||||
l.second();
|
||||
}
|
||||
}
|
||||
pmp_igd->renewal_ = clock::now() + std::chrono::minutes(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
UPnPContext::PMPaddPortMapping(const PMPIGD& /*pmp_igd*/, natpmp_t& natpmp, GlobalMapping& mapping, bool remove) const
|
||||
{
|
||||
if (sendnewportmappingrequest(&natpmp,
|
||||
mapping.getType() == PortType::UDP ? NATPMP_PROTOCOL_UDP : NATPMP_PROTOCOL_TCP,
|
||||
mapping.getPortInternal(),
|
||||
mapping.getPortExternal(), remove ? 0 : 3600) < 0) {
|
||||
RING_ERR("NAT-PMP: can't send port mapping request");
|
||||
mapping.renewal_ = clock::now() + std::chrono::minutes(1);
|
||||
return;
|
||||
}
|
||||
RING_DBG("NAT-PMP: sent port mapping %srequest", remove ? "removal " : "");
|
||||
while (pmpRun_) {
|
||||
natpmpresp_t response;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
||||
auto r = readnatpmpresponseorretry(&natpmp, &response);
|
||||
if (r < 0 && r != NATPMP_TRYAGAIN) {
|
||||
RING_ERR("NAT-PMP: can't %sregister port mapping", remove ? "un" : "");
|
||||
break;
|
||||
}
|
||||
else if (r != NATPMP_TRYAGAIN) {
|
||||
mapping.renewal_ = clock::now()
|
||||
+ std::chrono::seconds(response.pnu.newportmapping.lifetime/2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
UPnPContext::PMPdeleteAllPortMapping(const PMPIGD& /*pmp_igd*/, natpmp_t& natpmp, int proto) const
|
||||
{
|
||||
if (sendnewportmappingrequest(&natpmp, proto, 0, 0, 0) < 0) {
|
||||
RING_ERR("NAT-PMP: can't send all port mapping removal request");
|
||||
return;
|
||||
}
|
||||
RING_DBG("NAT-PMP: sent all port mapping removal request");
|
||||
while (pmpRun_) {
|
||||
natpmpresp_t response;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
||||
auto r = readnatpmpresponseorretry(&natpmp, &response);
|
||||
if (r < 0 && r != NATPMP_TRYAGAIN) {
|
||||
RING_ERR("NAT-PMP: can't remove all port mappings");
|
||||
break;
|
||||
}
|
||||
else if (r != NATPMP_TRYAGAIN) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_LIBNATPMP */
|
||||
|
||||
|
||||
#if HAVE_LIBUPNP
|
||||
|
||||
void
|
||||
UPnPContext::searchForIGD()
|
||||
{
|
||||
if (not clientRegistered_) {
|
||||
RING_WARN("UPnP: Control Point not registered");
|
||||
return;
|
||||
}
|
||||
|
||||
/* send out search for both types, as some routers may possibly only reply to one */
|
||||
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_ROOT_DEVICE, this);
|
||||
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_IGD_DEVICE, this);
|
||||
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANIP_SERVICE, this);
|
||||
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANPPP_SERVICE, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the device description and adds desired devices to
|
||||
* relevant lists
|
||||
@ -726,7 +528,7 @@ UPnPContext::parseIGD(IXML_Document* doc, const Upnp_Discovery* d_event)
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<UPnPIGD> new_igd;
|
||||
std::unique_ptr<IGD> new_igd;
|
||||
int upnp_err;
|
||||
|
||||
std::string friendlyName = get_first_doc_item(doc, "friendlyName");
|
||||
@ -814,9 +616,9 @@ UPnPContext::parseIGD(IXML_Document* doc, const Upnp_Discovery* d_event)
|
||||
if (not (serviceId.empty() and controlURL.empty() and eventSubURL.empty()) ) {
|
||||
/* RING_DBG("UPnP: got service info from device:\n\tserviceType: %s\n\tserviceID: %s\n\tcontrolURL: %s\n\teventSubURL: %s",
|
||||
serviceType.c_str(), serviceId.c_str(), controlURL.c_str(), eventSubURL.c_str()); */
|
||||
new_igd.reset(new UPnPIGD(UDN, baseURL, friendlyName, serviceType, serviceId, controlURL, eventSubURL));
|
||||
if (isIGDConnected(*new_igd)) {
|
||||
new_igd->publicIp = getExternalIP(*new_igd);
|
||||
new_igd.reset(new IGD(UDN, baseURL, friendlyName, serviceType, serviceId, controlURL, eventSubURL));
|
||||
if (isIGDConnected(new_igd.get())) {
|
||||
new_igd->publicIp = getExternalIP(new_igd.get());
|
||||
if (new_igd->publicIp) {
|
||||
RING_DBG("UPnP: got external IP: %s", new_igd->publicIp.toString().c_str());
|
||||
new_igd->localIp = ip_utils::getLocalAddr(pj_AF_INET());
|
||||
@ -844,7 +646,7 @@ UPnPContext::parseIGD(IXML_Document* doc, const Upnp_Discovery* d_event)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(validIGDMutex_);
|
||||
/* delete all RING mappings first */
|
||||
removeMappingsByLocalIPAndDescription(*new_igd, Mapping::UPNP_DEFAULT_MAPPING_DESCRIPTION);
|
||||
removeMappingsByLocalIPAndDescription(new_igd.get(), Mapping::UPNP_DEFAULT_MAPPING_DESCRIPTION);
|
||||
validIGDs_.emplace(UDN, std::move(new_igd));
|
||||
validIGDCondVar_.notify_all();
|
||||
for (const auto& l : igdListeners_)
|
||||
@ -895,17 +697,6 @@ get_first_element_item(IXML_Element* element, const char* item)
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
UPnPContext::cp_callback(Upnp_EventType event_type, void* event, void* user_data)
|
||||
{
|
||||
if (auto upnpContext = static_cast<UPnPContext*>(user_data))
|
||||
return upnpContext->handleUPnPEvents(event_type, event);
|
||||
|
||||
RING_WARN("UPnP callback without UPnPContext");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
UPnPContext::handleUPnPEvents(Upnp_EventType event_type, void* event)
|
||||
{
|
||||
@ -1075,22 +866,22 @@ checkResponseError(IXML_Document* doc)
|
||||
}
|
||||
|
||||
bool
|
||||
UPnPContext::isIGDConnected(const UPnPIGD& igd)
|
||||
UPnPContext::isIGDConnected(const IGD* igd)
|
||||
{
|
||||
bool connected = false;
|
||||
std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> action(nullptr, ixmlDocument_free);
|
||||
action.reset(UpnpMakeAction("GetStatusInfo", igd.getServiceType().c_str(), 0, nullptr));
|
||||
action.reset(UpnpMakeAction("GetStatusInfo", igd->getServiceType().c_str(), 0, nullptr));
|
||||
|
||||
std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> response(nullptr, ixmlDocument_free);
|
||||
IXML_Document* response_ptr = nullptr;
|
||||
int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(),
|
||||
igd.getServiceType().c_str(), nullptr, action.get(), &response_ptr);
|
||||
int upnp_err = UpnpSendAction(ctrlptHandle_, igd->getControlURL().c_str(),
|
||||
igd->getServiceType().c_str(), nullptr, action.get(), &response_ptr);
|
||||
response.reset(response_ptr);
|
||||
checkResponseError(response.get());
|
||||
if( upnp_err != UPNP_E_SUCCESS) {
|
||||
/* TODO: if failed, should we chck if the igd is disconnected? */
|
||||
RING_WARN("UPnP: Failed to get GetStatusInfo from: %s, %d: %s",
|
||||
igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
|
||||
igd->getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -1108,21 +899,21 @@ UPnPContext::isIGDConnected(const UPnPIGD& igd)
|
||||
}
|
||||
|
||||
IpAddr
|
||||
UPnPContext::getExternalIP(const UPnPIGD& igd)
|
||||
UPnPContext::getExternalIP(const IGD* igd)
|
||||
{
|
||||
std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> action(nullptr, ixmlDocument_free);
|
||||
action.reset(UpnpMakeAction("GetExternalIPAddress", igd.getServiceType().c_str(), 0, nullptr));
|
||||
action.reset(UpnpMakeAction("GetExternalIPAddress", igd->getServiceType().c_str(), 0, nullptr));
|
||||
|
||||
std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> response(nullptr, ixmlDocument_free);
|
||||
IXML_Document* response_ptr = nullptr;
|
||||
int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(),
|
||||
igd.getServiceType().c_str(), nullptr, action.get(), &response_ptr);
|
||||
int upnp_err = UpnpSendAction(ctrlptHandle_, igd->getControlURL().c_str(),
|
||||
igd->getServiceType().c_str(), nullptr, action.get(), &response_ptr);
|
||||
response.reset(response_ptr);
|
||||
checkResponseError(response.get());
|
||||
if( upnp_err != UPNP_E_SUCCESS) {
|
||||
/* TODO: if failed, should we chck if the igd is disconnected? */
|
||||
RING_WARN("UPnP: Failed to get GetExternalIPAddress from: %s, %d: %s",
|
||||
igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
|
||||
igd->getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -1131,15 +922,15 @@ UPnPContext::getExternalIP(const UPnPIGD& igd)
|
||||
}
|
||||
|
||||
void
|
||||
UPnPContext::removeMappingsByLocalIPAndDescription(const UPnPIGD& igd, const std::string& description)
|
||||
UPnPContext::removeMappingsByLocalIPAndDescription(const IGD* igd, const std::string& description)
|
||||
{
|
||||
if (!igd.localIp) {
|
||||
if (!igd->localIp) {
|
||||
RING_DBG("UPnP: cannot determine local IP in function removeMappingsByLocalIPAndDescription()");
|
||||
return;
|
||||
}
|
||||
|
||||
RING_DBG("UPnP: removing all port mappings with description: \"%s\" and local ip: %s",
|
||||
description.c_str(), igd.localIp.toString().c_str());
|
||||
description.c_str(), igd->localIp.toString().c_str());
|
||||
|
||||
int entry_idx = 0;
|
||||
bool done = false;
|
||||
@ -1147,19 +938,19 @@ UPnPContext::removeMappingsByLocalIPAndDescription(const UPnPIGD& igd, const std
|
||||
do {
|
||||
std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> action(nullptr, ixmlDocument_free);
|
||||
IXML_Document* action_ptr = nullptr;
|
||||
UpnpAddToAction(&action_ptr, "GetGenericPortMappingEntry", igd.getServiceType().c_str(),
|
||||
UpnpAddToAction(&action_ptr, "GetGenericPortMappingEntry", igd->getServiceType().c_str(),
|
||||
"NewPortMappingIndex", ring::to_string(entry_idx).c_str());
|
||||
action.reset(action_ptr);
|
||||
|
||||
std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> response(nullptr, ixmlDocument_free);
|
||||
IXML_Document* response_ptr = nullptr;
|
||||
int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(),
|
||||
igd.getServiceType().c_str(), nullptr, action.get(), &response_ptr);
|
||||
int upnp_err = UpnpSendAction(ctrlptHandle_, igd->getControlURL().c_str(),
|
||||
igd->getServiceType().c_str(), nullptr, action.get(), &response_ptr);
|
||||
response.reset(response_ptr);
|
||||
if( not response and upnp_err != UPNP_E_SUCCESS) {
|
||||
/* TODO: if failed, should we chck if the igd is disconnected? */
|
||||
RING_WARN("UPnP: Failed to get GetGenericPortMappingEntry from: %s, %d: %s",
|
||||
igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
|
||||
igd->getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1172,7 +963,7 @@ UPnPContext::removeMappingsByLocalIPAndDescription(const UPnPIGD& igd, const std
|
||||
std::string client_ip = get_first_doc_item(response.get(), "NewInternalClient");
|
||||
|
||||
/* check if same IP and description */
|
||||
if (IpAddr(client_ip) == igd.localIp and desc_actual.compare(description) == 0) {
|
||||
if (IpAddr(client_ip) == igd->localIp and desc_actual.compare(description) == 0) {
|
||||
/* get the rest of the needed parameters */
|
||||
std::string port_internal = get_first_doc_item(response.get(), "NewInternalPort");
|
||||
std::string port_external = get_first_doc_item(response.get(), "NewExternalPort");
|
||||
@ -1205,28 +996,28 @@ UPnPContext::removeMappingsByLocalIPAndDescription(const UPnPIGD& igd, const std
|
||||
}
|
||||
|
||||
bool
|
||||
UPnPContext::deletePortMapping(const UPnPIGD& igd, const std::string& port_external, const std::string& protocol)
|
||||
UPnPContext::deletePortMapping(const IGD* igd, const std::string& port_external, const std::string& protocol)
|
||||
{
|
||||
std::string action_name{"DeletePortMapping"};
|
||||
std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> action(nullptr, ixmlDocument_free);
|
||||
IXML_Document* action_ptr = nullptr;
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
|
||||
"NewRemoteHost", "");
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
|
||||
"NewExternalPort", port_external.c_str());
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
|
||||
"NewProtocol", protocol.c_str());
|
||||
action.reset(action_ptr);
|
||||
|
||||
std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> response(nullptr, ixmlDocument_free);
|
||||
IXML_Document* response_ptr = nullptr;
|
||||
int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(),
|
||||
igd.getServiceType().c_str(), nullptr, action.get(), &response_ptr);
|
||||
int upnp_err = UpnpSendAction(ctrlptHandle_, igd->getControlURL().c_str(),
|
||||
igd->getServiceType().c_str(), nullptr, action.get(), &response_ptr);
|
||||
response.reset(response_ptr);
|
||||
if( upnp_err != UPNP_E_SUCCESS) {
|
||||
/* TODO: if failed, should we check if the igd is disconnected? */
|
||||
RING_WARN("UPnP: Failed to get %s from: %s, %d: %s", action_name.c_str(),
|
||||
igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
|
||||
igd->getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
|
||||
return false;
|
||||
}
|
||||
/* check if there is an error code */
|
||||
@ -1241,41 +1032,41 @@ UPnPContext::deletePortMapping(const UPnPIGD& igd, const std::string& port_exter
|
||||
}
|
||||
|
||||
bool
|
||||
UPnPContext::addPortMapping(const UPnPIGD& igd, const Mapping& mapping, int* error_code)
|
||||
UPnPContext::addPortMapping(const IGD* igd, const Mapping& mapping, int* error_code)
|
||||
{
|
||||
*error_code = UPNP_E_SUCCESS;
|
||||
|
||||
std::string action_name{"AddPortMapping"};
|
||||
std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> action(nullptr, ixmlDocument_free);
|
||||
IXML_Document* action_ptr = nullptr;
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
|
||||
"NewRemoteHost", "");
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
|
||||
"NewExternalPort", mapping.getPortExternalStr().c_str());
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
|
||||
"NewProtocol", mapping.getTypeStr().c_str());
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
|
||||
"NewInternalPort", mapping.getPortInternalStr().c_str());
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
|
||||
"NewInternalClient", igd.localIp.toString().c_str());
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
|
||||
"NewInternalClient", igd->localIp.toString().c_str());
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
|
||||
"NewEnabled", "1");
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
|
||||
"NewPortMappingDescription", mapping.getDescription().c_str());
|
||||
/* for now assume lease duration is always infinite */
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
|
||||
UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
|
||||
"NewLeaseDuration", "0");
|
||||
action.reset(action_ptr);
|
||||
|
||||
std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> response(nullptr, ixmlDocument_free);
|
||||
IXML_Document* response_ptr = nullptr;
|
||||
int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(),
|
||||
igd.getServiceType().c_str(), nullptr, action.get(), &response_ptr);
|
||||
int upnp_err = UpnpSendAction(ctrlptHandle_, igd->getControlURL().c_str(),
|
||||
igd->getServiceType().c_str(), nullptr, action.get(), &response_ptr);
|
||||
response.reset(response_ptr);
|
||||
if( not response and upnp_err != UPNP_E_SUCCESS) {
|
||||
/* TODO: if failed, should we chck if the igd is disconnected? */
|
||||
RING_WARN("UPnP: Failed to %s from: %s, %d: %s", action_name.c_str(),
|
||||
igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
|
||||
igd->getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
|
||||
*error_code = -1; /* make sure to -1 since we didn't get a response */
|
||||
return false;
|
||||
}
|
||||
|
@ -18,7 +18,8 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef UPNP_CONTEXT_H_
|
||||
#define UPNP_CONTEXT_H_
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
@ -31,7 +32,6 @@
|
||||
#include <condition_variable>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#if HAVE_LIBUPNP
|
||||
#ifdef _WIN32
|
||||
@ -43,10 +43,6 @@
|
||||
#include <upnp/upnptools.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_LIBNATPMP
|
||||
#include <natpmp.h>
|
||||
#endif
|
||||
|
||||
#include "noncopyable.h"
|
||||
#include "upnp_igd.h"
|
||||
|
||||
@ -60,6 +56,7 @@ class UPnPContext {
|
||||
public:
|
||||
constexpr static unsigned SEARCH_TIMEOUT {30};
|
||||
|
||||
#if HAVE_LIBUPNP
|
||||
UPnPContext();
|
||||
~UPnPContext();
|
||||
|
||||
@ -107,63 +104,21 @@ public:
|
||||
*/
|
||||
IpAddr getLocalIP() const;
|
||||
|
||||
/**
|
||||
* callback function for the UPnP client (control point)
|
||||
* all UPnP events received by the client are processed here
|
||||
*/
|
||||
int handleUPnPEvents(Upnp_EventType event_type, void* event);
|
||||
|
||||
#else
|
||||
/* use default constructor and destructor */
|
||||
UPnPContext() = default;
|
||||
~UPnPContext() = default;
|
||||
#endif
|
||||
|
||||
private:
|
||||
NON_COPYABLE(UPnPContext);
|
||||
|
||||
std::atomic_bool clientRegistered_ {false};
|
||||
|
||||
/**
|
||||
* map of valid IGDs - IGDs which have the correct services and are connected
|
||||
* to some external network (have an external IP)
|
||||
*
|
||||
* the UDN string is used to uniquely identify the IGD
|
||||
*
|
||||
* the mutex is used to access these lists and IGDs in a thread-safe manner
|
||||
*/
|
||||
std::map<std::string, std::unique_ptr<IGD>> validIGDs_;
|
||||
mutable std::mutex validIGDMutex_;
|
||||
std::condition_variable validIGDCondVar_;
|
||||
|
||||
/**
|
||||
* Map of valid IGD listeners.
|
||||
*/
|
||||
std::map<size_t, IGDFoundCallback> igdListeners_;
|
||||
|
||||
/**
|
||||
* Last provided token for valid IGD listeners.
|
||||
* 0 is the invalid token.
|
||||
*/
|
||||
size_t listenerToken_ {0};
|
||||
|
||||
/**
|
||||
* chooses the IGD to use (currently selects the first one in the map)
|
||||
* assumes you already have a lock on igd_mutex_
|
||||
*/
|
||||
IGD* chooseIGD_unlocked() const;
|
||||
|
||||
|
||||
/* tries to add mapping, assumes you alreayd have lock on igd_mutex_ */
|
||||
Mapping addMapping(IGD* igd,
|
||||
uint16_t port_external,
|
||||
uint16_t port_internal,
|
||||
PortType type,
|
||||
int *upnp_error);
|
||||
|
||||
uint16_t chooseRandomPort(const IGD& igd, PortType type);
|
||||
|
||||
#if HAVE_LIBNATPMP
|
||||
|
||||
std::thread pmpThread_;
|
||||
std::mutex pmpMutex_;
|
||||
std::condition_variable pmpCv_;
|
||||
std::atomic_bool pmpRun_ {true};
|
||||
|
||||
void PMPsearchForIGD(PMPIGD* pmp_igd, natpmp_t& natpmp, bool& found);
|
||||
void PMPaddPortMapping(const PMPIGD& pmp_igd, natpmp_t& natpmp, GlobalMapping& mapping, bool remove=false) const;
|
||||
void PMPdeleteAllPortMapping(const PMPIGD& pmp_igd, natpmp_t& natpmp, int proto) const;
|
||||
|
||||
#endif
|
||||
|
||||
#if HAVE_LIBUPNP
|
||||
|
||||
/**
|
||||
@ -195,18 +150,40 @@ private:
|
||||
UpnpDevice_Handle deviceHandle_ {-1};
|
||||
|
||||
/**
|
||||
* keep track if we've successfully registered a device
|
||||
* keep track if we've successfully registered
|
||||
* a client and/ore device
|
||||
*/
|
||||
std::atomic_bool clientRegistered_ {false};
|
||||
bool deviceRegistered_ {false};
|
||||
|
||||
static int cp_callback(Upnp_EventType event_type, void* event, void* user_data);
|
||||
/**
|
||||
* map of valid IGDs - IGDs which have the correct services and are connected
|
||||
* to some external network (have an external IP)
|
||||
*
|
||||
* the UDN string is used to uniquely identify the IGD
|
||||
*
|
||||
* the mutex is used to access these lists and IGDs in a thread-safe manner
|
||||
*/
|
||||
std::map<std::string, std::unique_ptr<IGD>> validIGDs_;
|
||||
mutable std::mutex validIGDMutex_;
|
||||
std::condition_variable validIGDCondVar_;
|
||||
|
||||
/**
|
||||
* callback function for the UPnP client (control point)
|
||||
* all UPnP events received by the client are processed here
|
||||
* Map of valid IGD listeners.
|
||||
*/
|
||||
int handleUPnPEvents(Upnp_EventType event_type, void* event);
|
||||
std::map<size_t, IGDFoundCallback> igdListeners_;
|
||||
|
||||
/**
|
||||
* Last provided token for valid IGD listeners.
|
||||
* 0 is the invalid token.
|
||||
*/
|
||||
size_t listenerToken_ {0};
|
||||
|
||||
/**
|
||||
* chooses the IGD to use (currently selects the first one in the map)
|
||||
* assumes you already have a lock on igd_mutex_
|
||||
*/
|
||||
IGD* chooseIGD_unlocked() const;
|
||||
|
||||
/* sends out async search for IGD */
|
||||
void searchForIGD();
|
||||
@ -219,24 +196,33 @@ private:
|
||||
|
||||
void parseIGD(IXML_Document* doc, const Upnp_Discovery* d_event);
|
||||
|
||||
/* tries to add mapping, assumes you alreayd have lock on igd_mutex_ */
|
||||
Mapping addMapping(IGD* igd,
|
||||
uint16_t port_external,
|
||||
uint16_t port_internal,
|
||||
PortType type,
|
||||
int *upnp_error);
|
||||
|
||||
uint16_t chooseRandomPort(const IGD* igd, PortType type);
|
||||
|
||||
/* these functions directly create UPnP actions
|
||||
* and make synchronous UPnP control point calls
|
||||
* they assume you have a lock on the igd_mutex_ */
|
||||
bool isIGDConnected(const UPnPIGD& igd);
|
||||
bool isIGDConnected(const IGD* igd);
|
||||
|
||||
IpAddr getExternalIP(const UPnPIGD& igd);
|
||||
IpAddr getExternalIP(const IGD* igd);
|
||||
|
||||
void removeMappingsByLocalIPAndDescription(const UPnPIGD& igd,
|
||||
void removeMappingsByLocalIPAndDescription(const IGD* igd,
|
||||
const std::string& description);
|
||||
|
||||
bool deletePortMapping(const UPnPIGD& igd,
|
||||
bool deletePortMapping(const IGD* igd,
|
||||
const std::string& port_external,
|
||||
const std::string& protocol);
|
||||
|
||||
bool addPortMapping(const UPnPIGD& igd,
|
||||
bool addPortMapping(const IGD* igd,
|
||||
const Mapping& mapping,
|
||||
int* error_code);
|
||||
|
||||
#endif /* HAVE_LIBUPNP */
|
||||
|
||||
};
|
||||
@ -250,3 +236,5 @@ private:
|
||||
std::shared_ptr<UPnPContext> getUPnPContext();
|
||||
|
||||
}} // namespace ring::upnp
|
||||
|
||||
#endif /* UPNP_CONTEXT_H_ */
|
||||
|
@ -46,14 +46,19 @@ Controller::~Controller()
|
||||
{
|
||||
/* remove all mappings */
|
||||
removeMappings();
|
||||
#if HAVE_LIBUPNP
|
||||
if (listToken_ and upnpContext_)
|
||||
upnpContext_->removeIGDListener(listToken_);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
Controller::hasValidIGD(std::chrono::seconds timeout)
|
||||
{
|
||||
#if HAVE_LIBUPNP
|
||||
return upnpContext_ and upnpContext_->hasValidIGD(timeout);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
@ -61,9 +66,11 @@ Controller::setIGDListener(IGDFoundCallback&& cb)
|
||||
{
|
||||
if (not upnpContext_)
|
||||
return;
|
||||
#if HAVE_LIBUPNP
|
||||
if (listToken_)
|
||||
upnpContext_->removeIGDListener(listToken_);
|
||||
listToken_ = cb ? upnpContext_->addIGDListener(std::move(cb)) : 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
@ -74,6 +81,7 @@ Controller::addAnyMapping(uint16_t port_desired,
|
||||
bool unique,
|
||||
uint16_t *port_used)
|
||||
{
|
||||
#if HAVE_LIBUPNP
|
||||
if (not upnpContext_)
|
||||
return false;
|
||||
|
||||
@ -89,6 +97,7 @@ Controller::addAnyMapping(uint16_t port_desired,
|
||||
instanceMappings.emplace(usedPort, std::move(mapping));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -104,6 +113,7 @@ Controller::addAnyMapping(uint16_t port_desired,
|
||||
|
||||
void
|
||||
Controller::removeMappings(PortType type) {
|
||||
#if HAVE_LIBUPNP
|
||||
if (not upnpContext_)
|
||||
return;
|
||||
|
||||
@ -113,28 +123,34 @@ Controller::removeMappings(PortType type) {
|
||||
upnpContext_->removeMapping(mapping);
|
||||
iter = instanceMappings.erase(iter);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Controller::removeMappings()
|
||||
{
|
||||
#if HAVE_LIBUPNP
|
||||
removeMappings(PortType::UDP);
|
||||
removeMappings(PortType::TCP);
|
||||
#endif
|
||||
}
|
||||
|
||||
IpAddr
|
||||
Controller::getLocalIP() const
|
||||
{
|
||||
#if HAVE_LIBUPNP
|
||||
if (upnpContext_)
|
||||
return upnpContext_->getLocalIP();
|
||||
#endif
|
||||
return {}; // empty address
|
||||
}
|
||||
|
||||
IpAddr
|
||||
Controller::getExternalIP() const
|
||||
{
|
||||
#if HAVE_LIBUPNP
|
||||
if (upnpContext_)
|
||||
return upnpContext_->getExternalIP();
|
||||
#endif
|
||||
return {}; // empty address
|
||||
}
|
||||
|
||||
|
@ -18,16 +18,12 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#ifndef UPNP_IGD_H_
|
||||
#define UPNP_IGD_H_
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <chrono>
|
||||
|
||||
#include "noncopyable.h"
|
||||
#include "ip_utils.h"
|
||||
@ -67,13 +63,13 @@ public:
|
||||
friend bool operator== (const Mapping& cRedir1, const Mapping& cRedir2);
|
||||
friend bool operator!= (const Mapping& cRedir1, const Mapping& cRedir2);
|
||||
|
||||
uint16_t getPortExternal() const { return port_external_; }
|
||||
std::string getPortExternalStr() const { return ring::to_string(port_external_); }
|
||||
uint16_t getPortInternal() const { return port_internal_; }
|
||||
std::string getPortInternalStr() const { return ring::to_string(port_internal_); }
|
||||
PortType getType() const { return type_; }
|
||||
uint16_t getPortExternal() const { return port_external_; };
|
||||
std::string getPortExternalStr() const { return ring::to_string(port_external_); };
|
||||
uint16_t getPortInternal() const { return port_internal_; };
|
||||
std::string getPortInternalStr() const { return ring::to_string(port_internal_); };
|
||||
PortType getType() const { return type_; };
|
||||
std::string getTypeStr() const { return type_ == PortType::UDP ? "UDP" : "TCP"; }
|
||||
std::string getDescription() const { return description_; }
|
||||
std::string getDescription() const { return description_; };
|
||||
|
||||
std::string toString() const {
|
||||
return getPortExternalStr() + ":" + getPortInternalStr() + ", " + getTypeStr();
|
||||
@ -87,11 +83,6 @@ public:
|
||||
return isValid();
|
||||
}
|
||||
|
||||
#if HAVE_LIBNATPMP
|
||||
std::chrono::system_clock::time_point renewal_ {std::chrono::system_clock::time_point::min()};
|
||||
bool remove {false};
|
||||
#endif
|
||||
|
||||
private:
|
||||
NON_COPYABLE(Mapping);
|
||||
|
||||
@ -132,6 +123,7 @@ using IGDFoundCallback = std::function<void()>;
|
||||
/* defines a UPnP capable Internet Gateway Device (a router) */
|
||||
class IGD {
|
||||
public:
|
||||
|
||||
/* device address seen by IGD */
|
||||
IpAddr localIp;
|
||||
|
||||
@ -144,22 +136,7 @@ public:
|
||||
|
||||
/* constructors */
|
||||
IGD() {}
|
||||
|
||||
/* move constructor and operator */
|
||||
IGD(IGD&&) = default;
|
||||
IGD& operator=(IGD&&) = default;
|
||||
|
||||
virtual ~IGD() = default;
|
||||
|
||||
private:
|
||||
NON_COPYABLE(IGD);
|
||||
};
|
||||
|
||||
#if HAVE_LIBUPNP
|
||||
|
||||
class UPnPIGD : public IGD {
|
||||
public:
|
||||
UPnPIGD(std::string UDN,
|
||||
IGD(std::string UDN,
|
||||
std::string baseURL,
|
||||
std::string friendlyName,
|
||||
std::string serviceType,
|
||||
@ -175,6 +152,12 @@ public:
|
||||
, eventSubURL_(eventSubURL)
|
||||
{}
|
||||
|
||||
/* move constructor and operator */
|
||||
IGD(IGD&&) = default;
|
||||
IGD& operator=(IGD&&) = default;
|
||||
|
||||
~IGD() = default;
|
||||
|
||||
const std::string& getUDN() const { return UDN_; };
|
||||
const std::string& getBaseURL() const { return baseURL_; };
|
||||
const std::string& getFriendlyName() const { return friendlyName_; };
|
||||
@ -184,6 +167,8 @@ public:
|
||||
const std::string& getEventSubURL() const { return eventSubURL_; };
|
||||
|
||||
private:
|
||||
NON_COPYABLE(IGD);
|
||||
|
||||
/* root device info */
|
||||
std::string UDN_ {}; /* used to uniquely identify this UPnP device */
|
||||
std::string baseURL_ {};
|
||||
@ -194,47 +179,9 @@ private:
|
||||
std::string serviceId_ {};
|
||||
std::string controlURL_ {};
|
||||
std::string eventSubURL_ {};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if HAVE_LIBNATPMP
|
||||
|
||||
using clock = std::chrono::system_clock;
|
||||
using time_point = clock::time_point;
|
||||
|
||||
class PMPIGD : public IGD {
|
||||
public:
|
||||
|
||||
void clearAll() {
|
||||
toRemove_.clear();
|
||||
udpMappings.clear();
|
||||
tcpMappings.clear();
|
||||
clearAll_ = true;
|
||||
}
|
||||
|
||||
GlobalMapping* getNextMappingToRenew() const {
|
||||
const GlobalMapping* mapping {nullptr};
|
||||
for (const auto& m : udpMappings)
|
||||
if (!mapping or m.second.renewal_ < mapping->renewal_)
|
||||
mapping = &m.second;
|
||||
for (const auto& m : tcpMappings)
|
||||
if (!mapping or m.second.renewal_ < mapping->renewal_)
|
||||
mapping = &m.second;
|
||||
return (GlobalMapping*)mapping;
|
||||
}
|
||||
|
||||
time_point getRenewalTime() const {
|
||||
const auto next = getNextMappingToRenew();
|
||||
auto nextTime = std::min(renewal_, next ? next->renewal_ : time_point::max());
|
||||
return toRemove_.empty() ? nextTime : std::min(nextTime, time_point::min());
|
||||
}
|
||||
|
||||
time_point renewal_ {time_point::min()};
|
||||
std::vector<GlobalMapping> toRemove_ {};
|
||||
bool clearAll_ {false};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
}} // namespace ring::upnp
|
||||
|
||||
#endif /* UPNP_IGD_H_ */
|
||||
|
Reference in New Issue
Block a user