chore: making it compile

This commit is contained in:
Adam Uhlíř 2025-10-31 11:47:27 +01:00
parent a816201f50
commit 56a1b664b5
No known key found for this signature in database
GPG Key ID: 0CBD7AA7B5A72FED
3 changed files with 183 additions and 172 deletions

View File

@ -10,7 +10,6 @@
import std/[options, os, strutils, times, net, atomics]
import pkg/stew/objects
import pkg/nat_traversal/[miniupnpc, natpmp]
import pkg/json_serialization/std/net
import pkg/results
@ -40,6 +39,9 @@ type PortMappingStrategy* = enum
type MappingPort* = ref object of RootObj
value*: Port
proc `$`(p: MappingPort): string =
$(p.value)
type TcpPort* = ref object of MappingPort
type UdpPort* = ref object of MappingPort
@ -49,7 +51,7 @@ proc newTcpMappingPort*(value: Port): TcpPort =
proc newUdpMappingPort*(value: Port): UdpPort =
UdpPort(value: value)
type PortMapping = tuple[internalPort: MappingPort, externalPort: Option[MappingPort]]
type PortMapping* = tuple[internalPort: MappingPort, externalPort: Option[MappingPort]]
type RenewelThreadArgs =
tuple[strategy: PortMappingStrategy, portMapping: seq[PortMapping]]
@ -150,7 +152,7 @@ proc upnpPortMapping(
if pmres.isErr:
error "UPnP port mapping", msg = pmres.error
return failure(pmres.error)
return failure($pmres.error)
# let's check it
let cres = upnp.getSpecificPortMapping(
@ -175,22 +177,23 @@ proc npmpPortMapping(
internalPort = internalPort.value
protocol = protocol
without extPort =?
npmp.addPortMapping(
eport = externalPort.value,
iport = internalPort.value,
protocol = protocol,
lifetime = Pmp_LIFETIME,
), err:
error "NAT-PMP port mapping error", msg = err.msg
return failure(err.msg)
let extPortRes = npmp.addPortMapping(
eport = externalPort.value.cushort,
iport = internalPort.value.cushort,
protocol = protocol,
lifetime = Pmp_LIFETIME,
)
if extPortRes.isErr:
error "NAT-PMP port mapping error", msg = extPortRes.error()
return failure(extPortRes.error())
info "NAT-PMP: added port mapping"
if internalPort is TcpPort:
return success(newTcpMappingPort(extPort))
return success(MappingPort(newTcpMappingPort(Port(extPortRes.value))))
else:
return success(newUdpMappingPort(extPort))
return success(MappingPort(newUdpMappingPort(Port(extPortRes.value))))
## Create port mapping that will try to utilize the same port number
## of the internal port for the external port mapping.
@ -217,118 +220,6 @@ proc doPortMapping(
return failure("No active startegy")
## Gets external IP provided by the port mapping protocols
## Port mapping needs to be succesfully started first using `startPortMapping()`
proc getExternalIP*(): ?IpAddress =
if upnp == nil and npmp == nil:
warn "No available port-mapping protocol"
return IpAddress.none
if upnp != nil:
let ires = upnp.externalIPAddress
if ires.isOk():
info "Got externa IP address: " & ires.value, ip = ires.value
return parseIpAddress(ires.value).some
else:
debug "Getting external IP address using UPnP failed",
msg = ires.error, protocol = "upnp"
if npmp != nil:
let nires = npmp.externalIPAddress()
if nires.isErr:
debug "Getting external IP address using NAT-PMP failed", msg = nires.error
else:
try:
info "Got externa IP address: " & $(nires.value),
ip =$ (nires.value), protocol = "npmp"
return parseIpAddress($(nires.value)).some
except ValueError as e:
error "parseIpAddress() exception", err = e.msg
return IpAddress.none
proc startPortMapping*(
strategy: PortMappingStrategy, internalPorts: seq[MappingPort]
): ?!seq[PortMapping] =
if strategy == PortMappingStrategy.None:
return failure("No port mapping strategy requested")
if internalPorts.len == 0:
return failure("No internal ports to be mapped were supplied")
strategy = initProtocols(strategy)
if strategy == PortMappingStrategy.None:
return failure("No available port mapping protocols on the network")
portMapping = newSeqOfCap[PortMappings](internalPorts.len)
for port in internalPorts:
without mappedPort =? doPortMapping(port), err:
warn "Failed to map port", port = port.value, msg = err.msg
portMapping.add((internalPort: port, externalPort: MappingPort.none))
portMapping.add((internalPort: port, externalPort: mappedPort.some))
startRenewalThread(strategy)
return success(externalPorts)
proc stopPortMapping*() =
if upnp == nil or npmp == nil:
debug "Port mapping is not running, nothing to stop"
return
info "Stopping port mapping renewal threads"
try:
portMappingExiting.store(true)
renewalThread.join()
except CatchableError as exc:
warn "Failed to stop port mapping renewal thread", exc = exc.msg
for mapping in portMapping:
if upnp != nil:
let protocol =
if (internalPort is TcpPort): UPNPProtocol.TCP else: UPNPProtocol.UDP
if err =?
upnp.deletePortMapping(
externalPort = $(mapping.externalPort.value), protocol = protocol
).errorOption:
error "UPnP port mapping deletion error", msg = err.msg
else:
debug "UPnP: deleted port mapping",
externalPort = mapping.externalPort,
internalPort = mapping.internalPort,
protocol = protocol
if npnp != nil:
let protocol =
if (internalPort is TcpPort): NatPmpProtocol.TCP else: NatPmpProtocol.UDP
if err =?
npmp.deletePortMapping(
eport = mapping.externalPort.value,
iport = mapping.internalPort.value,
protocol = protocol,
).errorOption:
error "NAT-PMP port mapping deletion error", msg = err.msg
else:
debug "NAT-PMP: deleted port mapping",
externalPort = mapping.externalPort,
internalPort = mapping.internalPort,
protocol = protocol
proc startRenewalThread(
strategy: PortMappingStrategy,
internalPorts: seq[MappingPort],
externalPorts: seq[?MappingPort],
) =
try:
renewalThread = Thread[RenewelThreadArgs]()
renewalThread.createThread(renewPortMapping, (strategy, portMapping))
except CatchableError as exc:
warn "Failed to create NAT port mapping renewal thread", exc = exc.msg
proc renewPortMapping(args: RenewelThreadArgs) {.thread, raises: [ValueError].} =
ignoreSignalsInThread()
let
@ -361,3 +252,121 @@ proc renewPortMapping(args: RenewelThreadArgs) {.thread, raises: [ValueError].}
lastUpdate = now()
sleep(sleepDuration)
proc startRenewalThread(strategy: PortMappingStrategy) =
try:
renewalThread = Thread[RenewelThreadArgs]()
renewalThread.createThread(renewPortMapping, (strategy, mappings))
except CatchableError as exc:
warn "Failed to create NAT port mapping renewal thread", exc = exc.msg
## Gets external IP provided by the port mapping protocols
## Port mapping needs to be succesfully started first using `startPortMapping()`
proc getExternalIP*(): ?IpAddress =
if upnp == nil and npmp == nil:
warn "No available port-mapping protocol"
return IpAddress.none
if upnp != nil:
let ires = upnp.externalIPAddress
if ires.isOk():
info "Got externa IP address", ip = ires.value
try:
return parseIpAddress(ires.value).some
except ValueError as e:
error "Failed to parse IP address", err = e.msg
else:
debug "Getting external IP address using UPnP failed",
msg = ires.error, protocol = "upnp"
if npmp != nil:
let nires = npmp.externalIPAddress()
if nires.isErr:
debug "Getting external IP address using NAT-PMP failed", msg = nires.error
else:
try:
info "Got externa IP address", ip = $(nires.value), protocol = "npmp"
return parseIpAddress($(nires.value)).some
except ValueError as e:
error "Failed to parse IP address", err = e.msg
return IpAddress.none
proc startPortMapping*(
strategy: var PortMappingStrategy, internalPorts: seq[MappingPort]
): ?!seq[PortMapping] =
if strategy == PortMappingStrategy.None:
return failure("No port mapping strategy requested")
if internalPorts.len == 0:
return failure("No internal ports to be mapped were supplied")
strategy = initProtocols(strategy)
if strategy == PortMappingStrategy.None:
return failure("No available port mapping protocols on the network")
if mappings.len > 0:
return failure("Port mapping was already started! Stop first before re-starting.")
mappings = newSeqOfCap[PortMapping](internalPorts.len)
for port in internalPorts:
without mappedPort =? doPortMapping(port), err:
warn "Failed to map port", port = port, msg = err.msg
mappings.add((internalPort: port, externalPort: MappingPort.none))
mappings.add((internalPort: port, externalPort: mappedPort.some))
startRenewalThread(strategy)
return success(mappings)
proc stopPortMapping*() =
if upnp == nil or npmp == nil:
debug "Port mapping is not running, nothing to stop"
return
info "Stopping port mapping renewal threads"
try:
portMappingExiting.store(true)
renewalThread.joinThread()
except CatchableError as exc:
warn "Failed to stop port mapping renewal thread", exc = exc.msg
for mapping in mappings:
if mapping.externalPort.isNone:
continue
if upnp != nil:
let protocol =
if (mapping.internalPort is TcpPort): UPNPProtocol.TCP else: UPNPProtocol.UDP
if err =?
upnp.deletePortMapping(
externalPort = $((!mapping.externalPort).value), protocol = protocol
).errorOption:
error "UPnP port mapping deletion error", msg = err
else:
debug "UPnP: deleted port mapping",
externalPort = !mapping.externalPort,
internalPort = mapping.internalPort,
protocol = protocol
if npmp != nil:
let protocol =
if (mapping.internalPort is TcpPort): NatPmpProtocol.TCP else: NatPmpProtocol.UDP
if err =?
npmp.deletePortMapping(
eport = (!mapping.externalPort).value.cushort,
iport = mapping.internalPort.value.cushort,
protocol = protocol,
).errorOption:
error "NAT-PMP port mapping deletion error", msg = err
else:
debug "NAT-PMP: deleted port mapping",
externalPort = !mapping.externalPort,
internalPort = mapping.internalPort,
protocol = protocol
mappings = @[]

View File

@ -10,6 +10,7 @@ import pkg/libp2p/protocols/connectivity/autonat/service
import ../rng as random
import ./port_mapping
import ./utils
const AutonatCheckInterval = Opt.some(chronos.seconds(30))
@ -34,11 +35,53 @@ proc new*(
): T =
return T(portMappingStrategy: portMappingStrategy)
proc startPortMapping(self: ReachabilityManager): bool =
if not self.started:
warn "ReachabilityManager is not started, yet we are trying to map ports already!"
return false
try:
let announceRecords = (!self.getAnnounceRecords)()
let discoveryRecords = (!self.getDiscoveryRecords)()
let portsToBeMapped =
(announceRecords & discoveryRecords).mapIt(getAddressAndPort(it)).mapIt(it.port)
without mappedPorts =? startPortMapping(self.portMappingStrategy, portsToBeMapped),
err:
warn "Could not start port mapping", msg = err.msg
return false
if mappedPorts.any(
proc(x: PortMapping): bool =
isNone(x.externalPort)
):
warn "Some ports were not mapped - not using port mapping then"
return false
info "Started port mapping"
let announceMappedRecords = zip(
announceRecords, mappedPorts[0 .. announceRecords.len - 1]
)
.mapIt(getMultiAddr(getAddressAndPort(it[0]).ip, !it[1].externalPort))
(!self.updateAnnounceRecords)(announceMappedRecords)
let discoveryMappedRecords = zip(
discoveryRecords, mappedPorts[announceRecords.len .. ^1]
)
.mapIt(getMultiAddr(getAddressAndPort(it[0]).ip, !it[1].externalPort))
(!self.updateDiscoveryRecords)(discoveryMappedRecords)
return true
except ValueError as exc:
error "Error while starting port mapping", msg = exc.msg
return false
proc getReachabilityHandler(manager: ReachabilityManager): StatusAndConfidenceHandler =
let statusAndConfidenceHandler = proc(
networkReachability: NetworkReachability, confidenceOpt: Opt[float]
): Future[void] {.gcsafe, async: (raises: [CancelledError]).} =
if not started:
): Future[void] {.async: (raises: [CancelledError]).} =
if not manager.started:
warn "ReachabilityManager was not started, but we are already getting reachability updates! Ignoring..."
return
@ -56,7 +99,7 @@ proc getReachabilityHandler(manager: ReachabilityManager): StatusAndConfidenceHa
manager.networkReachability = networkReachability
if networkReachability == NetworkReachability.Unreachable:
if networkReachability == NetworkReachability.NotReachable:
# Lets first start to expose port using port mapping protocols like NAT-PMP or UPnP
if manager.startPortMapping():
return # We exposed ports so we should be good!
@ -65,45 +108,6 @@ proc getReachabilityHandler(manager: ReachabilityManager): StatusAndConfidenceHa
return statusAndConfidenceHandler
proc startPortMapping(self: ReachabilityManager): bool =
try:
let announceRecords = self.getAnnounceRecords()
let discoveryRecords = self.getDiscoveryRecords()
let portsToBeMapped =
(announceRecords & discoveryRecords).mapIt(getAddressAndPort(it)).mapIt(it.port)
without mappedPorts =? startPortMapping(
manager.portMappingStrategy, portsToBeMapped
), err:
warn "Could not start port mapping", msg = err
return false
if mappedPorts.any(
proc(x: ?MappingPort): bool =
isNone(x)
):
warn "Some ports were not mapped - not using port mapping then"
return false
info "Started port mapping"
let announceMappedRecords = zip(
announceRecords, mappedPorts[0 .. announceRecords.len - 1]
)
.mapIt(getMultiAddr(getAddressAndPort(it[0]).ip, it[1].value))
self.updateAnnounceRecords(announceMappedRecords)
let discoveryMappedRecords = zip(
discoveryRecords, mappedPorts[announceRecords.len, ^1]
)
.mapIt(getMultiAddr(getAddressAndPort(it[0]).ip, it[1].value))
self.updateDiscoveryRecords(discoveryMappedRecords)
return true
except ValueError as exc:
error "Error while starting port mapping", msg = exc.msg
return false
proc start*(
self: ReachabilityManager, switch: Switch, bootNodes: seq[SignedPeerRecord]
): Future[void] {.async: (raises: [CancelledError]).} =

View File

@ -7,13 +7,11 @@
## This file may not be copied, modified, or distributed except according to
## those terms.
import std/strutils
import std/options
import std/net
import pkg/libp2p
import pkg/questionable
import pkg/questionable/results
import pkg/stew/endians2
import ./port_mapping
@ -62,7 +60,7 @@ proc getAddressAndPort*(
proc getMultiAddr*(ip: IpAddress, port: MappingPort): MultiAddress =
let ipFamily = if ip.family == IpAddressFamily.IPv4: "/ip4/" else: "/ip6/"
let portType = if (internalPort is TcpPort): "/tcp/" else: "/udp/"
let portType = if (port is TcpPort): "/tcp/" else: "/udp/"
return MultiAddress.init(ipFamily & $ip & portType & $(port.value)).expect(
"valid multiaddr"
)