UNPKG

libp2p

Version:

JavaScript implementation of libp2p, a modular peer to peer network stack

215 lines (170 loc) • 6.23 kB
import { isIPv4 } from '@chainsafe/is-ip' import { getNetConfig, isNetworkAddress, trackedMap } from '@libp2p/utils' import { CODE_IP4, CODE_IP6, multiaddr } from '@multiformats/multiaddr' import type { AddressManagerComponents, AddressManagerInit } from './index.js' import type { Logger } from '@libp2p/interface' import type { NodeAddress } from '@libp2p/interface-internal' import type { Multiaddr } from '@multiformats/multiaddr' export const defaultValues = { maxObservedAddresses: 10 } interface PublicAddressMapping { internalIp: string internalPort: number externalIp: string externalPort: number externalFamily: 4 | 6 protocol: 'tcp' | 'udp' verified: boolean expires: number lastVerified?: number } export class IPMappings { private readonly log: Logger private readonly mappings: Map<string, PublicAddressMapping[]> constructor (components: AddressManagerComponents, init: AddressManagerInit = {}) { this.log = components.logger.forComponent('libp2p:address-manager:ip-mappings') this.mappings = trackedMap({ name: 'libp2p_address_manager_ip_mappings', metrics: components.metrics }) } has (ma: Multiaddr): boolean { const config = getNetConfig(ma) if (config.type !== 'ip4' && config.type !== 'ip6') { return false } for (const mappings of this.mappings.values()) { for (const mapping of mappings) { if (mapping.externalIp === config.host) { return true } } } return false } add (internalIp: string, internalPort: number, externalIp: string, externalPort: number = internalPort, protocol: 'tcp' | 'udp' = 'tcp'): void { const key = `${internalIp}-${internalPort}-${protocol}` const mappings = this.mappings.get(key) ?? [] const mapping: PublicAddressMapping = { internalIp, internalPort, externalIp, externalPort, externalFamily: isIPv4(externalIp) ? 4 : 6, protocol, verified: false, expires: 0 } mappings.push(mapping) this.mappings.set(key, mappings) } remove (ma: Multiaddr): boolean { const config = getNetConfig(ma) if (config.type !== 'ip4' && config.type !== 'ip6') { return false } let wasConfident = false for (const [key, mappings] of this.mappings.entries()) { for (let i = 0; i < mappings.length; i++) { const mapping = mappings[i] if (mapping.externalIp === config.host && mapping.externalPort === config.port && mapping.protocol === config.protocol) { this.log('removing %s:%s to %s:%s %s IP mapping', mapping.externalIp, mapping.externalPort, config.host, config.port, config.protocol) wasConfident = wasConfident || mapping.verified mappings.splice(i, 1) i-- } } if (mappings.length === 0) { this.mappings.delete(key) } } return wasConfident } getAll (addresses: NodeAddress[]): NodeAddress[] { const ipMappedAddresses: NodeAddress[] = [] for (const { multiaddr: ma } of addresses) { if (!isNetworkAddress(ma)) { continue } const config = getNetConfig(ma) if (config.type !== 'ip4' && config.type !== 'ip6') { continue } let key: string | undefined // see if the internal host/port/protocol tuple has been mapped externally if (config.protocol === 'tcp') { key = `${config.host}-${config.port}-tcp` } else if (config.protocol === 'udp') { key = `${config.host}-${config.port}-udp` } if (key == null) { continue } const mappings = this.mappings.get(key) if (mappings == null) { continue } for (const mapping of mappings) { ipMappedAddresses.push({ multiaddr: this.maybeOverrideIp(ma, mapping.externalIp, mapping.externalFamily, mapping.protocol, mapping.externalPort), verified: mapping.verified, type: 'ip-mapping', expires: mapping.expires, lastVerified: mapping.lastVerified }) } } return ipMappedAddresses } private maybeOverrideIp (ma: Multiaddr, externalIp: string, externalFamily: number, protocol: 'tcp' | 'udp', externalPort: number): Multiaddr { const components = ma.getComponents() const ipIndex = components.findIndex(c => c.code === CODE_IP4 || c.code === CODE_IP6) const portIndex = components.findIndex(c => c.name === protocol) if (ipIndex > -1 && portIndex > -1) { components[ipIndex].value = externalIp components[ipIndex].code = externalFamily === 4 ? CODE_IP4 : CODE_IP6 components[portIndex].value = `${externalPort}` return multiaddr(components) } return ma } confirm (ma: Multiaddr, ttl: number): boolean { if (!isNetworkAddress(ma)) { return false } const config = getNetConfig(ma) let startingConfidence = false for (const mappings of this.mappings.values()) { for (const mapping of mappings) { if (mapping.externalIp === config.host) { this.log('marking %s to %s IP mapping as verified', mapping.internalIp, mapping.externalIp) startingConfidence = mapping.verified mapping.verified = true mapping.expires = Date.now() + ttl mapping.lastVerified = Date.now() } } } return startingConfidence } unconfirm (ma: Multiaddr, ttl: number): boolean { if (!isNetworkAddress(ma)) { return false } const config = getNetConfig(ma) let wasConfident = false for (const mappings of this.mappings.values()) { for (let i = 0; i < mappings.length; i++) { const mapping = mappings[i] if (mapping.externalIp === config.host && mapping.externalPort === config.port && mapping.protocol === config.protocol) { this.log('removing verification of %s:%s to %s:%s %s IP mapping', mapping.externalIp, mapping.externalPort, config.host, config.port, config.protocol) wasConfident = wasConfident || mapping.verified mapping.verified = false mapping.expires = Date.now() + ttl } } } return wasConfident } }