Skip to content

Commit 7dcabb8

Browse files
authored
feat: add dns mappings to address manager (#2818)
To allow dynamically adding new multiaddrs to the listening-on list allow adding runtime mappings of ip to domain names. This allows things like the auto-tls component to affect the returned list of multiaddrs to include newly minted domain names.
1 parent 7626b22 commit 7dcabb8

File tree

11 files changed

+240
-110
lines changed

11 files changed

+240
-110
lines changed

packages/interface-internal/src/address-manager/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,17 @@ export interface AddressManager {
4040
* Get the current node's addresses
4141
*/
4242
getAddresses(): Multiaddr[]
43+
44+
/**
45+
* Adds a mapping between one or more IP addresses and a domain name - when
46+
* `getAddresses` is invoked, where the IP addresses are present in a
47+
* multiaddr, an additional multiaddr will be added with `ip4` and `ip6`
48+
* tuples replaced with `dns4` and `dns6 ones respectively.
49+
*/
50+
addDNSMapping(domain: string, ipAddresses: string[]): void
51+
52+
/**
53+
* Remove a mapping previously added with `addDNSMapping`.
54+
*/
55+
removeDNSMapping(domain: string): void
4356
}

packages/libp2p/src/address-manager/index.ts renamed to packages/libp2p/src/address-manager.ts

Lines changed: 111 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { peerIdFromString } from '@libp2p/peer-id'
2-
import { multiaddr } from '@multiformats/multiaddr'
3-
import { debounce } from './utils.js'
2+
import { debounce } from '@libp2p/utils/debounce'
3+
import { multiaddr, protocols } from '@multiformats/multiaddr'
44
import type { ComponentLogger, Libp2pEvents, Logger, TypedEventTarget, PeerId, PeerStore } from '@libp2p/interface'
5-
import type { TransportManager } from '@libp2p/interface-internal'
5+
import type { AddressManager as AddressManagerInterface, TransportManager } from '@libp2p/interface-internal'
66
import type { Multiaddr } from '@multiformats/multiaddr'
77

88
export interface AddressManagerInit {
@@ -28,7 +28,7 @@ export interface AddressManagerInit {
2828
noAnnounce?: string[]
2929
}
3030

31-
export interface DefaultAddressManagerComponents {
31+
export interface AddressManagerComponents {
3232
peerId: PeerId
3333
transportManager: TransportManager
3434
peerStore: PeerStore
@@ -69,29 +69,38 @@ function stripPeerId (ma: Multiaddr, peerId: PeerId): Multiaddr {
6969
return ma
7070
}
7171

72-
export class DefaultAddressManager {
72+
const CODEC_IP4 = 0x04
73+
const CODEC_IP6 = 0x29
74+
const CODEC_DNS4 = 0x36
75+
const CODEC_DNS6 = 0x37
76+
77+
export class AddressManager implements AddressManagerInterface {
7378
private readonly log: Logger
74-
private readonly components: DefaultAddressManagerComponents
79+
private readonly components: AddressManagerComponents
7580
// this is an array to allow for duplicates, e.g. multiples of `/ip4/0.0.0.0/tcp/0`
7681
private readonly listen: string[]
7782
private readonly announce: Set<string>
7883
private readonly observed: Map<string, ObservedAddressMetadata>
7984
private readonly announceFilter: AddressFilter
85+
private readonly ipDomainMappings: Map<string, string>
86+
87+
private readonly where: Error
8088

8189
/**
8290
* Responsible for managing the peer addresses.
8391
* Peers can specify their listen and announce addresses.
8492
* The listen addresses will be used by the libp2p transports to listen for new connections,
8593
* while the announce addresses will be used for the peer addresses' to other peers in the network.
8694
*/
87-
constructor (components: DefaultAddressManagerComponents, init: AddressManagerInit = {}) {
95+
constructor (components: AddressManagerComponents, init: AddressManagerInit = {}) {
8896
const { listen = [], announce = [] } = init
8997

9098
this.components = components
9199
this.log = components.logger.forComponent('libp2p:address-manager')
92100
this.listen = listen.map(ma => ma.toString())
93101
this.announce = new Set(announce.map(ma => ma.toString()))
94102
this.observed = new Map()
103+
this.ipDomainMappings = new Map()
95104
this.announceFilter = init.announceFilter ?? defaultAddressFilter
96105

97106
// this method gets called repeatedly on startup when transports start listening so
@@ -106,6 +115,8 @@ export class DefaultAddressManager {
106115
components.events.addEventListener('transport:close', () => {
107116
this._updatePeerStoreAddresses()
108117
})
118+
119+
this.where = new Error('where')
109120
}
110121

111122
readonly [Symbol.toStringTag] = '@libp2p/address-manager'
@@ -200,37 +211,109 @@ export class DefaultAddressManager {
200211
}
201212

202213
getAddresses (): Multiaddr[] {
203-
let addrs = this.getAnnounceAddrs().map(ma => ma.toString())
214+
let multiaddrs = this.getAnnounceAddrs()
204215

205-
if (addrs.length === 0) {
216+
if (multiaddrs.length === 0) {
206217
// no configured announce addrs, add configured listen addresses
207-
addrs = this.components.transportManager.getAddrs().map(ma => ma.toString())
218+
multiaddrs = this.components.transportManager.getAddrs()
208219
}
209220

210221
// add observed addresses we are confident in
211-
addrs = addrs.concat(
212-
Array.from(this.observed)
213-
.filter(([ma, metadata]) => metadata.confident)
214-
.map(([ma]) => ma)
215-
)
222+
multiaddrs = multiaddrs
223+
.concat(
224+
Array.from(this.observed)
225+
.filter(([ma, metadata]) => metadata.confident)
226+
.map(([ma]) => multiaddr(ma))
227+
)
228+
229+
const mappedMultiaddrs: Multiaddr[] = []
230+
231+
// add ip->domain mappings
232+
for (const ma of multiaddrs) {
233+
const tuples = [...ma.stringTuples()]
234+
let mappedIp = false
235+
236+
for (const [ip, domain] of this.ipDomainMappings.entries()) {
237+
for (let i = 0; i < tuples.length; i++) {
238+
if (tuples[i][1] !== ip) {
239+
continue
240+
}
241+
242+
if (tuples[i][0] === CODEC_IP4) {
243+
tuples[i][0] = CODEC_DNS4
244+
tuples[i][1] = domain
245+
mappedIp = true
246+
}
247+
248+
if (tuples[i][0] === CODEC_IP6) {
249+
tuples[i][0] = CODEC_DNS6
250+
tuples[i][1] = domain
251+
mappedIp = true
252+
}
253+
}
254+
}
255+
256+
if (mappedIp) {
257+
mappedMultiaddrs.push(
258+
multiaddr(`/${
259+
tuples.map(tuple => {
260+
return [
261+
protocols(tuple[0]).name,
262+
tuple[1]
263+
].join('/')
264+
}).join('/')
265+
}`)
266+
)
267+
}
268+
}
269+
270+
multiaddrs = multiaddrs.concat(mappedMultiaddrs)
216271

217272
// dedupe multiaddrs
218-
const addrSet = new Set(addrs)
273+
const addrSet = new Set<string>()
274+
multiaddrs = multiaddrs.filter(ma => {
275+
const maStr = ma.toString()
276+
277+
if (addrSet.has(maStr)) {
278+
return false
279+
}
280+
281+
addrSet.add(maStr)
282+
283+
return true
284+
})
219285

220286
// Create advertising list
221-
return this.announceFilter(Array.from(addrSet)
222-
.map(str => multiaddr(str)))
223-
.map(ma => {
224-
// do not append our peer id to a path multiaddr as it will become invalid
225-
if (ma.protos().pop()?.path === true) {
226-
return ma
227-
}
287+
return this.announceFilter(
288+
Array.from(addrSet)
289+
.map(str => {
290+
const ma = multiaddr(str)
291+
292+
// do not append our peer id to a path multiaddr as it will become invalid
293+
if (ma.protos().pop()?.path === true) {
294+
return ma
295+
}
296+
297+
if (ma.getPeerId() === this.components.peerId.toString()) {
298+
return ma
299+
}
300+
301+
return ma.encapsulate(`/p2p/${this.components.peerId.toString()}`)
302+
})
303+
)
304+
}
228305

229-
if (ma.getPeerId() === this.components.peerId.toString()) {
230-
return ma
231-
}
306+
addDNSMapping (domain: string, addresses: string[]): void {
307+
addresses.forEach(ip => {
308+
this.ipDomainMappings.set(ip, domain)
309+
})
310+
}
232311

233-
return ma.encapsulate(`/p2p/${this.components.peerId.toString()}`)
234-
})
312+
removeDNSMapping (domain: string): void {
313+
for (const [key, value] of this.ipDomainMappings.entries()) {
314+
if (value === domain) {
315+
this.ipDomainMappings.delete(key)
316+
}
317+
}
235318
}
236319
}

packages/libp2p/src/address-manager/README.md

Lines changed: 0 additions & 43 deletions
This file was deleted.

packages/libp2p/src/address-manager/utils.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

packages/libp2p/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { generateKeyPair } from '@libp2p/crypto/keys'
1818
import { peerIdFromPrivateKey } from '@libp2p/peer-id'
1919
import { validateConfig } from './config.js'
2020
import { Libp2p as Libp2pClass } from './libp2p.js'
21-
import type { AddressManagerInit, AddressFilter } from './address-manager/index.js'
21+
import type { AddressManagerInit, AddressFilter } from './address-manager.js'
2222
import type { Components } from './components.js'
2323
import type { ConnectionManagerInit } from './connection-manager/index.js'
2424
import type { ConnectionMonitorInit } from './connection-monitor.js'

packages/libp2p/src/libp2p.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { isMultiaddr, type Multiaddr } from '@multiformats/multiaddr'
88
import { MemoryDatastore } from 'datastore-core/memory'
99
import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
1010
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
11-
import { DefaultAddressManager } from './address-manager/index.js'
11+
import { AddressManager } from './address-manager.js'
1212
import { checkServiceDependencies, defaultComponents } from './components.js'
1313
import { connectionGater } from './config/connection-gater.js'
1414
import { DefaultConnectionManager } from './connection-manager/index.js'
@@ -129,7 +129,7 @@ export class Libp2p<T extends ServiceMap = ServiceMap> extends TypedEventEmitter
129129
this.configureComponent('registrar', new DefaultRegistrar(this.components))
130130

131131
// Addresses {listen, announce, noAnnounce}
132-
this.configureComponent('addressManager', new DefaultAddressManager(this.components, init.addresses))
132+
this.configureComponent('addressManager', new AddressManager(this.components, init.addresses))
133133

134134
// Peer routers
135135
const peerRouters: PeerRouting[] = (init.peerRouters ?? []).map((fn, index) => this.configureComponent(`peer-router-${index}`, fn(this.components)))

0 commit comments

Comments
 (0)