libp2p
Version:
JavaScript implementation of libp2p, a modular peer to peer network stack
157 lines • 6.21 kB
JavaScript
import { isIPv4 } from '@chainsafe/is-ip';
import { getNetConfig, isNetworkAddress, trackedMap } from '@libp2p/utils';
import { CODE_IP4, CODE_IP6, multiaddr } from '@multiformats/multiaddr';
export const defaultValues = {
maxObservedAddresses: 10
};
export class IPMappings {
log;
mappings;
constructor(components, init = {}) {
this.log = components.logger.forComponent('libp2p:address-manager:ip-mappings');
this.mappings = trackedMap({
name: 'libp2p_address_manager_ip_mappings',
metrics: components.metrics
});
}
has(ma) {
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, internalPort, externalIp, externalPort = internalPort, protocol = 'tcp') {
const key = `${internalIp}-${internalPort}-${protocol}`;
const mappings = this.mappings.get(key) ?? [];
const mapping = {
internalIp,
internalPort,
externalIp,
externalPort,
externalFamily: isIPv4(externalIp) ? 4 : 6,
protocol,
verified: false,
expires: 0
};
mappings.push(mapping);
this.mappings.set(key, mappings);
}
remove(ma) {
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) {
const ipMappedAddresses = [];
for (const { multiaddr: ma } of addresses) {
if (!isNetworkAddress(ma)) {
continue;
}
const config = getNetConfig(ma);
if (config.type !== 'ip4' && config.type !== 'ip6') {
continue;
}
let key;
// 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;
}
maybeOverrideIp(ma, externalIp, externalFamily, protocol, externalPort) {
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, ttl) {
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, ttl) {
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;
}
}
//# sourceMappingURL=ip-mappings.js.map