2024-05-15 14:14:18 +00:00
|
|
|
# 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/[deques, sequtils]
|
|
|
|
import stew/[byteutils, results, endians2]
|
|
|
|
import chronos, chronos/transports/[osnet, ipnet], metrics
|
|
|
|
import ../[multiaddress, multicodec]
|
|
|
|
import ../switch
|
|
|
|
import ../wire
|
|
|
|
import ../utils/heartbeat
|
|
|
|
import ../crypto/crypto
|
|
|
|
|
|
|
|
logScope:
|
|
|
|
topics = "libp2p wildcardresolverservice"
|
|
|
|
|
|
|
|
type
|
|
|
|
WildcardAddressResolverService* = ref object of Service
|
2024-05-16 14:08:33 +00:00
|
|
|
## Service used to resolve wildcard addresses of the type "0.0.0.0" for IPv4 or "::" for IPv6.
|
2024-05-16 16:39:58 +00:00
|
|
|
## When used with a `Switch`, this service will be automatically started 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`.
|
2024-05-15 14:14:18 +00:00
|
|
|
networkInterfaceProvider: NetworkInterfaceProvider
|
2024-05-16 14:08:33 +00:00
|
|
|
## Provides a list of network addresses.
|
2024-05-15 14:14:18 +00:00
|
|
|
addressMapper: AddressMapper
|
2024-05-16 14:08:33 +00:00
|
|
|
## 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.
|
2024-05-15 14:14:18 +00:00
|
|
|
scheduleHandle: Future[void]
|
2024-05-16 14:08:33 +00:00
|
|
|
## Represents the task that is scheduled to run the service.
|
2024-05-15 14:14:18 +00:00
|
|
|
scheduleInterval: Opt[Duration]
|
2024-05-16 14:08:33 +00:00
|
|
|
## The interval at which the service should run.
|
2024-05-15 14:14:18 +00:00
|
|
|
|
|
|
|
NetworkInterfaceProvider* = ref object of RootObj
|
|
|
|
|
|
|
|
proc isLoopbackOrUp(networkInterface: NetworkInterface): bool =
|
|
|
|
if (networkInterface.ifType == IfSoftwareLoopback) or
|
|
|
|
(networkInterface.state == StatusUp): true else: false
|
|
|
|
|
|
|
|
method getAddresses*(
|
|
|
|
networkInterfaceProvider: NetworkInterfaceProvider, addrFamily: AddressFamily
|
|
|
|
): seq[InterfaceAddress] {.base.} =
|
2024-05-16 14:08:33 +00:00
|
|
|
## This method retrieves the addresses of network interfaces based on the specified address family.
|
|
|
|
##
|
|
|
|
## The `getAddresses` method 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:
|
|
|
|
## - `networkInterfaceProvider`: A provider that offers access to network interfaces.
|
|
|
|
## - `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.
|
2024-05-15 14:14:18 +00:00
|
|
|
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],
|
|
|
|
scheduleInterval: Opt[Duration] = Opt.none(Duration),
|
|
|
|
networkInterfaceProvider: NetworkInterfaceProvider = new(NetworkInterfaceProvider),
|
|
|
|
): T =
|
2024-05-16 16:39:58 +00:00
|
|
|
## This procedure initializes a new `WildcardAddressResolverService` with the provided
|
|
|
|
## schedule interval and network interface provider.
|
|
|
|
##
|
|
|
|
## Parameters:
|
|
|
|
## - `T`: The type descriptor for `WildcardAddressResolverService`.
|
|
|
|
## - `scheduleInterval`: An optional interval at which the service should run. Defaults to none.
|
|
|
|
## - `networkInterfaceProvider`: A provider that offers access to network interfaces. Defaults to a new instance of `NetworkInterfaceProvider`.
|
|
|
|
##
|
|
|
|
## Returns:
|
|
|
|
## - A new instance of `WildcardAddressResolverService`.
|
2024-05-15 14:14:18 +00:00
|
|
|
return T(
|
|
|
|
scheduleInterval: scheduleInterval,
|
|
|
|
networkInterfaceProvider: networkInterfaceProvider,
|
|
|
|
)
|
|
|
|
|
|
|
|
proc schedule(
|
|
|
|
service: WildcardAddressResolverService, switch: Switch, interval: Duration
|
|
|
|
) {.async.} =
|
2024-05-16 16:39:58 +00:00
|
|
|
## Schedules the WildcardAddressResolverService to run at regular intervals.
|
|
|
|
##
|
|
|
|
## Sets up a schedule for the WildcardAddressResolverService to execute periodically based
|
|
|
|
## on the specified interval. It continuously runs the service on the given switch at the defined time intervals.
|
|
|
|
##
|
|
|
|
## Parameters:
|
|
|
|
## - `service`: The instance of the WildcardAddressResolverService that will be scheduled.
|
|
|
|
## - `switch`: The Switch object that the service will operate on.
|
|
|
|
## - `interval`: The Duration specifying how often the service should run.
|
2024-05-15 14:14:18 +00:00
|
|
|
heartbeat "Scheduling WildcardAddressResolverService run", interval:
|
|
|
|
await service.run(switch)
|
|
|
|
|
|
|
|
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,
|
|
|
|
suffix: MultiAddress,
|
|
|
|
): seq[MultiAddress] =
|
|
|
|
var addresses: seq[MultiAddress]
|
|
|
|
for ifaddr in interfaceAddresses:
|
|
|
|
var address = ifaddr.host
|
|
|
|
address.port = port
|
|
|
|
MultiAddress.init(address, protocol).toOpt.withValue(maddress):
|
|
|
|
maddress.concat(suffix).toOpt.withValue(a):
|
|
|
|
addresses.add(a)
|
|
|
|
addresses
|
|
|
|
|
|
|
|
proc getWildcardAddress(
|
|
|
|
maddress: MultiAddress,
|
|
|
|
multiCodec: MultiCodec,
|
|
|
|
anyAddr: openArray[uint8],
|
|
|
|
addrFamily: AddressFamily,
|
|
|
|
port: Port,
|
|
|
|
networkInterfaceProvider: NetworkInterfaceProvider,
|
|
|
|
peerId: MultiAddress,
|
|
|
|
): seq[MultiAddress] =
|
|
|
|
var addresses: seq[MultiAddress]
|
|
|
|
maddress.getProtocolArgument(multiCodec).toOpt.withValue(address):
|
|
|
|
if address == anyAddr:
|
|
|
|
let filteredInterfaceAddresses = networkInterfaceProvider.getAddresses(addrFamily)
|
|
|
|
addresses.add(
|
|
|
|
getWildcardMultiAddresses(filteredInterfaceAddresses, IPPROTO_TCP, port, peerId)
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
maddress.concat(peerId).toOpt.withValue(a):
|
|
|
|
addresses.add(a)
|
|
|
|
return addresses
|
|
|
|
|
|
|
|
proc expandWildcardAddresses(
|
|
|
|
networkInterfaceProvider: NetworkInterfaceProvider,
|
|
|
|
peerId: PeerId,
|
|
|
|
listenAddrs: seq[MultiAddress],
|
|
|
|
): seq[MultiAddress] =
|
|
|
|
let peerIdMa = MultiAddress.init(multiCodec("p2p"), peerId).valueOr:
|
|
|
|
return default(seq[MultiAddress])
|
|
|
|
|
|
|
|
var addresses: seq[MultiAddress]
|
|
|
|
# In this loop we expand bounded 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")).toOpt.withValue(portArg):
|
|
|
|
if len(portArg) == sizeof(uint16):
|
|
|
|
let port = Port(uint16.fromBytesBE(portArg))
|
|
|
|
if IP4.matchPartial(listenAddr):
|
|
|
|
let wildcardAddresses = getWildcardAddress(
|
|
|
|
listenAddr,
|
|
|
|
multiCodec("ip4"),
|
|
|
|
AnyAddress.address_v4,
|
|
|
|
AddressFamily.IPv4,
|
|
|
|
port,
|
|
|
|
networkInterfaceProvider,
|
|
|
|
peerIdMa,
|
|
|
|
)
|
|
|
|
addresses.add(wildcardAddresses)
|
|
|
|
elif IP6.matchPartial(listenAddr):
|
|
|
|
let wildcardAddresses = getWildcardAddress(
|
|
|
|
listenAddr,
|
|
|
|
multiCodec("ip6"),
|
|
|
|
AnyAddress6.address_v6,
|
|
|
|
AddressFamily.IPv6,
|
|
|
|
port,
|
|
|
|
networkInterfaceProvider,
|
|
|
|
peerIdMa,
|
|
|
|
)
|
|
|
|
addresses.add(wildcardAddresses)
|
|
|
|
else:
|
|
|
|
listenAddr.concat(peerIdMa).withValue(ma):
|
|
|
|
addresses.add(ma)
|
|
|
|
else:
|
|
|
|
let suffixed = listenAddr.concat(peerIdMa).valueOr:
|
|
|
|
continue
|
|
|
|
addresses.add(suffixed)
|
|
|
|
addresses
|
|
|
|
|
|
|
|
method setup*(
|
|
|
|
self: WildcardAddressResolverService, switch: Switch
|
|
|
|
): Future[bool] {.async.} =
|
2024-05-16 16:39:58 +00:00
|
|
|
## Sets up the `WildcardAddressResolverService`.
|
|
|
|
##
|
|
|
|
## This method adds the address mapper to the peer's list of address mappers and schedules the seervice to run
|
|
|
|
## at the specified interval.
|
|
|
|
##
|
|
|
|
## 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`.
|
2024-05-15 14:14:18 +00:00
|
|
|
self.addressMapper = proc(
|
|
|
|
listenAddrs: seq[MultiAddress]
|
|
|
|
): Future[seq[MultiAddress]] {.async.} =
|
|
|
|
echo listenAddrs
|
|
|
|
return expandWildcardAddresses(
|
|
|
|
self.networkInterfaceProvider, switch.peerInfo.peerId, listenAddrs
|
|
|
|
)
|
|
|
|
|
|
|
|
debug "Setting up WildcardAddressResolverService"
|
|
|
|
let hasBeenSetup = await procCall Service(self).setup(switch)
|
|
|
|
if hasBeenSetup:
|
|
|
|
self.scheduleInterval.withValue(interval):
|
|
|
|
self.scheduleHandle = schedule(self, switch, interval)
|
|
|
|
switch.peerInfo.addressMappers.add(self.addressMapper)
|
|
|
|
return hasBeenSetup
|
|
|
|
|
|
|
|
method run*(self: WildcardAddressResolverService, switch: Switch) {.async, public.} =
|
2024-05-16 16:39:58 +00:00
|
|
|
## 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.
|
|
|
|
##
|
2024-05-15 14:14:18 +00:00
|
|
|
trace "Running WildcardAddressResolverService"
|
|
|
|
await switch.peerInfo.update()
|
|
|
|
|
|
|
|
method stop*(
|
|
|
|
self: WildcardAddressResolverService, switch: Switch
|
|
|
|
): Future[bool] {.async, public.} =
|
2024-05-16 16:39:58 +00:00
|
|
|
## Stops the WildcardAddressResolverService.
|
|
|
|
##
|
|
|
|
## Handles the shutdown process of the WildcardAddressResolverService for a given switch.
|
|
|
|
## It cancels the scheduled task, if any, and 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`.
|
2024-05-15 14:14:18 +00:00
|
|
|
debug "Stopping WildcardAddressResolverService"
|
|
|
|
let hasBeenStopped = await procCall Service(self).stop(switch)
|
|
|
|
if hasBeenStopped:
|
|
|
|
if not isNil(self.scheduleHandle):
|
|
|
|
self.scheduleHandle.cancel()
|
|
|
|
self.scheduleHandle = nil
|
|
|
|
switch.peerInfo.addressMappers.keepItIf(it != self.addressMapper)
|
|
|
|
await switch.peerInfo.update()
|
|
|
|
return hasBeenStopped
|