add upnp/natpmp support
Change-Id: I4945a7df3a30cb39d81a33fc7a32e9fea600bdff
diff --git a/src/upnp/upnp_context.cpp b/src/upnp/upnp_context.cpp
index 6b1ff90..5022eb3 100644
--- a/src/upnp/upnp_context.cpp
+++ b/src/upnp/upnp_context.cpp
@@ -17,6 +17,13 @@
#include "upnp/upnp_context.h"
#include "protocol/upnp_protocol.h"
+#if HAVE_LIBNATPMP
+#include "protocol/natpmp/nat_pmp.h"
+#endif
+#if HAVE_LIBUPNP
+#include "protocol/pupnp/pupnp.h"
+#endif
+
#include <asio/steady_timer.hpp>
#if __has_include(<fmt/std.h>)
#include <fmt/std.h>
@@ -37,7 +44,7 @@
constexpr static uint16_t UPNP_UDP_PORT_MAX {UPNP_UDP_PORT_MIN + 5000};
UPnPContext::UPnPContext(const std::shared_ptr<asio::io_context>& ioContext, const std::shared_ptr<dht::log::Logger>& logger)
- : mappingListUpdateTimer_(*ioContext)
+ : mappingListUpdateTimer_(*ioContext), ctx(ioContext), logger_(logger)
{
// JAMI_DBG("Creating UPnPContext instance [%p]", this);
@@ -45,7 +52,7 @@
portRange_.emplace(PortType::TCP, std::make_pair(UPNP_TCP_PORT_MIN, UPNP_TCP_PORT_MAX));
portRange_.emplace(PortType::UDP, std::make_pair(UPNP_UDP_PORT_MIN, UPNP_UDP_PORT_MAX));
- ioContext->post([this] { init(); });
+ ctx->post([this] { init(); });
}
/*std::shared_ptr<UPnPContext>
@@ -86,8 +93,8 @@
std::unique_lock<std::mutex> lk(mappingMutex_);
std::condition_variable cv;
- runOnUpnpContextQueue([&, this] { shutdown(cv); });
-
+ ctx->post([&, this] { shutdown(cv); });
+
// JAMI_DBG("Waiting for shutdown ...");
if (cv.wait_for(lk, std::chrono::seconds(30), [this] { return shutdownComplete_; })) {
@@ -105,17 +112,14 @@
void
UPnPContext::init()
{
- threadId_ = getCurrentThread();
- CHECK_VALID_THREAD();
-
#if HAVE_LIBNATPMP
- auto natPmp = std::make_shared<NatPmp>();
+ auto natPmp = std::make_shared<NatPmp>(ctx, logger_);
natPmp->setObserver(this);
protocolList_.emplace(NatProtocolType::NAT_PMP, std::move(natPmp));
#endif
#if HAVE_LIBUPNP
- auto pupnp = std::make_shared<PUPnP>();
+ auto pupnp = std::make_shared<PUPnP>(ctx, logger_);
pupnp->setObserver(this);
protocolList_.emplace(NatProtocolType::PUPNP, std::move(pupnp));
#endif
@@ -126,13 +130,11 @@
{
assert(not controllerList_.empty());
- CHECK_VALID_THREAD();
-
// JAMI_DBG("Starting UPNP context");
// Request a new IGD search.
for (auto const& [_, protocol] : protocolList_) {
- protocol->searchForIgd();
+ ctx->dispatch([p=protocol] { p->searchForIgd(); });
}
started_ = true;
@@ -141,10 +143,10 @@
void
UPnPContext::stopUpnp(bool forceRelease)
{
- if (not isValidThread()) {
- runOnUpnpContextQueue([this, forceRelease] { stopUpnp(forceRelease); });
+ /*if (not isValidThread()) {
+ ctx->post([this, forceRelease] { stopUpnp(forceRelease); });
return;
- }
+ }*/
// JAMI_DBG("Stopping UPNP context");
@@ -170,13 +172,12 @@
for (auto const& map : toRemoveList) {
requestRemoveMapping(map);
- // Notify is not needed in updateMappingState when
+ // Notify is not needed in updateState when
// shutting down (hence set it to false). NotifyCallback
// would trigger a new SIP registration and create a
// false registered state upon program close.
// It's handled by upper layers.
-
- updateMappingState(map, MappingState::FAILED, false);
+ map->updateState(MappingState::FAILED, false);
// We dont remove mappings with auto-update enabled,
// unless forceRelease is true.
if (not map->getAutoUpdate() or forceRelease) {
@@ -187,7 +188,7 @@
// Clear all current IGDs.
for (auto const& [_, protocol] : protocolList_) {
- protocol->clearIgds();
+ ctx->dispatch([p=protocol]{ p->clearIgds(); });
}
started_ = false;
@@ -221,10 +222,10 @@
void
UPnPContext::connectivityChanged()
{
- if (not isValidThread()) {
- runOnUpnpContextQueue([this] { connectivityChanged(); });
+ /*if (not isValidThread()) {
+ ctx->post([this] { connectivityChanged(); });
return;
- }
+ }*/
auto hostAddr = ip_utils::getLocalAddr(AF_INET);
@@ -367,10 +368,10 @@
void
UPnPContext::releaseMapping(const Mapping& map)
{
- if (not isValidThread()) {
- runOnUpnpContextQueue([this, map] { releaseMapping(map); });
+ /*if (not isValidThread()) {
+ ctx->post([this, map] { releaseMapping(map); });
return;
- }
+ }*/
auto mapPtr = getMappingWithKey(map.getMapKey());
@@ -401,10 +402,10 @@
}
}
- if (not isValidThread()) {
- runOnUpnpContextQueue([this, controller] { registerController(controller); });
+ /*if (not isValidThread()) {
+ ctx->post([this, controller] { registerController(controller); });
return;
- }
+ }*/
auto ret = controllerList_.emplace(controller);
if (not ret.second) {
@@ -420,10 +421,10 @@
void
UPnPContext::unregisterController(void* controller)
{
- if (not isValidThread()) {
- runOnUpnpContextQueue([this, controller] { unregisterController(controller); });
+ /*if (not isValidThread()) {
+ ctx->post([this, controller] { unregisterController(controller); });
return;
- }
+ }*/
if (controllerList_.erase(controller) == 1) {
// JAMI_DBG("Successfully unregistered controller %p", controller);
@@ -462,10 +463,10 @@
{
assert(map);
- if (not isValidThread()) {
- runOnUpnpContextQueue([this, map] { requestMapping(map); });
+ /*if (not isValidThread()) {
+ ctx->post([this, map] { requestMapping(map); });
return;
- }
+ }*/
auto const& igd = getPreferredIgd();
// We must have at least a valid IGD pointer if we get here.
@@ -484,8 +485,7 @@
// igd->getProtocolName(),
// igd->toString().c_str());
- if (map->getState() != MappingState::IN_PROGRESS)
- updateMappingState(map, MappingState::IN_PROGRESS);
+ map->updateState(MappingState::IN_PROGRESS);
auto const& protocol = protocolList_.at(igd->getProtocol());
protocol->requestMappingAdd(*map);
@@ -522,7 +522,7 @@
assert(portCount > 0);
- CHECK_VALID_THREAD();
+ //CHECK_VALID_THREAD();
std::lock_guard<std::mutex> lock(mappingMutex_);
auto& mappingList = getMappingList(type);
@@ -539,14 +539,14 @@
if (map->getState() == MappingState::OPEN and portCount > 0) {
// Close portCount mappings in "OPEN" state.
requestRemoveMapping(map);
- it = unregisterMapping(it);
+ it = mappingList.erase(it);
portCount--;
} else if (map->getState() != MappingState::OPEN) {
// If this methods is called, it means there are more open
// mappings than required. So, all mappings in a state other
// than "OPEN" state (typically in in-progress state) will
// be deleted as well.
- it = unregisterMapping(it);
+ it = mappingList.erase(it);
} else {
it++;
}
@@ -558,7 +558,7 @@
void
UPnPContext::updatePreferredIgd()
{
- CHECK_VALID_THREAD();
+ //CHECK_VALID_THREAD();
if (preferredIgd_ and preferredIgd_->isValid())
return;
@@ -594,7 +594,7 @@
std::shared_ptr<IGD>
UPnPContext::getPreferredIgd() const
{
- CHECK_VALID_THREAD();
+ //CHECK_VALID_THREAD();
return preferredIgd_;
}
@@ -604,11 +604,11 @@
{
// Run async if requested.
if (async) {
- runOnUpnpContextQueue([this] { updateMappingList(false); });
+ ctx->post([this] { updateMappingList(false); });
return;
}
- CHECK_VALID_THREAD();
+ //CHECK_VALID_THREAD();
// Update the preferred IGD.
updatePreferredIgd();
@@ -712,7 +712,7 @@
void
UPnPContext::pruneMappingList()
{
- CHECK_VALID_THREAD();
+ //CHECK_VALID_THREAD();
MappingStatus status;
getMappingStatus(status);
@@ -776,7 +776,7 @@
}
for (auto const& map : toRemoveList) {
- updateMappingState(map, MappingState::FAILED);
+ map->updateState(MappingState::FAILED);
unregisterMapping(map);
}
}
@@ -817,7 +817,7 @@
void
UPnPContext::pruneMappingsWithInvalidIgds(const std::shared_ptr<IGD>& igd)
{
- CHECK_VALID_THREAD();
+ //CHECK_VALID_THREAD();
// Use temporary list to avoid holding the lock while
// processing the mapping list.
@@ -840,7 +840,7 @@
// map->toString().c_str(),
// igd->toString().c_str(),
// igd->getProtocolName());
- updateMappingState(map, MappingState::FAILED);
+ map->updateState(MappingState::FAILED);
unregisterMapping(map);
}
}
@@ -926,10 +926,10 @@
{
assert(igd);
- if (not isValidThread()) {
- runOnUpnpContextQueue([this, igd, event] { onIgdUpdated(igd, event); });
+ /*if (not isValidThread()) {
+ ctx->post([this, igd, event] { onIgdUpdated(igd, event); });
return;
- }
+ }*/
// Reset to start search for a new best IGD.
preferredIgd_.reset();
@@ -1005,7 +1005,7 @@
void
UPnPContext::onMappingAdded(const std::shared_ptr<IGD>& igd, const Mapping& mapRes)
{
- CHECK_VALID_THREAD();
+ //CHECK_VALID_THREAD();
// Check if we have a pending request for this response.
auto map = getMappingWithKey(mapRes.getMapKey());
@@ -1024,7 +1024,7 @@
map->setExternalPort(mapRes.getExternalPort());
// Update the state and report to the owner.
- updateMappingState(map, MappingState::OPEN);
+ map->updateState(MappingState::OPEN);
// JAMI_DBG("Mapping %s (on IGD %s [%s]) successfully performed",
// map->toString().c_str(),
@@ -1066,18 +1066,10 @@
void
UPnPContext::requestRemoveMapping(const Mapping::sharedPtr_t& map)
{
- CHECK_VALID_THREAD();
-
- if (not map) {
- // JAMI_ERR("Mapping shared pointer is null!");
- return;
- }
-
- if (not map->isValid()) {
+ if (not map or not map->isValid()) {
// Silently ignore if the mapping is invalid
return;
}
-
auto protocol = protocolList_.at(map->getIgd()->getProtocol());
protocol->requestMappingRemove(*map);
}
@@ -1085,10 +1077,10 @@
void
UPnPContext::deleteAllMappings(PortType type)
{
- if (not isValidThread()) {
- runOnUpnpContextQueue([this, type] { deleteAllMappings(type); });
+ /*if (not isValidThread()) {
+ ctx->post([this, type] { deleteAllMappings(type); });
return;
- }
+ }*/
std::lock_guard<std::mutex> lock(mappingMutex_);
auto& mappingList = getMappingList(type);
@@ -1104,10 +1096,10 @@
if (not mapRes.isValid())
return;
- if (not isValidThread()) {
- runOnUpnpContextQueue([this, igd, mapRes] { onMappingRemoved(igd, mapRes); });
+ /*if (not isValidThread()) {
+ ctx->post([this, igd, mapRes] { onMappingRemoved(igd, mapRes); });
return;
- }
+ }*/
auto map = getMappingWithKey(mapRes.getMapKey());
// Notify the listener.
@@ -1154,23 +1146,10 @@
return mapPtr;
}
-std::map<Mapping::key_t, Mapping::sharedPtr_t>::iterator
-UPnPContext::unregisterMapping(std::map<Mapping::key_t, Mapping::sharedPtr_t>::iterator it)
-{
- assert(it->second);
-
- CHECK_VALID_THREAD();
- auto descr = it->second->toString();
- auto& mappingList = getMappingList(it->second->getType());
- auto ret = mappingList.erase(it);
-
- return ret;
-}
-
void
UPnPContext::unregisterMapping(const Mapping::sharedPtr_t& map)
{
- CHECK_VALID_THREAD();
+ //CHECK_VALID_THREAD();
if (not map) {
// JAMI_ERR("Mapping pointer is null");
@@ -1256,8 +1235,6 @@
void
UPnPContext::onMappingRequestFailed(const Mapping& mapRes)
{
- CHECK_VALID_THREAD();
-
auto const& map = getMappingWithKey(mapRes.getMapKey());
if (not map) {
// We may receive a response for a removed request. Just ignore it.
@@ -1273,7 +1250,7 @@
return;
}
- updateMappingState(map, MappingState::FAILED);
+ map->updateState(MappingState::FAILED);
unregisterMapping(map);
// JAMI_WARN("Mapping request for %s failed on IGD %s [%s]",
@@ -1282,32 +1259,11 @@
// igd->getProtocolName());
}
-void
-UPnPContext::updateMappingState(const Mapping::sharedPtr_t& map, MappingState newState, bool notify)
-{
- CHECK_VALID_THREAD();
-
- assert(map);
-
- // Ignore if the state did not change.
- if (newState == map->getState()) {
- // JAMI_DBG("Mapping %s already in state %s", map->toString().c_str(), map->getStateStr());
- return;
- }
-
- // Update the state.
- map->setState(newState);
-
- // Notify the listener if set.
- if (notify and map->getNotifyCallback())
- map->getNotifyCallback()(map);
-}
-
#if HAVE_LIBNATPMP
void
UPnPContext::renewAllocations()
{
- CHECK_VALID_THREAD();
+ //CHECK_VALID_THREAD();
// Check if the we have valid PMP IGD.
auto pmpProto = protocolList_.at(NatProtocolType::NAT_PMP);