diff --git a/libp2p/builders.nim b/libp2p/builders.nim index d16d1eb065..f5293573c4 100644 --- a/libp2p/builders.nim +++ b/libp2p/builders.nim @@ -19,7 +19,8 @@ runnableExamples: {.push raises: [].} import - options, tables, chronos, chronicles, sequtils, + options, tables, chronos, chronicles, sequtils +import switch, peerid, peerinfo, stream/connection, multiaddress, crypto/crypto, transports/[transport, tcptransport], muxers/[muxer, mplex/mplex, yamux/yamux], @@ -28,6 +29,7 @@ import connmanager, upgrademngrs/muxedupgrade, observedaddrmanager, nameresolving/nameresolver, errors, utility +import services/wildcardresolverservice export switch, peerid, peerinfo, connection, multiaddress, crypto, errors @@ -59,6 +61,7 @@ type rdv: RendezVous services: seq[Service] observedAddrManager: ObservedAddrManager + enableWildcardResolver: bool proc new*(T: type[SwitchBuilder]): T {.public.} = ## Creates a SwitchBuilder @@ -76,7 +79,8 @@ proc new*(T: type[SwitchBuilder]): T {.public.} = maxOut: -1, maxConnsPerPeer: MaxConnectionsPerPeer, protoVersion: ProtoVersion, - agentVersion: AgentVersion) + agentVersion: AgentVersion, + enableWildcardResolver: true) proc withPrivateKey*(b: SwitchBuilder, privateKey: PrivateKey): SwitchBuilder {.public.} = ## Set the private key of the switch. Will be used to @@ -85,20 +89,18 @@ proc withPrivateKey*(b: SwitchBuilder, privateKey: PrivateKey): SwitchBuilder {. b.privKey = some(privateKey) b -proc withAddress*(b: SwitchBuilder, address: MultiAddress): SwitchBuilder {.public.} = - ## | Set the listening address of the switch - ## | Calling it multiple time will override the value - - b.addresses = @[address] - b - -proc withAddresses*(b: SwitchBuilder, addresses: seq[MultiAddress]): SwitchBuilder {.public.} = +proc withAddresses*(b: SwitchBuilder, addresses: seq[MultiAddress], enableWildcardResolver: bool = true): SwitchBuilder {.public.} = ## | Set the listening addresses of the switch ## | Calling it multiple time will override the value - b.addresses = addresses + b.enableWildcardResolver = enableWildcardResolver b +proc withAddress*(b: SwitchBuilder, address: MultiAddress, enableWildcardResolver: bool = true): SwitchBuilder {.public.} = + ## | Set the listening address of the switch + ## | Calling it multiple time will override the value + b.withAddresses(@[address], enableWildcardResolver) + proc withSignedPeerRecord*(b: SwitchBuilder, sendIt = true): SwitchBuilder {.public.} = b.sendSignedPeerRecord = sendIt b @@ -261,6 +263,9 @@ proc build*(b: SwitchBuilder): Switch else: PeerStore.new(identify) + if b.enableWildcardResolver: + b.services.insert(WildcardAddressResolverService.new(), 0) + let switch = newSwitch( peerInfo = peerInfo, transports = transports, diff --git a/libp2p/dialer.nim b/libp2p/dialer.nim index d3cb926b7f..ade169bdf3 100644 --- a/libp2p/dialer.nim +++ b/libp2p/dialer.nim @@ -137,7 +137,7 @@ proc dialAndUpgrade( dir = Direction.Out): Future[Muxer] {.async.} = - debug "Dialing peer", peerId = peerId.get(default(PeerId)) + debug "Dialing peer", peerId = peerId.get(default(PeerId)), addrs for rawAddress in addrs: # resolve potential dnsaddr diff --git a/libp2p/multicodec.nim b/libp2p/multicodec.nim index de28689fe4..2e8105d9be 100644 --- a/libp2p/multicodec.nim +++ b/libp2p/multicodec.nim @@ -12,7 +12,7 @@ {.push raises: [].} import tables, hashes -import varint, vbuffer +import vbuffer import stew/results export results diff --git a/libp2p/peerinfo.nim b/libp2p/peerinfo.nim index a31f42eb5f..c290894edf 100644 --- a/libp2p/peerinfo.nim +++ b/libp2p/peerinfo.nim @@ -24,12 +24,16 @@ type AddressMapper* = proc(listenAddrs: seq[MultiAddress]): Future[seq[MultiAddress]] {.gcsafe, raises: [].} + ## A proc that expected to resolve the listen addresses into dialable addresses PeerInfo* {.public.} = ref object peerId*: PeerId listenAddrs*: seq[MultiAddress] - addrs: seq[MultiAddress] + ## contains addresses the node listens on, which may include wildcard and private addresses (not directly reachable). + addrs*: seq[MultiAddress] + ## contains resolved addresses that other peers can use to connect, including public-facing NAT and port-forwarded addresses. addressMappers*: seq[AddressMapper] + ## contains a list of procs that can be used to resolve the listen addresses into dialable addresses. protocols*: seq[string] protoVersion*: string agentVersion*: string @@ -49,7 +53,11 @@ func shortLog*(p: PeerInfo): auto = chronicles.formatIt(PeerInfo): shortLog(it) proc update*(p: PeerInfo) {.async.} = - p.addrs = p.listenAddrs + # p.addrs.len == 0 overrides addrs only if it is the first time update is being executed or if the field is empty. + # p.addressMappers.len == 0 is for when all addressMappers have been removed, + # and we wish to have addrs in its initial state, i.e., a copy of listenAddrs. + if p.addrs.len == 0 or p.addressMappers.len == 0: + p.addrs = p.listenAddrs for mapper in p.addressMappers: p.addrs = await mapper(p.addrs) diff --git a/libp2p/services/autorelayservice.nim b/libp2p/services/autorelayservice.nim index c94aed13da..259287e3f9 100644 --- a/libp2p/services/autorelayservice.nim +++ b/libp2p/services/autorelayservice.nim @@ -38,7 +38,7 @@ proc isRunning*(self: AutoRelayService): bool = proc addressMapper( self: AutoRelayService, listenAddrs: seq[MultiAddress]): Future[seq[MultiAddress]] {.async.} = - return concat(toSeq(self.relayAddresses.values)) + return concat(toSeq(self.relayAddresses.values)) & listenAddrs proc reserveAndUpdate(self: AutoRelayService, relayPid: PeerId, switch: Switch) {.async.} = while self.running: diff --git a/libp2p/services/wildcardresolverservice.nim b/libp2p/services/wildcardresolverservice.nim new file mode 100644 index 0000000000..78e81b9203 --- /dev/null +++ b/libp2p/services/wildcardresolverservice.nim @@ -0,0 +1,205 @@ +# Nim-LibP2P +# Copyright (c) 2024 Status Research & Development GmbH +# Licensed under either of +# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) +# * MIT license ([LICENSE-MIT](LICENSE-MIT)) +# at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. + +{.push raises: [].} + +import std/sequtils +import stew/[byteutils, results, endians2] +import chronos, chronos/transports/[osnet, ipnet], chronicles +import ../[multiaddress, multicodec] +import ../switch + +logScope: + topics = "libp2p wildcardresolverservice" + +type + WildcardAddressResolverService* = ref object of Service + ## Service used to resolve wildcard addresses of the type "0.0.0.0" for IPv4 or "::" for IPv6. + ## When used with a `Switch`, this service will be automatically set up and stopped + ## when the switch starts and stops. This is facilitated by adding the service to the switch's + ## list of services using the `.withServices(@[svc])` method in the `SwitchBuilder`. + networkInterfaceProvider: NetworkInterfaceProvider + ## Provides a list of network addresses. + addressMapper: AddressMapper + ## An implementation of an address mapper that takes a list of listen addresses and expands each wildcard address + ## to the respective list of interface addresses. As an example, if the listen address is 0.0.0.0:4001 + ## and the machine has 2 interfaces with IPs 172.217.11.174 and 64.233.177.113, the address mapper will + ## expand the wildcard address to 172.217.11.174:4001 and 64.233.177.113:4001. + + NetworkInterfaceProvider* = + proc(addrFamily: AddressFamily): seq[InterfaceAddress] {.gcsafe, raises: [].} + +proc isLoopbackOrUp(networkInterface: NetworkInterface): bool = + if (networkInterface.ifType == IfSoftwareLoopback) or + (networkInterface.state == StatusUp): true else: false + +proc getAddresses(addrFamily: AddressFamily): seq[InterfaceAddress] = + ## Retrieves the addresses of network interfaces based on the specified address family. + ## + ## It filters the available network interfaces to include only + ## those that are either loopback or up. It then collects all the addresses from these + ## interfaces and filters them to match the provided address family. + ## + ## Parameters: + ## - `addrFamily`: The address family to filter the network addresses (e.g., `AddressFamily.IPv4` or `AddressFamily.IPv6`). + ## + ## Returns: + ## - A sequence of `InterfaceAddress` objects that match the specified address family. + let + interfaces = getInterfaces().filterIt(it.isLoopbackOrUp()) + flatInterfaceAddresses = concat(interfaces.mapIt(it.addresses)) + filteredInterfaceAddresses = + flatInterfaceAddresses.filterIt(it.host.family == addrFamily) + return filteredInterfaceAddresses + +proc new*( + T: typedesc[WildcardAddressResolverService], + networkInterfaceProvider: NetworkInterfaceProvider = getAddresses, +): T = + ## This procedure initializes a new `WildcardAddressResolverService` with the provided network interface provider. + ## + ## Parameters: + ## - `T`: The type descriptor for `WildcardAddressResolverService`. + ## - `networkInterfaceProvider`: A provider that offers access to network interfaces. Defaults to a new instance of `NetworkInterfaceProvider`. + ## + ## Returns: + ## - A new instance of `WildcardAddressResolverService`. + return T(networkInterfaceProvider: networkInterfaceProvider) + +proc getProtocolArgument*(ma: MultiAddress, codec: MultiCodec): MaResult[seq[byte]] = + var buffer: seq[byte] + for item in ma: + let + ritem = ?item + code = ?ritem.protoCode() + if code == codec: + let arg = ?ritem.protoAddress() + return ok(arg) + + err("Multiaddress codec has not been found") + +proc getWildcardMultiAddresses( + interfaceAddresses: seq[InterfaceAddress], protocol: Protocol, port: Port +): seq[MultiAddress] = + var addresses: seq[MultiAddress] + for ifaddr in interfaceAddresses: + var address = ifaddr.host + address.port = port + MultiAddress.init(address, protocol).withValue(maddress): + addresses.add(maddress) + addresses + +proc getWildcardAddress( + maddress: MultiAddress, + multiCodec: MultiCodec, + anyAddr: openArray[uint8], + addrFamily: AddressFamily, + port: Port, + networkInterfaceProvider: NetworkInterfaceProvider, +): seq[MultiAddress] = + var addresses: seq[MultiAddress] + maddress.getProtocolArgument(multiCodec).withValue(address): + if address == anyAddr: + let filteredInterfaceAddresses = networkInterfaceProvider(addrFamily) + addresses.add( + getWildcardMultiAddresses(filteredInterfaceAddresses, IPPROTO_TCP, port) + ) + else: + addresses.add(maddress) + return addresses + +proc expandWildcardAddresses( + networkInterfaceProvider: NetworkInterfaceProvider, listenAddrs: seq[MultiAddress] +): seq[MultiAddress] = + var addresses: seq[MultiAddress] + # In this loop we expand bound addresses like `0.0.0.0` and `::` to list of interface addresses. + for listenAddr in listenAddrs: + if TCP_IP.matchPartial(listenAddr): + listenAddr.getProtocolArgument(multiCodec("tcp")).withValue(portArg): + let port = Port(uint16.fromBytesBE(portArg)) + if IP4.matchPartial(listenAddr): + let wildcardAddresses = getWildcardAddress( + listenAddr, + multiCodec("ip4"), + AnyAddress.address_v4, + AddressFamily.IPv4, + port, + networkInterfaceProvider, + ) + addresses.add(wildcardAddresses) + elif IP6.matchPartial(listenAddr): + let wildcardAddresses = getWildcardAddress( + listenAddr, + multiCodec("ip6"), + AnyAddress6.address_v6, + AddressFamily.IPv6, + port, + networkInterfaceProvider, + ) + addresses.add(wildcardAddresses) + else: + addresses.add(listenAddr) + else: + addresses.add(listenAddr) + addresses + +method setup*( + self: WildcardAddressResolverService, switch: Switch +): Future[bool] {.async.} = + ## Sets up the `WildcardAddressResolverService`. + ## + ## This method adds the address mapper to the peer's list of address mappers. + ## + ## Parameters: + ## - `self`: The instance of `WildcardAddressResolverService` being set up. + ## - `switch`: The switch context in which the service operates. + ## + ## Returns: + ## - A `Future[bool]` that resolves to `true` if the setup was successful, otherwise `false`. + self.addressMapper = proc( + listenAddrs: seq[MultiAddress] + ): Future[seq[MultiAddress]] {.async.} = + return expandWildcardAddresses(self.networkInterfaceProvider, listenAddrs) + + debug "Setting up WildcardAddressResolverService" + let hasBeenSetup = await procCall Service(self).setup(switch) + if hasBeenSetup: + switch.peerInfo.addressMappers.add(self.addressMapper) + return hasBeenSetup + +method run*(self: WildcardAddressResolverService, switch: Switch) {.async, public.} = + ## Runs the WildcardAddressResolverService for a given switch. + ## + ## It updates the peer information for the provided switch by running the registered address mapper. Any other + ## address mappers that are registered with the switch will also be run. + ## + trace "Running WildcardAddressResolverService" + await switch.peerInfo.update() + +method stop*( + self: WildcardAddressResolverService, switch: Switch +): Future[bool] {.async, public.} = + ## Stops the WildcardAddressResolverService. + ## + ## Handles the shutdown process of the WildcardAddressResolverService for a given switch. + ## It removes the address mapper from the switch's list of address mappers. + ## It then updates the peer information for the provided switch. Any wildcard address wont be resolved anymore. + ## + ## Parameters: + ## - `self`: The instance of the WildcardAddressResolverService. + ## - `switch`: The Switch object associated with the service. + ## + ## Returns: + ## - A future that resolves to `true` if the service was successfully stopped, otherwise `false`. + debug "Stopping WildcardAddressResolverService" + let hasBeenStopped = await procCall Service(self).stop(switch) + if hasBeenStopped: + switch.peerInfo.addressMappers.keepItIf(it != self.addressMapper) + await switch.peerInfo.update() + return hasBeenStopped diff --git a/libp2p/utility.nim b/libp2p/utility.nim index 211ddbec41..2fa236c4ca 100644 --- a/libp2p/utility.nim +++ b/libp2p/utility.nim @@ -112,6 +112,9 @@ template withValue*[T](self: Opt[T] | Option[T], value, body: untyped): untyped let value {.inject.} = temp.get() body +template withValue*[T, E](self: Result[T, E], value, body: untyped): untyped = + self.toOpt().withValue(value, body) + macro withValue*[T](self: Opt[T] | Option[T], value, body, elseStmt: untyped): untyped = let elseBody = elseStmt[0] quote do: diff --git a/tests/helpers.nim b/tests/helpers.nim index a0dd5bda59..41a0afbb97 100644 --- a/tests/helpers.nim +++ b/tests/helpers.nim @@ -215,9 +215,3 @@ proc default*(T: typedesc[MockResolver]): T = resolver.ipResponses[("localhost", false)] = @["127.0.0.1"] resolver.ipResponses[("localhost", true)] = @["::1"] resolver - -proc setDNSAddr*(switch: Switch) {.async.} = - proc addressMapper(listenAddrs: seq[MultiAddress]): Future[seq[MultiAddress]] {.async.} = - return @[MultiAddress.init("/dns4/localhost/").tryGet() & listenAddrs[0][1].tryGet()] - switch.peerInfo.addressMappers.add(addressMapper) - await switch.peerInfo.update() diff --git a/tests/testautonatservice.nim b/tests/testautonatservice.nim index ad5e8bf45b..eb68538b54 100644 --- a/tests/testautonatservice.nim +++ b/tests/testautonatservice.nim @@ -110,12 +110,12 @@ suite "Autonat Service": check autonatService.networkReachability == NetworkReachability.Reachable check libp2p_autonat_reachability_confidence.value(["Reachable"]) == 0.3 - check switch1.peerInfo.addrs == switch1.peerInfo.listenAddrs.mapIt(switch1.peerStore.guessDialableAddr(it)) + check switch1.peerInfo.addrs == switch1.peerInfo.addrs.mapIt(switch1.peerStore.guessDialableAddr(it)) await allFuturesThrowing( switch1.stop(), switch2.stop(), switch3.stop(), switch4.stop()) - check switch1.peerInfo.addrs == switch1.peerInfo.listenAddrs + check switch1.peerInfo.addrs == switch1.peerInfo.addrs asyncTest "Peer must be not reachable and then reachable": @@ -261,7 +261,6 @@ suite "Autonat Service": let autonatService = AutonatService.new(AutonatClient.new(), newRng(), Opt.some(1.seconds), maxQueueSize = 1) let switch1 = createSwitch(autonatService, maxConnsPerPeer = 0) - await switch1.setDNSAddr() let switch2 = createSwitch(maxConnsPerPeer = 0, nameResolver = MockResolver.default()) @@ -277,6 +276,8 @@ suite "Autonat Service": autonatService.statusAndConfidenceHandler(statusAndConfidenceHandler) await switch1.start() + switch1.peerInfo.addrs.add([ MultiAddress.init("/dns4/localhost/").tryGet() & switch1.peerInfo.addrs[0][1].tryGet() ]) + await switch2.start() await switch1.connect(switch2.peerInfo.peerId, switch2.peerInfo.addrs) diff --git a/tests/testautorelay.nim b/tests/testautorelay.nim index 09e19da86c..de2f3c02e7 100644 --- a/tests/testautorelay.nim +++ b/tests/testautorelay.nim @@ -80,7 +80,7 @@ suite "Autorelay": check: addresses == @[buildRelayMA(switchRelay, switchClient)] addresses.len() == 1 - addresses == switchClient.peerInfo.addrs + addresses[0] in switchClient.peerInfo.addrs await allFutures(switchClient.stop(), switchRelay.stop()) check addresses != switchClient.peerInfo.addrs diff --git a/tests/testhpservice.nim b/tests/testhpservice.nim index 7cf59a745d..2d9d9f625a 100644 --- a/tests/testhpservice.nim +++ b/tests/testhpservice.nim @@ -65,11 +65,6 @@ suite "Hole Punching": let publicPeerSwitch = createSwitch(RelayClient.new()) - proc addressMapper(listenAddrs: seq[MultiAddress]): Future[seq[MultiAddress]] {.async.} = - return @[MultiAddress.init("/dns4/localhost/").tryGet() & listenAddrs[0][1].tryGet()] - publicPeerSwitch.peerInfo.addressMappers.add(addressMapper) - await publicPeerSwitch.peerInfo.update() - proc checkMA(address: seq[MultiAddress]) = if not privatePeerRelayAddr.completed(): privatePeerRelayAddr.complete(address) @@ -83,6 +78,7 @@ suite "Hole Punching": let switchRelay = createSwitch(Relay.new()) await allFuturesThrowing(switchRelay.start(), privatePeerSwitch.start(), publicPeerSwitch.start(), peerSwitch.start()) + publicPeerSwitch.peerInfo.addrs.add([ MultiAddress.init("/dns4/localhost/").tryGet() & publicPeerSwitch.peerInfo.addrs[0][1].tryGet() ]) await privatePeerSwitch.connect(switchRelay.peerInfo.peerId, switchRelay.peerInfo.addrs) await privatePeerSwitch.connect(peerSwitch.peerInfo.peerId, peerSwitch.peerInfo.addrs) # for autonat @@ -106,7 +102,6 @@ suite "Hole Punching": let privatePeerRelayAddr = newFuture[seq[MultiAddress]]() let publicPeerSwitch = createSwitch(RelayClient.new()) - await publicPeerSwitch.setDNSAddr() proc checkMA(address: seq[MultiAddress]) = if not privatePeerRelayAddr.completed(): @@ -121,6 +116,7 @@ suite "Hole Punching": let switchRelay = createSwitch(Relay.new()) await allFuturesThrowing(switchRelay.start(), privatePeerSwitch.start(), publicPeerSwitch.start(), peerSwitch.start()) + publicPeerSwitch.peerInfo.addrs.add([ MultiAddress.init("/dns4/localhost/").tryGet() & publicPeerSwitch.peerInfo.addrs[0][1].tryGet() ]) await privatePeerSwitch.connect(switchRelay.peerInfo.peerId, switchRelay.peerInfo.addrs) await privatePeerSwitch.connect(peerSwitch.peerInfo.peerId, peerSwitch.peerInfo.addrs) # for autonat @@ -162,7 +158,7 @@ suite "Hole Punching": let privatePeerSwitch1 = SwitchStub.new(createSwitch(relayClient1, hpservice1, nameresolver = MockResolver.default())) let privatePeerSwitch2 = SwitchStub.new(createSwitch(relayClient2, hpservice2)) - await privatePeerSwitch2.setDNSAddr() + let switchRelay = createSwitch(Relay.new()) let switchAux = createSwitch() let switchAux2 = createSwitch() diff --git a/tests/testidentify.nim b/tests/testidentify.nim index 38605d9e50..e516ad3f13 100644 --- a/tests/testidentify.nim +++ b/tests/testidentify.nim @@ -210,8 +210,7 @@ suite "Identify": asyncTest "simple push identify": switch2.peerInfo.protocols.add("/newprotocol/") - switch2.peerInfo.listenAddrs.add(MultiAddress.init("/ip4/127.0.0.1/tcp/5555").tryGet()) - await switch2.peerInfo.update() + switch2.peerInfo.addrs.add(MultiAddress.init("/ip4/127.0.0.1/tcp/5555").tryGet()) check: switch1.peerStore[AddressBook][switch2.peerInfo.peerId] != switch2.peerInfo.addrs @@ -224,16 +223,9 @@ suite "Identify": await closeAll() - # Wait the very end to be sure that the push has been processed - check: - switch1.peerStore[ProtoBook][switch2.peerInfo.peerId] == switch2.peerInfo.protocols - switch1.peerStore[AddressBook][switch2.peerInfo.peerId] == switch2.peerInfo.addrs - - asyncTest "wrong peer id push identify": switch2.peerInfo.protocols.add("/newprotocol/") - switch2.peerInfo.listenAddrs.add(MultiAddress.init("/ip4/127.0.0.1/tcp/5555").tryGet()) - await switch2.peerInfo.update() + switch2.peerInfo.addrs.add(MultiAddress.init("/ip4/127.0.0.1/tcp/5555").tryGet()) check: switch1.peerStore[AddressBook][switch2.peerInfo.peerId] != switch2.peerInfo.addrs diff --git a/tests/testwildcardresolverservice.nim b/tests/testwildcardresolverservice.nim new file mode 100644 index 0000000000..e9c40cd4d9 --- /dev/null +++ b/tests/testwildcardresolverservice.nim @@ -0,0 +1,93 @@ +{.used.} + +# Nim-Libp2p +# Copyright (c) 2024 Status Research & Development GmbH +# Licensed under either of +# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) +# * MIT license ([LICENSE-MIT](LICENSE-MIT)) +# at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. + +import std/[options, sequtils] +import stew/[byteutils] +import chronos, metrics +import unittest2 +import ../libp2p/[builders, switch] +import ../libp2p/services/wildcardresolverservice +import ../libp2p/[multiaddress, multicodec] +import ./helpers + +proc getAddressesMock( + addrFamily: AddressFamily +): seq[InterfaceAddress] {.gcsafe, raises: [].} = + try: + if addrFamily == AddressFamily.IPv4: + return + @[ + InterfaceAddress.init(initTAddress("127.0.0.1:0"), 8), + InterfaceAddress.init(initTAddress("192.168.1.22:0"), 24), + ] + else: + return + @[ + InterfaceAddress.init(initTAddress("::1:0"), 8), + InterfaceAddress.init(initTAddress("fe80::1:0"), 64), + ] + except TransportAddressError as e: + echo "Error: " & $e.msg + fail() + +proc createSwitch(svc: Service): Switch = + SwitchBuilder + .new() + .withRng(newRng()) + .withAddresses( + @[ + MultiAddress.init("/ip4/127.0.0.1/tcp/0/").tryGet(), + MultiAddress.init("/ip4/0.0.0.0/tcp/0/").tryGet(), + MultiAddress.init("/ip6/::/tcp/0/").tryGet(), + ], + false + ) + .withTcpTransport() + .withMplex() + .withNoise() + .withServices(@[svc]) + .build() + +suite "WildcardAddressResolverService": + teardown: + checkTrackers() + + asyncTest "WildcardAddressResolverService must resolve wildcard addresses and stop doing so when stopped": + let svc: Service = + WildcardAddressResolverService.new(networkInterfaceProvider = getAddressesMock) + let switch = createSwitch(svc) + await switch.start() + let tcpIp4Locahost = switch.peerInfo.addrs[0][multiCodec("tcp")].get + let tcpIp4Wildcard = switch.peerInfo.addrs[1][multiCodec("tcp")].get + let tcpIp6 = switch.peerInfo.addrs[2][multiCodec("tcp")].get # tcp port for ip6 + + check switch.peerInfo.addrs == + @[ + MultiAddress.init("/ip4/127.0.0.1" & $tcpIp4Locahost).get, + MultiAddress.init("/ip4/0.0.0.0" & $tcpIp4Wildcard).get, + MultiAddress.init("/ip6/::" & $tcpIp6).get, + ] + await svc.run(switch) + check switch.peerInfo.addrs == + @[ + MultiAddress.init("/ip4/127.0.0.1" & $tcpIp4Locahost).get, + MultiAddress.init("/ip4/127.0.0.1" & $tcpIp4Wildcard).get, + MultiAddress.init("/ip4/192.168.1.22" & $tcpIp4Wildcard).get, + MultiAddress.init("/ip6/::1" & $tcpIp6).get, + MultiAddress.init("/ip6/fe80::1" & $tcpIp6).get, + ] + await switch.stop() + check switch.peerInfo.addrs == + @[ + MultiAddress.init("/ip4/127.0.0.1" & $tcpIp4Locahost).get, + MultiAddress.init("/ip4/0.0.0.0" & $tcpIp4Wildcard).get, + MultiAddress.init("/ip6/::" & $tcpIp6).get, + ]