nim-libp2p/libp2p/services/wildcardresolverservice.nim

208 lines
8.2 KiB
Nim
Raw Normal View History

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: [].}
2024-05-16 17:20:30 +00:00
import std/sequtils
2024-05-15 14:14:18 +00:00
import stew/[byteutils, results, endians2]
2024-05-16 17:20:30 +00:00
import chronos, chronos/transports/[osnet, ipnet], chronicles
2024-05-15 14:14:18 +00:00
import ../[multiaddress, multicodec]
import ../switch
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 17:20:30 +00:00
## When used with a `Switch`, this service will be automatically set up and stopped
2024-05-16 16:39:58 +00:00
## 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
2024-05-23 15:07:18 +00:00
NetworkInterfaceProvider* =
proc(addrFamily: AddressFamily): seq[InterfaceAddress] {.gcsafe, raises: [].}
2024-05-15 14:14:18 +00:00
proc isLoopbackOrUp(networkInterface: NetworkInterface): bool =
if (networkInterface.ifType == IfSoftwareLoopback) or
(networkInterface.state == StatusUp): true else: false
2024-05-23 15:06:25 +00:00
proc getAddresses(addrFamily: AddressFamily): seq[InterfaceAddress] =
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:
## - `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),
2024-05-23 15:06:25 +00:00
networkInterfaceProvider: NetworkInterfaceProvider = getAddresses,
2024-05-15 14:14:18 +00:00
): T =
2024-05-16 17:20:30 +00:00
## This procedure initializes a new `WildcardAddressResolverService` with the provided network interface provider.
2024-05-16 16:39:58 +00:00
##
## 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`.
2024-05-23 15:07:18 +00:00
return T(networkInterfaceProvider: networkInterfaceProvider)
2024-05-15 14:14:18 +00:00
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(
2024-05-23 15:07:18 +00:00
interfaceAddresses: seq[InterfaceAddress], protocol: Protocol, port: Port
2024-05-15 14:14:18 +00:00
): seq[MultiAddress] =
var addresses: seq[MultiAddress]
for ifaddr in interfaceAddresses:
var address = ifaddr.host
address.port = port
2024-05-17 12:51:04 +00:00
MultiAddress.init(address, protocol).withValue(maddress):
addresses.add(maddress)
2024-05-15 14:14:18 +00:00
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).toOpt.withValue(address):
if address == anyAddr:
2024-05-23 15:06:25 +00:00
let filteredInterfaceAddresses = networkInterfaceProvider(addrFamily)
2024-05-15 14:14:18 +00:00
addresses.add(
2024-05-17 12:51:04 +00:00
getWildcardMultiAddresses(filteredInterfaceAddresses, IPPROTO_TCP, port)
2024-05-15 14:14:18 +00:00
)
else:
2024-05-17 12:51:04 +00:00
addresses.add(maddress)
2024-05-15 14:14:18 +00:00
return addresses
proc expandWildcardAddresses(
2024-05-23 15:07:18 +00:00
networkInterfaceProvider: NetworkInterfaceProvider, listenAddrs: seq[MultiAddress]
2024-05-15 14:14:18 +00:00
): 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):
2024-05-16 23:21:11 +00:00
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)
2024-05-15 14:14:18 +00:00
else:
2024-05-16 23:19:23 +00:00
addresses.add(listenAddr)
2024-05-15 14:14:18 +00:00
addresses
method setup*(
self: WildcardAddressResolverService, switch: Switch
): Future[bool] {.async.} =
2024-05-16 16:39:58 +00:00
## Sets up the `WildcardAddressResolverService`.
##
2024-05-16 17:20:30 +00:00
## This method adds the address mapper to the peer's list of address mappers.
2024-05-16 16:39:58 +00:00
##
## 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
2024-05-23 15:07:18 +00:00
return expandWildcardAddresses(self.networkInterfaceProvider, listenAddrs)
2024-05-15 14:14:18 +00:00
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.} =
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.
2024-05-16 17:20:30 +00:00
## It removes the address mapper from the switch's list of address mappers.
2024-05-16 16:39:58 +00:00
## 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:
switch.peerInfo.addressMappers.keepItIf(it != self.addressMapper)
await switch.peerInfo.update()
return hasBeenStopped