diff --git a/src/account.cpp b/src/account.cpp index ee28a9b383b9afac88838ce12380e6648717c688..8eac6fece976eeed4a4c95e5d7e42848a8b406f4 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -155,10 +155,10 @@ Account::enableUpnp(bool state) { std::lock_guard<std::mutex> lk {upnp_mtx}; - if (state and !upnp_) - upnp_.reset(new upnp::Controller()); - else if (!state and upnp_) - upnp_.reset(); + if (state and !upnpCtrl_) + upnpCtrl_.reset(new upnp::Controller()); + else if (!state and upnpCtrl_) + upnpCtrl_.reset(); } void @@ -529,8 +529,8 @@ IpAddr Account::getUPnPIpAddress() const { std::lock_guard<std::mutex> lk(upnp_mtx); - if (upnp_) - return upnp_->getExternalIP(); + if (upnpCtrl_) + return upnpCtrl_->getExternalIP(); return {}; } @@ -542,8 +542,8 @@ bool Account::getUPnPActive() const { std::lock_guard<std::mutex> lk(upnp_mtx); - if (upnp_) - return upnp_->hasValidIGD(); + if (upnpCtrl_) + return upnpCtrl_->hasValidIGD(); return false; } diff --git a/src/account.h b/src/account.h index 0fca8e383cc023c4615e38741b2bd104c5260dfd..f11a5007612f49198e3d6279cdbb888ab6387a30 100644 --- a/src/account.h +++ b/src/account.h @@ -324,7 +324,7 @@ public: void addDefaultModerator(const std::string& peerURI); void removeDefaultModerator(const std::string& peerURI); - bool isLocalModeratorsEnabled() const { return localModeratorsEnabled_; } + bool isLocalModeratorsEnabled() const { return localModeratorsEnabled_; } void enableLocalModerators(bool isModEnabled) { localModeratorsEnabled_ = isModEnabled; } public: // virtual methods that has to be implemented by concrete classes @@ -343,9 +343,9 @@ private: std::mutex callIDSetMtx_; std::set<std::string> callIDSet_; - void enableUpnp(bool state); - protected: + virtual void enableUpnp(bool state); + static void parseString(const std::map<std::string, std::string>& details, const char* key, std::string& s); @@ -515,7 +515,7 @@ protected: */ bool upnpEnabled_; mutable std::mutex upnp_mtx {}; - std::shared_ptr<jami::upnp::Controller> upnp_; + std::shared_ptr<jami::upnp::Controller> upnpCtrl_; std::set<std::string> defaultModerators_ {}; bool localModeratorsEnabled_; diff --git a/src/ice_transport.cpp b/src/ice_transport.cpp index 914b756806d07da7d8fb6f5e6d9782fca7a16dd3..146eaceae8bc11f8cb001d973f9a98e65573bcc0 100644 --- a/src/ice_transport.cpp +++ b/src/ice_transport.cpp @@ -120,10 +120,16 @@ public: static const char* getCandidateType(const pj_ice_sess_cand* cand); bool isTcpEnabled() const { return config_.protocol == PJ_ICE_TP_TCP; } bool addStunConfig(int af); - void setupDefaultCandidates(); + void addDefaultCandidates(); void requestUpnpMappings(); bool hasUpnp() const; - void setupServerReflexiveCandidates(); + // Take a list of address pairs (local/public) and add them as + // reflexive candidates using STUN config. + void addServerReflexiveCandidates(const std::vector<std::pair<IpAddr, IpAddr>>& addrList); + // Generate server reflexive candidates using the published (DHT/Account) address + const std::vector<std::pair<IpAddr, IpAddr>> setupGenericReflexiveCandidates(); + // Generate server reflexive candidates using UPNP mappings. + const std::vector<std::pair<IpAddr, IpAddr>> setupUpnpReflexiveCandidates(); void setDefaultRemoteAddress(unsigned comp_id, const IpAddr& addr); const IpAddr& getDefaultRemoteAddress(unsigned comp_id) const; bool handleEvents(unsigned max_msec); @@ -337,12 +343,14 @@ IceTransport::Impl::Impl(const char* name, peerChannels_.resize(component_count_ + 1); - setupDefaultCandidates(); + addDefaultCandidates(); - if (upnp_) - requestUpnpMappings(); + addServerReflexiveCandidates(setupGenericReflexiveCandidates()); - setupServerReflexiveCandidates(); + if (upnp_) { + requestUpnpMappings(); + addServerReflexiveCandidates(setupUpnpReflexiveCandidates()); + } pool_.reset( pj_pool_create(iceTransportFactory.getPoolFactory(), "IceTransport.pool", 512, 512, NULL)); @@ -544,9 +552,9 @@ IceTransport::Impl::handleEvents(unsigned max_msec) void IceTransport::Impl::onComplete(pj_ice_strans* ice_st, pj_ice_strans_op op, pj_status_t status) { - const char* opname = op == PJ_ICE_STRANS_OP_INIT - ? "initialization" - : op == PJ_ICE_STRANS_OP_NEGOTIATION ? "negotiation" : "unknown_op"; + const char* opname = op == PJ_ICE_STRANS_OP_INIT ? "initialization" + : op == PJ_ICE_STRANS_OP_NEGOTIATION ? "negotiation" + : "unknown_op"; const bool done = status == PJ_SUCCESS; if (done) { @@ -742,7 +750,7 @@ IceTransport::Impl::addStunConfig(int af) stun.af = af; stun.conn_type = config_.stun.conn_type; - JAMI_DBG("[ice:%p)] added host stun config for %s transport", + JAMI_DBG("[ice:%p] added host stun config for %s transport", this, config_.protocol == PJ_ICE_TP_TCP ? "TCP" : "UDP"); @@ -750,7 +758,7 @@ IceTransport::Impl::addStunConfig(int af) } void -IceTransport::Impl::setupDefaultCandidates() +IceTransport::Impl::addDefaultCandidates() { JAMI_DBG("[ice:%p]: Setup default candidates", this); @@ -790,11 +798,11 @@ IceTransport::Impl::requestUpnpMappings() std::lock_guard<std::mutex> lock(upnpMappingsMutex_); auto ret = upnpMappings_.emplace(mapPtr->getMapKey(), *mapPtr); if (ret.second) { - JAMI_DBG("[ice:%p]: UPNP mapping %s successfully allocated", + JAMI_DBG("[ice:%p] UPNP mapping %s successfully allocated", this, mapPtr->toString(true).c_str()); } else { - JAMI_WARN("[ice:%p]: UPNP mapping %s already in the list!", + JAMI_WARN("[ice:%p] UPNP mapping %s already in the list!", this, mapPtr->toString().c_str()); } @@ -812,70 +820,14 @@ IceTransport::Impl::hasUpnp() const } void -IceTransport::Impl::setupServerReflexiveCandidates() +IceTransport::Impl::addServerReflexiveCandidates( + const std::vector<std::pair<IpAddr, IpAddr>>& addrList) { - JAMI_DBG("[ice:%p]: Setup server reflexive candidates", this); - - std::vector<std::pair<IpAddr, IpAddr>> addrList; - auto hasUpnpCand = hasUpnp(); - auto isTcp = isTcpEnabled(); - - // Server reflexive candidates are built using UPNP mappings if enabled and - // available. Otherwise, the published address will be used instead. This - // address is given with the other ICE options. - // For TCP transport, the connection type is set to passive for UPNP - // candidates and set to active otherwise. - if (hasUpnpCand) { - std::lock_guard<std::mutex> lock(upnpMappingsMutex_); - unsigned compId = 1; - for (auto const& [_, map] : upnpMappings_) { - assert(map.getMapKey()); - IpAddr localAddr {map.getInternalAddress()}; - localAddr.setPort(map.getInternalPort()); - IpAddr publicAddr {map.getExternalAddress()}; - publicAddr.setPort(map.getExternalPort()); - addrList.emplace_back(localAddr, publicAddr); - - JAMI_DBG("[ice:%p]: Using upnp mapping %s (%s) -> %s (%s) for comp %u", - this, - localAddr.toString(true).c_str(), - localAddr.getFamily() == pj_AF_INET() - ? "IPv4" - : localAddr.getFamily() == pj_AF_INET6() ? "IPv6" : "unknown", - publicAddr.toString(true).c_str(), - publicAddr.getFamily() == pj_AF_INET() - ? "IPv4" - : localAddr.getFamily() == pj_AF_INET6() ? "IPv6" : "unknown", - compId); - compId++; - } - } else { - auto localAddr = accountLocalAddr_; - auto publicAddr = accountPublicAddr_; - if (localAddr and publicAddr) { - for (unsigned compIdx = 0; compIdx < component_count_; compIdx++) { - uint16_t port = 0; - if (isTcp and not hasUpnpCand) - port = 9; - else - port = upnp::Controller::generateRandomPort(isTcp ? PortType::TCP - : PortType::UDP); - - localAddr.setPort(port); - publicAddr.setPort(port); - addrList.emplace_back(localAddr, publicAddr); - - JAMI_DBG("[ice:%p]: Using Account address [%s : %s] for comp %u", - this, - localAddr.toString(true).c_str(), - publicAddr.toString(true).c_str(), - compIdx + 1); - } - } - } - if (addrList.size() != component_count_) { - JAMI_WARN("[ice:%p]: No DHT or UPNP server reflexive candidates added", this); + JAMI_WARN("[ice:%p]: Provided addr list size %lu does not match component count %u", + this, + addrList.size(), + component_count_); return; } @@ -889,14 +841,15 @@ IceTransport::Impl::setupServerReflexiveCandidates() for (unsigned compIdx = 0; compIdx < component_count_; compIdx++) { auto localAddr = addrList[compIdx].first; auto publicAddr = addrList[compIdx].second; + pj_sockaddr_cp(&stun.cfg.user_mapping[compIdx].local_addr, localAddr.pjPtr()); pj_sockaddr_cp(&stun.cfg.user_mapping[compIdx].mapped_addr, publicAddr.pjPtr()); - if (isTcp) { - if (hasUpnpCand) { - stun.cfg.user_mapping[compIdx].tp_type = PJ_CAND_TCP_PASSIVE; - } else { + if (isTcpEnabled()) { + if (publicAddr.getPort() == 9) { stun.cfg.user_mapping[compIdx].tp_type = PJ_CAND_TCP_ACTIVE; + } else { + stun.cfg.user_mapping[compIdx].tp_type = PJ_CAND_TCP_PASSIVE; } } else { stun.cfg.user_mapping[compIdx].tp_type = PJ_CAND_UDP; @@ -907,6 +860,81 @@ IceTransport::Impl::setupServerReflexiveCandidates() assert(stun.cfg.user_mapping_cnt < PJ_ICE_MAX_COMP); } +const std::vector<std::pair<IpAddr, IpAddr>> +IceTransport::Impl::setupGenericReflexiveCandidates() +{ + std::vector<std::pair<IpAddr, IpAddr>> addrList; + auto isTcp = isTcpEnabled(); + + // First, set default server reflexive candidates using current + // account public address. + // Then, check for UPNP mappings and and them if available. + // For TCP transport, the connection type is set to passive for UPNP + // candidates and set to active otherwise. + + if (accountLocalAddr_ and accountPublicAddr_) { + for (unsigned compIdx = 0; compIdx < component_count_; compIdx++) { + // For TCP, the type is set to active, because most likely the incoming + // connection will be blocked by the NAT. + // For UDP use random port number. + uint16_t port = isTcp ? 9 + : upnp::Controller::generateRandomPort(isTcp ? PortType::TCP + : PortType::UDP); + + accountLocalAddr_.setPort(port); + accountPublicAddr_.setPort(port); + addrList.emplace_back(accountLocalAddr_, accountPublicAddr_); + + JAMI_DBG("[ice:%p]: Add generic local reflexive candidates [%s : %s] for comp %u", + this, + accountLocalAddr_.toString(true).c_str(), + accountPublicAddr_.toString(true).c_str(), + compIdx + 1); + } + } + + return addrList; +} + +const std::vector<std::pair<IpAddr, IpAddr>> +IceTransport::Impl::setupUpnpReflexiveCandidates() +{ + // Add UPNP server reflexive candidates if available. + if (not hasUpnp()) + return {}; + + std::lock_guard<std::mutex> lock(upnpMappingsMutex_); + + if (static_cast<unsigned>(upnpMappings_.size()) < component_count_) { + JAMI_WARN("[ice:%p]: Not enough mappings %lu. Expected %u", + this, + upnpMappings_.size(), + component_count_); + return {}; + } + + std::vector<std::pair<IpAddr, IpAddr>> addrList; + + unsigned compId = 1; + for (auto const& [_, map] : upnpMappings_) { + assert(map.getMapKey()); + IpAddr localAddr {map.getInternalAddress()}; + localAddr.setPort(map.getInternalPort()); + IpAddr publicAddr {map.getExternalAddress()}; + publicAddr.setPort(map.getExternalPort()); + addrList.emplace_back(localAddr, publicAddr); + + JAMI_DBG("[ice:%p]: Add UPNP local reflexive candidates [%s : %s] for comp %u", + this, + localAddr.toString(true).c_str(), + publicAddr.toString(true).c_str(), + compId); + compId++; + } + + return addrList; +} + void IceTransport::Impl::setDefaultRemoteAddress(unsigned comp_id, const IpAddr& addr) { diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp index a1357123d370a0b08ee30db43e4076e6a53c055f..a111671c3f1ccc1a956ce59c065bd2c643f6e4d2 100644 --- a/src/jamidht/jamiaccount.cpp +++ b/src/jamidht/jamiaccount.cpp @@ -680,16 +680,9 @@ JamiAccount::onConnectedOutgoingCall(const std::shared_ptr<SIPCall>& call, const auto localAddress = ip_utils::getInterfaceAddr(shared->getLocalInterface(), target.getFamily()); - IpAddr addrSdp; - if (shared->getUPnPActive()) { - // use UPnP addr, or published addr if its set - addrSdp = shared->getPublishedSameasLocal() ? shared->getUPnPIpAddress() - : shared->getPublishedIpAddress(); - } else { - addrSdp = shared->isStunEnabled() or (not shared->getPublishedSameasLocal()) - ? shared->getPublishedIpAddress() - : localAddress; - } + IpAddr addrSdp = shared->getPublishedSameasLocal() + ? localAddress + : shared->getPublishedIpAddress(target.getFamily()); // fallback on local address if (not addrSdp) @@ -1804,12 +1797,12 @@ JamiAccount::registerAsyncOps() loadCachedProxyServer([onLoad](const std::string&) { onLoad(); }); - if (upnp_) { + if (upnpCtrl_) { JAMI_DBG("UPnP: Attempting to map ports for Jami account"); // Release current mapping if any. if (dhtUpnpMapping_.isValid()) { - upnp_->releaseMapping(dhtUpnpMapping_); + upnpCtrl_->releaseMapping(dhtUpnpMapping_); } dhtUpnpMapping_ = upnp::Mapping {0, 0, upnp::PortType::UDP}; @@ -1875,7 +1868,7 @@ JamiAccount::registerAsyncOps() }); // Request the mapping. - auto map = upnp_->reserveMapping(dhtUpnpMapping_); + auto map = upnpCtrl_->reserveMapping(dhtUpnpMapping_); // The returned mapping is invalid. Load the account now since // we may never receive the callback. if (not map or not map->isValid()) { @@ -1913,7 +1906,7 @@ JamiAccount::doRegister() setRegistrationState(RegistrationState::TRYING); /* if UPnP is enabled, then wait for IGD to complete registration */ - if (upnp_ or proxyServerCached_.empty()) { + if (upnpCtrl_ or proxyServerCached_.empty()) { registerAsyncOps(); } else { doRegister_(); @@ -1922,6 +1915,35 @@ JamiAccount::doRegister() cacheTurnServers(); // reset cache for TURN servers } +void +JamiAccount::enableUpnp(bool enable) +{ + std::lock_guard<std::mutex> lk {upnp_mtx}; + + if (enable) { + auto const& addr = getPublishedIpAddress(); + + if (upnpCtrl_ and upnpCtrl_->getExternalIP() == addr) { + JAMI_DBG( + "[Account %s] Already have a valid controller instance with external address [%s]", + getAccountID().c_str(), + upnpCtrl_->getExternalIP().toString().c_str()); + return; + } + + JAMI_DBG("[Account %s] Creating a new upnp controller instance", getAccountID().c_str()); + // Need a known IPv4 address to validate the external address + // returned by the IGD. + if (addr and addr.getFamily() == AF_INET) { + upnpCtrl_.reset(new upnp::Controller(addr)); + } else { + upnpCtrl_.reset(new upnp::Controller()); + } + } else { + upnpCtrl_.reset(); + } +} + std::vector<std::string> JamiAccount::loadBootstrap() const { @@ -2564,8 +2586,8 @@ JamiAccount::doUnregister(std::function<void(bool)> released_cb) dht_->join(); // Release current upnp mapping if any. - if (upnp_ and dhtUpnpMapping_.isValid()) { - upnp_->releaseMapping(dhtUpnpMapping_); + if (upnpCtrl_ and dhtUpnpMapping_.isValid()) { + upnpCtrl_->releaseMapping(dhtUpnpMapping_); dhtUpnpMapping_ = upnp::Mapping {}; } @@ -2590,7 +2612,7 @@ JamiAccount::connectivityChanged() return; } // Let UPNP handle connectivity change if enabled. - if (not upnp_) + if (not upnpCtrl_) dht_->connectivityChanged(); // reset cache setPublishedAddress({}); @@ -3352,6 +3374,11 @@ JamiAccount::storeActiveIpAddress() JAMI_DBG("[Account %s] Store DHT public IPv4 address : %s", getAccountID().c_str(), publicAddr.toString().c_str()); + + // Check if we need to create a new UPNP controller instance. + if (upnpEnabled_ and (not upnpCtrl_ or not upnpCtrl_->hasValidIGD())) { + enableUpnp(true); + } } // IPv6 diff --git a/src/jamidht/jamiaccount.h b/src/jamidht/jamiaccount.h index dfaadb497fbbeb9fdab83a3496bc5628ac1defe5..843e1c9dcee7dad9416531554746192de34d36be 100644 --- a/src/jamidht/jamiaccount.h +++ b/src/jamidht/jamiaccount.h @@ -186,6 +186,7 @@ public: */ void doUnregister(std::function<void(bool)> cb = {}) override; + void enableUpnp(bool enable) override; /** * @return pj_str_t "From" uri based on account information. * From RFC3261: "The To header field first and foremost specifies the desired @@ -648,8 +649,8 @@ private: */ in_port_t dhtPortUsed() { - return (upnp_ and dhtUpnpMapping_.isValid()) ? dhtUpnpMapping_.getExternalPort() - : dhtDefaultPort_; + return (upnpCtrl_ and dhtUpnpMapping_.isValid()) ? dhtUpnpMapping_.getExternalPort() + : dhtDefaultPort_; } /* Current UPNP mapping */ diff --git a/src/sip/sipaccount.cpp b/src/sip/sipaccount.cpp index 53c9473b8019af977a38aa6fa80b4814396b6463..b5b4beec1da782fe9d267a352d7b3fee61fcc252 100644 --- a/src/sip/sipaccount.cpp +++ b/src/sip/sipaccount.cpp @@ -770,7 +770,7 @@ SIPAccount::mapPortUPnP() } }); - auto mapRes = upnp_->reserveMapping(map); + auto mapRes = upnpCtrl_->reserveMapping(map); if (mapRes and mapRes->getState() == upnp::MappingState::OPEN) { return true; } @@ -789,7 +789,7 @@ SIPAccount::doRegister() JAMI_DBG("doRegister %s", hostname_.c_str()); /* if UPnP is enabled, then wait for IGD to complete registration */ - if (upnp_) { + if (upnpCtrl_) { JAMI_DBG("UPnP: waiting for IGD to register SIP account"); setRegistrationState(RegistrationState::TRYING); if (not mapPortUPnP()) { diff --git a/src/sip/sipaccountbase.cpp b/src/sip/sipaccountbase.cpp index aac4c79fb179aa58439138de4327d9f721b6d009..08f08e27c1452f64b2ac09d9e8f581506e7f63a7 100644 --- a/src/sip/sipaccountbase.cpp +++ b/src/sip/sipaccountbase.cpp @@ -602,9 +602,17 @@ SIPAccountBase::setMessageDisplayed(const std::string& contactId, } IpAddr -SIPAccountBase::getPublishedIpAddress() const +SIPAccountBase::getPublishedIpAddress(uint16_t family) const { - // Prefere IPv4 if available. More likely to succeed behind NAT. + if (family == AF_INET) + return publishedIp_[0]; + if (family == AF_INET6) + return publishedIp_[1]; + + assert(family == AF_UNSPEC); + + // If family is not set, prefere IPv4 if available. It's more + // likely to succeed behind NAT. if (publishedIp_[0]) return publishedIp_[0]; if (publishedIp_[1]) diff --git a/src/sip/sipaccountbase.h b/src/sip/sipaccountbase.h index 20c3d9e73dd98b0179d61859be47a0406ce8a510..8472a65aca2d789ae2fc21e897641b5a6151e8a7 100644 --- a/src/sip/sipaccountbase.h +++ b/src/sip/sipaccountbase.h @@ -184,7 +184,7 @@ public: */ std::string getPublishedAddress() const { return publishedIpAddress_; } - IpAddr getPublishedIpAddress() const; + IpAddr getPublishedIpAddress(uint16_t family = PF_UNSPEC) const; void setPublishedAddress(const IpAddr& ip_addr); diff --git a/src/upnp/upnp_context.cpp b/src/upnp/upnp_context.cpp index 7c881616240c5f70d184134ab916749ace6ee064..09ade02c30377b293c78457d613b5e207e4811c8 100644 --- a/src/upnp/upnp_context.cpp +++ b/src/upnp/upnp_context.cpp @@ -164,6 +164,20 @@ UPnPContext::connectivityChanged() StopUpnp(); StartUpnp(); + + processMappingWithAutoUpdate(); +} + +void +UPnPContext::setPublicAddress(const IpAddr& addr) +{ + if (not addr) + return; + + JAMI_DBG("Setting the known public address to %s", addr.toString().c_str()); + + std::lock_guard<std::mutex> lock(mappingMutex_); + knownPublicAddress_ = std::move(addr); } bool @@ -250,7 +264,8 @@ UPnPContext::releaseMapping(const Mapping& map) auto mapPtr = getMappingWithKey(map.getMapKey()); if (not mapPtr) { - JAMI_WARN("Trying to release an unknown mapping %s", map.toString().c_str()); + // Might happen if the mapping failed + JAMI_DBG("Mapping %s does not exist or was already removed", map.toString().c_str()); return false; } @@ -285,7 +300,6 @@ UPnPContext::registerController(void* controller) } JAMI_DBG("Successfully registered controller %p", this); - if (not started_) StartUpnp(); } @@ -453,7 +467,6 @@ UPnPContext::deleteUnneededMappings(PortType type, int portCount) // mappings than required. So, all mappings in a state other // than "OPEN" state (typically in in-progress state) will // be deleted as well. - deleteMapping(map); it = unregisterMapping(it); } else { it++; @@ -773,6 +786,8 @@ UPnPContext::processMappingWithAutoUpdate() registerMapping(newMapping); // Release the old one. map->setAvailable(true); + map->enableAutoUpdate(false); + map->setNotifyCallback(nullptr); unregisterMapping(map); } } @@ -789,25 +804,34 @@ UPnPContext::onIgdUpdated(const std::shared_ptr<IGD>& igd, UpnpIgdEvent event) CHECK_VALID_THREAD(); - auto const& igdAddr = igd->getLocalIp(); + auto const& igdLocalAddr = igd->getLocalIp(); auto protocolName = igd->getProtocolName(); // Check if IGD has a valid addresses. - if (not igdAddr) { + if (not igdLocalAddr) { JAMI_ERR("[%s] IGD has an invalid local address", protocolName); return; } + if (not igd->getPublicIp()) { - JAMI_ERR("[%s] IGD IGD has an invalid public address", protocolName); + JAMI_ERR("[%s] IGD has an invalid public address", protocolName); return; } - char const* IgdState = event == UpnpIgdEvent::ADDED - ? "ADDED" - : event == UpnpIgdEvent::REMOVED ? "REMOVED" : "INVALID"; + if (knownPublicAddress_ and igd->getPublicIp() != knownPublicAddress_) { + JAMI_WARN("[%s] IGD external address [%s] does not match known public address [%s]." + " The mapped addresses might not be reachable", + protocolName, + igd->getPublicIp().toString().c_str(), + knownPublicAddress_.toString().c_str()); + } + + char const* IgdState = event == UpnpIgdEvent::ADDED ? "ADDED" + : event == UpnpIgdEvent::REMOVED ? "REMOVED" + : "INVALID"; JAMI_WARN("State of IGD %s [%s] changed to [%s]", - igdAddr.toString(true, true).c_str(), + igdLocalAddr.toString(true, true).c_str(), protocolName, IgdState); @@ -827,12 +851,12 @@ UPnPContext::onIgdUpdated(const std::shared_ptr<IGD>& igd, UpnpIgdEvent event) if (ret.second) { JAMI_DBG("IGD [%s] on address %s was added. Will process any pending requests", protocolName, - igdAddr.toString(true, true).c_str()); + igdLocalAddr.toString(true, true).c_str()); } else { // Already in the list. JAMI_ERR("IGD [%s] on address %s already in the list", protocolName, - igdAddr.toString(true, true).c_str()); + igdLocalAddr.toString(true, true).c_str()); return; } } @@ -876,10 +900,10 @@ UPnPContext::onMappingAdded(const std::shared_ptr<IGD>& igd, const Mapping& mapR if (it == mappingList.end()) { // We may receive a response for a canceled request. Just // ignore it. - JAMI_WARN("Mapping request %s from IGD %s [%s] does not match local request", - mapRes.toString().c_str(), - igd->getLocalIp().toString().c_str(), - mapRes.getProtocolName()); + JAMI_DBG("Mapping request %s from IGD %s [%s] does not match local request", + mapRes.toString().c_str(), + igd->getLocalIp().toString().c_str(), + mapRes.getProtocolName()); return; } key = it->second->getMapKey(); @@ -1078,6 +1102,10 @@ UPnPContext::unregisterMapping(std::map<Mapping::key_t, Mapping::sharedPtr_t>::i void UPnPContext::unregisterMapping(const Mapping::sharedPtr_t& map) { + if (map->getAutoUpdate()) { + // Dont unregister mappings with auto-update enabled. + return; + } auto& mappingList = getMappingList(map->getType()); if (mappingList.erase(map->getMapKey()) == 1) { diff --git a/src/upnp/upnp_context.h b/src/upnp/upnp_context.h index 8d98d6cef4384436c9d02ca8e1a827042e527e45..566e87b61da7cf85844b28d52722a6b120d8e037 100644 --- a/src/upnp/upnp_context.h +++ b/src/upnp/upnp_context.h @@ -105,6 +105,9 @@ public: // Retrieve the UPnPContext singleton static std::shared_ptr<UPnPContext> getUPnPContext(); + // Set the known public address + void setPublicAddress(const IpAddr& addr); + // Check if there is a valid IGD in the IGD list. bool hasValidIGD() const; @@ -245,6 +248,10 @@ private: bool started_ {false}; + // The known public address. The external addresses returned by + // the IGDs will be checked against this address. + IpAddr knownPublicAddress_ {}; + // Set of registered controllers std::set<void*> controllerList_; diff --git a/src/upnp/upnp_control.cpp b/src/upnp/upnp_control.cpp index 69829e0b8040b9383a52253b81664197215dc67f..72048aa23dffec5803dcf9d36426acef58f6c51e 100644 --- a/src/upnp/upnp_control.cpp +++ b/src/upnp/upnp_control.cpp @@ -38,6 +38,16 @@ Controller::Controller() JAMI_DBG("Controller@%p: Created UPnP Controller session", this); } +Controller::Controller(const IpAddr& addr) + : Controller() +{ + assert(upnpContext_); + assert(addr); + assert(addr.getFamily() == AF_INET); + + upnpContext_->setPublicAddress(addr); +} + Controller::~Controller() { JAMI_DBG("Controller@%p: Destroying UPnP Controller session", this); diff --git a/src/upnp/upnp_control.h b/src/upnp/upnp_control.h index 920feb4ac4725079af0434196c283a3bc4489830..87714f12e8cbf353642d71994dee30811fda191c 100644 --- a/src/upnp/upnp_control.h +++ b/src/upnp/upnp_control.h @@ -47,6 +47,7 @@ class Controller { public: Controller(); + Controller(const IpAddr& addr); ~Controller(); // Checks if a valid IGD is available.