allAddrs property

List<MultiAddr> get allAddrs

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;
}