libp2p
Version:
JavaScript implementation of libp2p, a modular peer to peer network stack
143 lines • 5.33 kB
JavaScript
import { isPrivateIp } from '@libp2p/utils/private-ip';
import { trackedMap } from '@libp2p/utils/tracked-map';
import { multiaddr, protocols } from '@multiformats/multiaddr';
const MAX_DATE = 8_640_000_000_000_000;
export const defaultValues = {
maxObservedAddresses: 10
};
const CODEC_TLS = 0x01c0;
const CODEC_SNI = 0x01c1;
const CODEC_DNS = 0x35;
const CODEC_DNS4 = 0x36;
const CODEC_DNS6 = 0x37;
const CODEC_DNSADDR = 0x38;
export class DNSMappings {
log;
mappings;
constructor(components, init = {}) {
this.log = components.logger.forComponent('libp2p:address-manager:dns-mappings');
this.mappings = trackedMap({
name: 'libp2p_address_manager_dns_mappings',
metrics: components.metrics
});
}
has(ma) {
const host = this.findHost(ma);
for (const mapping of this.mappings.values()) {
if (mapping.domain === host) {
return true;
}
}
return false;
}
add(domain, addresses) {
addresses.forEach(ip => {
this.log('add DNS mapping %s to %s', ip, domain);
// we are only confident if this is an local domain mapping, otherwise
// we will require external validation
const verified = isPrivateIp(ip) === true;
this.mappings.set(ip, {
domain,
verified,
expires: verified ? MAX_DATE - Date.now() : 0,
lastVerified: verified ? MAX_DATE - Date.now() : undefined
});
});
}
remove(ma) {
const host = this.findHost(ma);
let wasConfident = false;
for (const [ip, mapping] of this.mappings.entries()) {
if (mapping.domain === host) {
this.log('removing %s to %s DNS mapping %e', ip, mapping.domain, new Error('where'));
this.mappings.delete(ip);
wasConfident = wasConfident || mapping.verified;
}
}
return wasConfident;
}
getAll(addresses) {
const dnsMappedAddresses = [];
for (let i = 0; i < addresses.length; i++) {
const address = addresses[i];
const tuples = address.multiaddr.stringTuples();
const host = tuples[0][1];
if (host == null) {
continue;
}
for (const [ip, mapping] of this.mappings.entries()) {
if (host !== ip) {
continue;
}
// insert SNI tuple after TLS tuple, if one is present
const mappedIp = this.maybeAddSNITuple(tuples, mapping.domain);
if (mappedIp) {
// remove the address and replace it with the version that includes
// the SNI tuple
addresses.splice(i, 1);
i--;
dnsMappedAddresses.push({
multiaddr: multiaddr(`/${tuples.map(tuple => {
return [
protocols(tuple[0]).name,
tuple[1]
].join('/');
}).join('/')}`),
verified: mapping.verified,
type: 'dns-mapping',
expires: mapping.expires,
lastVerified: mapping.lastVerified
});
}
}
}
return dnsMappedAddresses;
}
maybeAddSNITuple(tuples, domain) {
for (let j = 0; j < tuples.length; j++) {
if (tuples[j][0] === CODEC_TLS && tuples[j + 1]?.[0] !== CODEC_SNI) {
tuples.splice(j + 1, 0, [CODEC_SNI, domain]);
return true;
}
}
return false;
}
confirm(ma, ttl) {
const host = this.findHost(ma);
let startingConfidence = false;
for (const [ip, mapping] of this.mappings.entries()) {
if (mapping.domain === host) {
this.log('marking %s to %s DNS mapping as verified', ip, mapping.domain);
startingConfidence = mapping.verified;
mapping.verified = true;
mapping.expires = Date.now() + ttl;
mapping.lastVerified = Date.now();
}
}
return startingConfidence;
}
unconfirm(ma, ttl) {
const host = this.findHost(ma);
let wasConfident = false;
for (const [ip, mapping] of this.mappings.entries()) {
if (mapping.domain === host) {
this.log('removing verification of %s to %s DNS mapping', ip, mapping.domain);
wasConfident = wasConfident || mapping.verified;
mapping.verified = false;
mapping.expires = Date.now() + ttl;
}
}
return wasConfident;
}
findHost(ma) {
for (const tuple of ma.stringTuples()) {
if (tuple[0] === CODEC_SNI) {
return tuple[1];
}
if (tuple[0] === CODEC_DNS || tuple[0] === CODEC_DNS4 || tuple[0] === CODEC_DNS6 || tuple[0] === CODEC_DNSADDR) {
return tuple[1];
}
}
}
}
//# sourceMappingURL=dns-mappings.js.map