allAddrs property
Returns all addresses the host is listening on.
Implementation
List<MultiAddr> get allAddrs {
// _log.fine('[BasicHost allAddrs] for host ${id.toString()} - Called.');
final currentListenAddrs = _network.listenAddresses;
// _log.fine('[BasicHost allAddrs] for host ${id.toString()} - _network.listenAddresses: $currentListenAddrs');
// _log.fine('[BasicHost allAddrs] for host ${id.toString()} - _filteredInterfaceAddrs: $_filteredInterfaceAddrs');
final observedFromID = _idService.ownObservedAddrs();
// _log.fine('[BasicHost allAddrs] for host ${id.toString()} - _idService.ownObservedAddrs(): $observedFromID');
if (currentListenAddrs.isEmpty && observedFromID.isEmpty) {
// _log.fine('[BasicHost allAddrs] for host ${id.toString()} - Returning early: no listen or observed addrs.');
return [];
}
final resolvedAddrs = <MultiAddr>{}; // Use a Set to handle duplicates
// 1. Resolve unspecified listen addresses using filtered interface addresses
// (obtained by _updateLocalIpAddr)
for (final listenAddr in currentListenAddrs) {
final listenIp4 = listenAddr.valueForProtocol('ip4');
final listenIp6 = listenAddr.valueForProtocol('ip6');
final isUnspecified = (listenIp4 == '0.0.0.0' || listenIp4 == '0.0.0.0.0.0') || (listenIp6 == '::' || listenIp6 == '0:0:0:0:0:0:0:0');
if (isUnspecified) {
// Resolve unspecified listen addresses (e.g., /ip4/0.0.0.0/udp/port/udx)
// by combining them with specific interface addresses.
String suffixString = '';
final listenComponents = listenAddr.components;
int ipComponentIndex = -1;
// Find the index of the first IP component
for (int i = 0; i < listenComponents.length; i++) {
final protoName = listenComponents[i].$1.name;
if (protoName == 'ip4' || protoName == 'ip6') {
ipComponentIndex = i;
break;
}
}
if (ipComponentIndex != -1 && ipComponentIndex < listenComponents.length - 1) {
// If an IP component was found and it's not the last component,
// construct the suffix string from subsequent components.
final suffixComponents = listenComponents.sublist(ipComponentIndex + 1);
final sb = StringBuffer();
for (final (protocol, value) in suffixComponents) {
sb.write('/${protocol.name}');
// Only add value if protocol is not size 0 (e.g., for /udx)
// or if the value is not empty (for protocols that might have optional values, though less common here)
if (protocol.size != 0 || value.isNotEmpty) {
sb.write('/$value');
}
}
suffixString = sb.toString();
} else if (ipComponentIndex == -1) {
_log.fine('Listen address $listenAddr does not start with ip4 or ip6, cannot resolve unspecified.');
}
// If ipComponentIndex is the last component, suffixString remains empty, which is correct.
if (suffixString.isNotEmpty) {
if (_filteredInterfaceAddrs.isNotEmpty) {
for (final interfaceAddr in _filteredInterfaceAddrs) {
// interfaceAddr is a bare IP MultiAddr, e.g., /ip4/192.168.10.118
try {
// Combine the interface address string with the suffix string
final combinedAddrString = interfaceAddr.toString() + suffixString;
final newAddr = MultiAddr(combinedAddrString);
resolvedAddrs.add(newAddr);
_log.finer('Resolved unspecified listen addr: $listenAddr with interface $interfaceAddr to ${newAddr.toString()}');
} catch (e) {
_log.severe('Failed to create resolved address by encapsulating $interfaceAddr with suffix $suffixString (from $listenAddr): $e');
}
}
} else {
// No interface addresses available - trigger interface discovery and warn
_log.warning('No interface addresses available to resolve unspecified listen address: $listenAddr. Triggering interface discovery.');
_updateLocalIpAddr(); // Try to update interface addresses
// If still no interface addresses after update, this unspecified address cannot be resolved
if (_filteredInterfaceAddrs.isEmpty) {
_log.warning('Interface discovery failed or returned no addresses. Unspecified listen address $listenAddr cannot be resolved to concrete addresses.');
}
}
} else if (isUnspecified) {
// This case means it was an unspecified IP, but we couldn't get a suffix
// (e.g., listenAddr was just /ip4/0.0.0.0).
_log.warning('Could not determine suffix for unspecified listen address: $listenAddr. It will not be resolved against interface IPs.');
// We don't add the original unspecified listenAddr to resolvedAddrs here,
// as it would be filtered out by defaultAddrsFactory anyway.
}
} else {
// Address is already specific, add it directly.
resolvedAddrs.add(listenAddr);
}
}
// If there were no listen addresses, but we have interface addresses,
// this part might need refinement. Typically, host addresses are listen addresses.
// If currentListenAddrs is empty, resolvedAddrs will be empty here.
final natAppliedAddrs = <MultiAddr>{};
// 2. Apply NAT mappings if available
if (_natmgr != null && _natmgr!.hasDiscoveredNAT()) {
for (final addr in resolvedAddrs) {
// If resolvedAddrs is empty (e.g. all listen addrs were unspecified and no interface addrs found),
// try to map original listen addrs.
final mapped = _natmgr!.getMapping(addr);
if (mapped != null) {
natAppliedAddrs.add(mapped);
} else {
natAppliedAddrs.add(addr); // Keep original if no mapping
}
}
} else {
natAppliedAddrs.addAll(resolvedAddrs);
}
// If resolvedAddrs was empty and currentListenAddrs was not, but NAT manager didn't map anything,
// natAppliedAddrs would still be based on resolvedAddrs (empty).
// We should ensure that if resolvedAddrs is empty due to no interface IPs for 0.0.0.0,
// we still consider the original listen addresses for NAT mapping or inclusion.
// Let's refine: if resolvedAddrs is empty but currentListenAddrs is not, it means all were unspecified
// and no local IPs were found. In this case, `allAddrs` should probably be empty or only observed.
final finalAddrs = <MultiAddr>{};
finalAddrs.addAll(natAppliedAddrs);
// 3. Add observed addresses from identify service
// These are addresses observed by other peers, potentially behind NAT.
final observed = _idService.ownObservedAddrs();
finalAddrs.addAll(observed);
// If after all this, finalAddrs is empty, but we had original listen addresses,
// it implies they were all unspecified, no local IPs found, no NAT mapping, and no observed.
// This scenario should result in an empty list.
// Convert Set to List for the return type.
// The _addrsFactory can then do further filtering/sorting.
final result = finalAddrs.toList();
_log.fine('[BasicHost allAddrs] for host ${id.toString()} - resolvedAddrs: $resolvedAddrs');
_log.fine('[BasicHost allAddrs] for host ${id.toString()} - natAppliedAddrs: $natAppliedAddrs');
_log.fine('[BasicHost allAddrs] for host ${id.toString()} - finalAddrs (before toList): $finalAddrs');
_log.fine('[BasicHost allAddrs] for host ${id.toString()} - Returning: $result');
return result;
}