libp2p
Version:
JavaScript implementation of libp2p, a modular peer to peer network stack
139 lines • 5.28 kB
JavaScript
import { getNetConfig, isNetworkAddress, isPrivateIp, trackedMap } from '@libp2p/utils';
import { CODE_SNI, CODE_TLS, multiaddr } from '@multiformats/multiaddr';
const MAX_DATE = 8_640_000_000_000_000;
export const defaultValues = {
maxObservedAddresses: 10
};
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 config = getNetConfig(ma);
let host = config.host;
if ((config.type === 'ip4' || config.type === 'ip6') && config.sni != null) {
host = config.sni;
}
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 config = getNetConfig(ma);
if (config.type !== 'ip4' && config.type !== 'ip6') {
return false;
}
let wasConfident = false;
for (const [ip, mapping] of this.mappings.entries()) {
if (mapping.domain === config.sni) {
this.log('removing %s to %s DNS mapping %e', ip, mapping.domain);
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].multiaddr;
if (!isNetworkAddress(address)) {
continue;
}
const config = getNetConfig(address);
for (const [ip, mapping] of this.mappings.entries()) {
if (config.host !== ip) {
continue;
}
// insert SNI tuple after TLS tuple, if one is present
const maWithSni = this.maybeAddSNIComponent(address, mapping.domain);
if (maWithSni != null) {
// remove the address and replace it with the version that includes
// the SNI tuple
addresses.splice(i, 1);
i--;
dnsMappedAddresses.push({
multiaddr: maWithSni,
verified: mapping.verified,
type: 'dns-mapping',
expires: mapping.expires,
lastVerified: mapping.lastVerified
});
}
}
}
return dnsMappedAddresses;
}
maybeAddSNIComponent(ma, domain) {
const components = ma.getComponents();
for (let j = 0; j < components.length; j++) {
if (components[j].code === CODE_TLS && components[j + 1]?.code !== CODE_SNI) {
components.splice(j + 1, 0, {
name: 'sni',
code: CODE_SNI,
value: domain
});
return multiaddr(components);
}
}
}
confirm(ma, ttl) {
const config = getNetConfig(ma);
let host = config.host;
if ((config.type === 'ip4' || config.type === 'ip6') && config.sni != null) {
host = config.sni;
}
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 config = getNetConfig(ma);
if (config.type !== 'ip4' && config.type !== 'ip6') {
return false;
}
const host = config.sni ?? config.host;
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;
}
}
//# sourceMappingURL=dns-mappings.js.map