@@ -67,7 +67,7 @@ static constexpr uint16_t IPV4_HEADER_SIZE = 20; ///< Size in bytes of IPV4 pack
67
67
static constexpr int MAX_CANDIDATES {32 };
68
68
static constexpr int MAX_DESTRUCTION_TIMEOUT {3000 };
69
69
static constexpr int HANDLE_EVENT_DURATION {500 };
70
-
70
+ static constexpr std::chrono::seconds PORT_MAPPING_TIMEOUT { 4 };
71
71
// ==============================================================================
72
72
73
73
using namespace upnp ;
@@ -210,10 +210,12 @@ class IceTransport::Impl
210
210
};
211
211
212
212
std::shared_ptr<upnp::Controller> upnp_ {};
213
- std::mutex upnpMutex_ {};
214
213
std::map<Mapping::key_t , Mapping> upnpMappings_;
215
214
std::mutex upnpMappingsMutex_ {};
216
215
216
+ std::mutex upnpMutex_ {};
217
+ std::condition_variable upnpCv_;
218
+
217
219
bool onlyIPv4Private_ {true };
218
220
219
221
// IO/Timer events are handled by following thread
@@ -922,44 +924,74 @@ IceTransport::Impl::addStunConfig(int af)
922
924
void
923
925
IceTransport::Impl::requestUpnpMappings ()
924
926
{
925
- // Must be called once !
926
-
927
- std::lock_guard lock (upnpMutex_);
928
-
929
927
if (not upnp_)
930
928
return ;
931
-
932
929
auto transport = isTcpEnabled () ? PJ_CAND_TCP_PASSIVE : PJ_CAND_UDP;
933
930
auto portType = transport == PJ_CAND_UDP ? PortType::UDP : PortType::TCP;
934
931
932
+ // Use a different map instead of upnpMappings_ to store pointers to the mappings
933
+ auto upnpMappings = std::make_shared<std::map<Mapping::key_t , Mapping::sharedPtr_t>>();
934
+ auto isFailed = std::make_shared<bool >(false );
935
+
936
+ std::unique_lock lock (upnpMutex_);
937
+
935
938
// Request upnp mapping for each component.
936
939
for (unsigned id = 1 ; id <= compCount_; id++) {
937
940
// Set port number to 0 to get any available port.
938
941
Mapping requestedMap (portType);
939
942
940
- // Request the mapping
941
- Mapping::sharedPtr_t mapPtr = upnp_->reserveMapping (requestedMap);
942
-
943
- // To use a mapping, it must be valid, open and has valid host address.
944
- if (mapPtr and mapPtr->getMapKey () and (mapPtr->getState () == MappingState::OPEN)
945
- and mapPtr->hasValidHostAddress ()) {
946
- std::lock_guard lock (upnpMappingsMutex_);
947
- auto ret = upnpMappings_.emplace (mapPtr->getMapKey (), *mapPtr);
948
- if (ret.second ) {
943
+ requestedMap.setNotifyCallback ([upnpMappings, isFailed, this ](Mapping::sharedPtr_t mapPtr) {
944
+ // Ignore intermidiate states : PENDING, IN_PROGRESS
945
+ // only OPEN and FAILED are considered
946
+
947
+ // if the mapping is open check the validity
948
+ if ((mapPtr->getState () == MappingState::OPEN)) {
949
+ if (mapPtr->getMapKey () and mapPtr->hasValidHostAddress ()){
950
+ std::lock_guard lockMapping (upnpMappingsMutex_);
951
+ upnpMappings->emplace (mapPtr->getMapKey (), mapPtr);
952
+ } else {
953
+ *isFailed = true ;
954
+ }
955
+ } else if (mapPtr->getState () == MappingState::FAILED) {
956
+ *isFailed = true ;
949
957
if (logger_)
950
- logger_->debug (" [ice:{}] UPNP mapping {:s} successfully allocated" ,
951
- fmt::ptr (this ),
952
- mapPtr->toString (true ));
953
- } else {
954
- if (logger_)
955
- logger_->warn (" [ice:{}] UPNP mapping {:s} already in the list!" ,
956
- fmt::ptr (this ),
957
- mapPtr->toString ());
958
+ logger_->error (" [ice:{}] UPNP mapping failed: {:s}" ,
959
+ fmt::ptr (this ),
960
+ mapPtr->toString (true ));
958
961
}
959
- } else {
960
- if (logger_)
961
- logger_->warn (" [ice:{}] UPNP mapping request failed!" , fmt::ptr (this ));
962
- upnp_->releaseMapping (requestedMap);
962
+ upnpCv_.notify_all ();
963
+ });
964
+ // Request the mapping
965
+ upnp_->reserveMapping (requestedMap);
966
+ }
967
+ upnpCv_.wait_for (lock, PORT_MAPPING_TIMEOUT, [&] {
968
+ return upnpMappings->size () == compCount_ or *isFailed;
969
+ });
970
+
971
+ std::lock_guard lockMapping (upnpMappingsMutex_);
972
+
973
+ // remove the notify callback
974
+ for (auto & map : *upnpMappings) {
975
+ map.second ->setNotifyCallback (nullptr );
976
+ }
977
+ // Check the number of mappings
978
+ if (upnpMappings->size () != compCount_) {
979
+ if (logger_)
980
+ logger_->error (" [ice:{}] UPNP mapping failed: expected {:d} mappings, got {:d}" ,
981
+ fmt::ptr (this ),
982
+ compCount_,
983
+ upnpMappings->size ());
984
+ // release all mappings
985
+ for (auto & map : *upnpMappings) {
986
+ upnp_->releaseMapping (*map.second );
987
+ }
988
+ } else {
989
+ for (auto & map : *upnpMappings) {
990
+ upnpMappings_.emplace (map.first , *map.second );
991
+ if (logger_)
992
+ logger_->debug (" [ice:{}] UPNP mapping {:s} successfully allocated\n " ,
993
+ fmt::ptr (this ),
994
+ map.second ->toString (true ));
963
995
}
964
996
}
965
997
}
0 commit comments