@@ -209,7 +209,7 @@ UPnPContext::stopUpnp(bool forceRelease)
209
209
// gets recreated if we restart UPnP later.
210
210
map->setState (MappingState::PENDING);
211
211
} else {
212
- unregisterMapping (map, true );
212
+ unregisterMapping (map);
213
213
}
214
214
}
215
215
@@ -814,7 +814,7 @@ UPnPContext::_syncLocalMappingListWithIgd()
814
814
Mapping::UPNP_MAPPING_DESCRIPTION_PREFIX);
815
815
bool requestsInProgress = false ;
816
816
// Use a temporary list to avoid processing mappings while holding the lock.
817
- std::list<Mapping::sharedPtr_t> toRemoveFromLocalList ;
817
+ std::list<Mapping::sharedPtr_t> failedMappings ;
818
818
for (auto type: {PortType::TCP, PortType::UDP}) {
819
819
std::lock_guard lock (mappingMutex_);
820
820
for (auto & [_, map] : getMappingList (type)) {
@@ -830,10 +830,10 @@ UPnPContext::_syncLocalMappingListWithIgd()
830
830
auto it = remoteMapList.find (map->getMapKey ());
831
831
if (it == remoteMapList.end ()) {
832
832
if (logger_) logger_->warn (" Mapping {} (IGD {}) marked as \" OPEN\" but not found in the "
833
- " remote list. Removing from local list ." ,
833
+ " remote list. Setting state to \" FAILED \" ." ,
834
834
map->toString (),
835
835
igd->toString ());
836
- toRemoveFromLocalList .emplace_back (map);
836
+ failedMappings .emplace_back (map);
837
837
} else {
838
838
auto oldExpiryTime = map->getExpiryTime ();
839
839
auto newExpiryTime = it->second .getExpiryTime ();
@@ -860,11 +860,10 @@ UPnPContext::_syncLocalMappingListWithIgd()
860
860
}
861
861
scheduleMappingsRenewal ();
862
862
863
- for (auto const & map : toRemoveFromLocalList ) {
863
+ for (auto const & map : failedMappings ) {
864
864
updateMappingState (map, MappingState::FAILED);
865
- unregisterMapping (map);
866
865
}
867
- if (!toRemoveFromLocalList .empty ())
866
+ if (!failedMappings .empty ())
868
867
enforceAvailableMappingsLimits ();
869
868
870
869
if (requestsInProgress) {
@@ -924,7 +923,6 @@ UPnPContext::pruneMappingsWithInvalidIgds(const std::shared_ptr<IGD>& igd)
924
923
igd->toString (),
925
924
igd->getProtocolName ());
926
925
updateMappingState (map, MappingState::FAILED);
927
- unregisterMapping (map);
928
926
}
929
927
}
930
928
@@ -1130,19 +1128,27 @@ UPnPContext::_endIgdDiscovery()
1130
1128
if (isReady ()) {
1131
1129
return ;
1132
1130
}
1133
- // if there is no valid IGD, the pending mapping requests will be changed to failed
1134
- std::lock_guard lockMappings_ (mappingMutex_);
1135
- PortType types[2 ] {PortType::TCP, PortType::UDP};
1136
- for (auto & type : types) {
1137
- const auto & mappingList = getMappingList (type);
1138
- for (auto const & [_, map] : mappingList) {
1139
- updateMappingState (map, MappingState::FAILED);
1140
- // Do not unregister the mapping, it's up to the controller to decide. It will be unregistered when the controller releases it.
1141
- // unregisterMapping(map) here will cause a deadlock because of the lock on mappingMutex_.
1142
- if (logger_) logger_->warn (" Request for mapping {} failed, no IGD available" ,
1143
- map->toString ());
1131
+
1132
+ // Use a temporary list to avoid holding the lock while processing the mapping list.
1133
+ // This is necessary because updateMappingState calls a user-defined callback, which
1134
+ // in turn could end up calling a function (such as reserveMapping) that would also
1135
+ // attempt to lock mappingMutex_.
1136
+ std::list<Mapping::sharedPtr_t> toRemoveList;
1137
+ {
1138
+ std::lock_guard lock (mappingMutex_);
1139
+ PortType types[2 ] {PortType::TCP, PortType::UDP};
1140
+ for (auto type : types) {
1141
+ const auto & mappingList = getMappingList (type);
1142
+ for (const auto & [_, map] : mappingList) {
1143
+ toRemoveList.emplace_back (map);
1144
+ }
1144
1145
}
1145
1146
}
1147
+ // We reached the end of the IGD discovery period without finding a valid IGD,
1148
+ // so all mapping requests are considered to have failed.
1149
+ for (auto const & map : toRemoveList) {
1150
+ updateMappingState (map, MappingState::FAILED);
1151
+ }
1146
1152
}
1147
1153
1148
1154
void
@@ -1204,25 +1210,12 @@ UPnPContext::registerMapping(Mapping& map)
1204
1210
}
1205
1211
1206
1212
void
1207
- UPnPContext::unregisterMapping (const Mapping::sharedPtr_t& map, bool ignoreAutoUpdate )
1213
+ UPnPContext::unregisterMapping (const Mapping::sharedPtr_t& map)
1208
1214
{
1209
1215
if (not map) {
1210
1216
return ;
1211
1217
}
1212
1218
1213
- if (map->getAutoUpdate () && !ignoreAutoUpdate) {
1214
- if (logger_) logger_->debug (" Mapping {} has auto-update enabled, a new mapping will be requested" ,
1215
- map->toString ());
1216
-
1217
- Mapping newMapping (map->getType ());
1218
- newMapping.enableAutoUpdate (true );
1219
- newMapping.setNotifyCallback (map->getNotifyCallback ());
1220
- reserveMapping (newMapping);
1221
-
1222
- // TODO: figure out if this line is actually necessary
1223
- // (See https://review.jami.net/c/jami-daemon/+/16940)
1224
- map->setNotifyCallback (nullptr );
1225
- }
1226
1219
std::lock_guard lock (mappingMutex_);
1227
1220
auto & mappingList = getMappingList (map->getType ());
1228
1221
@@ -1268,7 +1261,6 @@ UPnPContext::onMappingRequestFailed(const Mapping& mapRes)
1268
1261
}
1269
1262
1270
1263
updateMappingState (map, MappingState::FAILED);
1271
- unregisterMapping (map);
1272
1264
1273
1265
if (logger_) logger_->warn (" Request for mapping {} on IGD {} failed" ,
1274
1266
map->toString (),
@@ -1293,6 +1285,39 @@ UPnPContext::updateMappingState(const Mapping::sharedPtr_t& map, MappingState ne
1293
1285
// Notify the listener if set.
1294
1286
if (notify and map->getNotifyCallback ())
1295
1287
map->getNotifyCallback ()(map);
1288
+
1289
+ if (newState == MappingState::FAILED)
1290
+ handleFailedMapping (map);
1291
+
1292
+ }
1293
+
1294
+ void
1295
+ UPnPContext::handleFailedMapping (const Mapping::sharedPtr_t& map)
1296
+ {
1297
+ if (!map->getAutoUpdate ()) {
1298
+ // If the mapping is not set to auto-update, it will be unregistered.
1299
+ unregisterMapping (map);
1300
+ return ;
1301
+ }
1302
+
1303
+ if (isReady ()) {
1304
+ Mapping newMapping (map->getType ());
1305
+ newMapping.enableAutoUpdate (true );
1306
+ newMapping.setNotifyCallback (map->getNotifyCallback ());
1307
+ reserveMapping (newMapping);
1308
+ if (logger_) logger_->debug (" Mapping {} has auto-update enabled, a new mapping will be requested" ,
1309
+ map->toString ());
1310
+
1311
+ // TODO: figure out if this line is actually necessary
1312
+ // (See https://review.jami.net/c/jami-daemon/+/16940)
1313
+ map->setNotifyCallback (nullptr );
1314
+ } else {
1315
+ // If there is no valid IGD, the mapping is marked as pending
1316
+ // and will be requested when an IGD becomes available.
1317
+ updateMappingState (map, MappingState::PENDING, false );
1318
+ if (logger_) logger_->debug (" Mapping {} will be requested when an IGD becomes available" ,
1319
+ map->toString ());
1320
+ }
1296
1321
}
1297
1322
1298
1323
} // namespace upnp
0 commit comments