Skip to content

Commit 398e231

Browse files
authored
fix: allow dialing multiaddrs without peer ids (#1548)
Most multiaddrs have a peer id, e.g. `/ip4/123.123.123.123/tcp/234/p2p/Qmfoo` but some cannot, e.g. `/unix/tmp/app.sock` - we should still be able to dial these addresses with the caveat that the dialer shoul have some other way of obtaining the peer id to ensure they aren't being man-in-the-middled.
1 parent 141e072 commit 398e231

File tree

7 files changed

+148
-125
lines changed

7 files changed

+148
-125
lines changed

src/connection-manager/dialer/index.ts

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type { Connection, ConnectionGater } from '@libp2p/interface-connection'
1818
import type { AbortOptions } from '@libp2p/interfaces'
1919
import type { Startable } from '@libp2p/interfaces/startable'
2020
import { isPeerId, PeerId } from '@libp2p/interface-peer-id'
21-
import { getPeer } from '../../get-peer.js'
21+
import { getPeerAddress } from '../../get-peer.js'
2222
import type { AddressSorter, PeerStore } from '@libp2p/interface-peer-store'
2323
import type { Metrics } from '@libp2p/interface-metrics'
2424
import type { Dialer } from '@libp2p/interface-connection-manager'
@@ -151,24 +151,24 @@ export class DefaultDialer implements Startable, Dialer {
151151
* will be used.
152152
*/
153153
async dial (peerIdOrMultiaddr: PeerId | Multiaddr, options: AbortOptions = {}): Promise<Connection> {
154-
const { id, multiaddrs } = getPeer(peerIdOrMultiaddr)
154+
const { peerId, multiaddr } = getPeerAddress(peerIdOrMultiaddr)
155155

156-
if (this.components.peerId.equals(id)) {
157-
throw errCode(new Error('Tried to dial self'), codes.ERR_DIALED_SELF)
158-
}
159-
160-
log('check multiaddrs %p', id)
156+
if (peerId != null) {
157+
if (this.components.peerId.equals(peerId)) {
158+
throw errCode(new Error('Tried to dial self'), codes.ERR_DIALED_SELF)
159+
}
161160

162-
if (multiaddrs != null && multiaddrs.length > 0) {
163-
log('storing multiaddrs %p', id, multiaddrs)
164-
await this.components.peerStore.addressBook.add(id, multiaddrs)
165-
}
161+
if (multiaddr != null) {
162+
log('storing multiaddrs %p', peerId, multiaddr)
163+
await this.components.peerStore.addressBook.add(peerId, [multiaddr])
164+
}
166165

167-
if (await this.components.connectionGater.denyDialPeer(id)) {
168-
throw errCode(new Error('The dial request is blocked by gater.allowDialPeer'), codes.ERR_PEER_DIAL_INTERCEPTED)
166+
if (await this.components.connectionGater.denyDialPeer(peerId)) {
167+
throw errCode(new Error('The dial request is blocked by gater.allowDialPeer'), codes.ERR_PEER_DIAL_INTERCEPTED)
168+
}
169169
}
170170

171-
log('creating dial target for %p', id)
171+
log('creating dial target for %p', peerId)
172172

173173
// resolving multiaddrs can involve dns lookups so allow them to be aborted
174174
const controller = new AbortController()
@@ -184,7 +184,7 @@ export class DefaultDialer implements Startable, Dialer {
184184
let dialTarget: DialTarget
185185

186186
try {
187-
dialTarget = await this._createDialTarget(peerIdOrMultiaddr, {
187+
dialTarget = await this._createDialTarget({ peerId, multiaddr }, {
188188
...options,
189189
signal
190190
})
@@ -198,7 +198,7 @@ export class DefaultDialer implements Startable, Dialer {
198198
}
199199

200200
// try to join an in-flight dial for this peer if one is available
201-
const pendingDial = this.pendingDials.get(dialTarget.id.toString()) ?? this._createPendingDial(dialTarget, options)
201+
const pendingDial = this.pendingDials.get(dialTarget.id) ?? this._createPendingDial(dialTarget, options)
202202

203203
try {
204204
const connection = await pendingDial.promise
@@ -225,26 +225,33 @@ export class DefaultDialer implements Startable, Dialer {
225225
*
226226
* Multiaddrs not supported by the available transports will be filtered out.
227227
*/
228-
async _createDialTarget (peerIdOrMultiaddr: PeerId | Multiaddr, options: AbortOptions): Promise<DialTarget> {
229-
const _resolve = this._resolve.bind(this)
228+
async _createDialTarget (peerIdOrMultiaddr: { peerId?: PeerId, multiaddr?: Multiaddr }, options: AbortOptions): Promise<DialTarget> {
229+
let addrs: Multiaddr[] = []
230230

231-
let addrs = isMultiaddr(peerIdOrMultiaddr) ? [peerIdOrMultiaddr] : await this._loadAddresses(peerIdOrMultiaddr)
231+
if (isMultiaddr(peerIdOrMultiaddr.multiaddr)) {
232+
addrs.push(peerIdOrMultiaddr.multiaddr)
233+
}
234+
235+
// only load addresses if a peer id was passed, otherwise only dial the passed multiaddr
236+
if (!isMultiaddr(peerIdOrMultiaddr.multiaddr) && isPeerId(peerIdOrMultiaddr.peerId)) {
237+
addrs.push(...await this._loadAddresses(peerIdOrMultiaddr.peerId))
238+
}
232239

233240
addrs = (await Promise.all(
234-
addrs.map(async (ma) => await _resolve(ma, options))
241+
addrs.map(async (ma) => await this._resolve(ma, options))
235242
))
236243
.flat()
237244
// Multiaddrs not supported by the available transports will be filtered out.
238245
.filter(ma => Boolean(this.components.transportManager.transportForMultiaddr(ma)))
239246

240247
// deduplicate addresses
241-
addrs = [...new Set(addrs)]
248+
addrs = [...new Set(addrs.map(ma => ma.toString()))].map(ma => multiaddr(ma))
242249

243250
if (addrs.length > this.maxAddrsToDial) {
244251
throw errCode(new Error('dial with more addresses than allowed'), codes.ERR_TOO_MANY_ADDRESSES)
245252
}
246253

247-
const peerId = isPeerId(peerIdOrMultiaddr) ? peerIdOrMultiaddr : undefined
254+
const peerId = isPeerId(peerIdOrMultiaddr.peerId) ? peerIdOrMultiaddr.peerId : undefined
248255

249256
if (peerId != null) {
250257
const peerIdMultiaddr = `/p2p/${peerId.toString()}`

src/connection-manager/index.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ import type { Connection, MultiaddrConnection } from '@libp2p/interface-connecti
1212
import type { ConnectionManager, ConnectionManagerEvents, Dialer } from '@libp2p/interface-connection-manager'
1313
import * as STATUS from '@libp2p/interface-connection/status'
1414
import type { AddressSorter, PeerStore } from '@libp2p/interface-peer-store'
15-
import { isMultiaddr, multiaddr, Multiaddr, Resolver } from '@multiformats/multiaddr'
15+
import { multiaddr, Multiaddr, Resolver } from '@multiformats/multiaddr'
1616
import { PeerMap } from '@libp2p/peer-collections'
1717
import { TimeoutController } from 'timeout-abort-controller'
1818
import { KEEP_ALIVE } from '@libp2p/interface-peer-store/tags'
1919
import { RateLimiterMemory } from 'rate-limiter-flexible'
2020
import type { Metrics } from '@libp2p/interface-metrics'
2121
import type { Upgrader } from '@libp2p/interface-transport'
22-
import { getPeer } from '../get-peer.js'
22+
import { getPeerAddress } from '../get-peer.js'
2323

2424
const log = logger('libp2p:connection-manager')
2525

@@ -471,24 +471,22 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
471471
}
472472

473473
async openConnection (peerIdOrMultiaddr: PeerId | Multiaddr, options: AbortOptions = {}): Promise<Connection> {
474-
let peerId: PeerId
474+
const { peerId, multiaddr } = getPeerAddress(peerIdOrMultiaddr)
475475

476-
if (isPeerId(peerIdOrMultiaddr)) {
477-
peerId = peerIdOrMultiaddr
478-
} else if (isMultiaddr(peerIdOrMultiaddr)) {
479-
const info = getPeer(peerIdOrMultiaddr)
480-
peerId = info.id
481-
} else {
476+
if (peerId == null && multiaddr == null) {
482477
throw errCode(new TypeError('Can only open connections to PeerIds or Multiaddrs'), codes.ERR_INVALID_PARAMETERS)
483478
}
484479

485-
log('dial to %p', peerId)
486-
const existingConnections = this.getConnections(peerId)
480+
if (peerId != null) {
481+
log('dial to', peerId)
482+
483+
const existingConnections = this.getConnections(peerId)
487484

488-
if (existingConnections.length > 0) {
489-
log('had an existing connection to %p', peerId)
485+
if (existingConnections.length > 0) {
486+
log('had an existing connection to %p', peerId)
490487

491-
return existingConnections[0]
488+
return existingConnections[0]
489+
}
492490
}
493491

494492
let timeoutController: TimeoutController | undefined
@@ -505,11 +503,11 @@ export class DefaultConnectionManager extends EventEmitter<ConnectionManagerEven
505503

506504
try {
507505
const connection = await this.components.dialer.dial(peerIdOrMultiaddr, options)
508-
let peerConnections = this.connections.get(peerId.toString())
506+
let peerConnections = this.connections.get(connection.remotePeer.toString())
509507

510508
if (peerConnections == null) {
511509
peerConnections = []
512-
this.connections.set(peerId.toString(), peerConnections)
510+
this.connections.set(connection.remotePeer.toString(), peerConnections)
513511
}
514512

515513
// we get notified of connections via the Upgrader emitting "connection"

src/get-peer.ts

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,50 +5,28 @@ import errCode from 'err-code'
55
import { codes } from './errors.js'
66
import { isPeerId } from '@libp2p/interface-peer-id'
77
import type { PeerId } from '@libp2p/interface-peer-id'
8-
import type { PeerInfo } from '@libp2p/interface-peer-info'
9-
10-
function peerIdFromMultiaddr (ma: Multiaddr) {
11-
const idStr = ma.getPeerId()
12-
13-
if (idStr == null) {
14-
throw errCode(
15-
new Error(`${ma.toString()} does not have a valid peer type`),
16-
codes.ERR_INVALID_MULTIADDR
17-
)
18-
}
19-
20-
try {
21-
return peerIdFromString(idStr)
22-
} catch (err: any) {
23-
throw errCode(
24-
new Error(`${ma.toString()} is not a valid peer type`),
25-
codes.ERR_INVALID_MULTIADDR
26-
)
27-
}
28-
}
298

309
/**
31-
* Converts the given `peer` to a `PeerInfo` object.
10+
* Extracts a PeerId and/or multiaddr from the passed PeerId or Multiaddr
3211
*/
33-
export function getPeer (peer: PeerId | Multiaddr): PeerInfo {
12+
export function getPeerAddress (peer: PeerId | Multiaddr): { peerId?: PeerId, multiaddr?: Multiaddr } {
3413
if (isPeerId(peer)) {
3514
return {
36-
id: peer,
37-
multiaddrs: [],
38-
protocols: []
15+
peerId: peer
3916
}
4017
}
4118

42-
let addr
43-
4419
if (isMultiaddr(peer)) {
45-
addr = peer
46-
peer = peerIdFromMultiaddr(peer)
47-
}
20+
const peerId = peer.getPeerId()
4821

49-
return {
50-
id: peer,
51-
multiaddrs: addr != null ? [addr] : [],
52-
protocols: []
22+
return {
23+
multiaddr: peer,
24+
peerId: peerId == null ? undefined : peerIdFromString(peerId)
25+
}
5326
}
27+
28+
throw errCode(
29+
new Error(`${peer} is not a PeerId or a Multiaddr`), // eslint-disable-line @typescript-eslint/restrict-template-expressions
30+
codes.ERR_INVALID_MULTIADDR
31+
)
5432
}

src/libp2p.ts

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ import { logger } from '@libp2p/logger'
22
import type { AbortOptions } from '@libp2p/interfaces'
33
import { EventEmitter, CustomEvent } from '@libp2p/interfaces/events'
44
import { Startable, isStartable } from '@libp2p/interfaces/startable'
5-
import type { Multiaddr } from '@multiformats/multiaddr'
5+
import { isMultiaddr, Multiaddr } from '@multiformats/multiaddr'
66
import { MemoryDatastore } from 'datastore-core/memory'
77
import { DefaultPeerRouting } from './peer-routing.js'
88
import { CompoundContentRouting } from './content-routing/index.js'
9-
import { getPeer } from './get-peer.js'
109
import { codes } from './errors.js'
1110
import { DefaultAddressManager } from './address-manager/index.js'
1211
import { DefaultConnectionManager } from './connection-manager/index.js'
@@ -49,6 +48,7 @@ import { DummyDHT } from './dht/dummy-dht.js'
4948
import { DummyPubSub } from './pubsub/dummy-pubsub.js'
5049
import { PeerSet } from '@libp2p/peer-collections'
5150
import { DefaultDialer } from './connection-manager/dialer/index.js'
51+
import { peerIdFromString } from '@libp2p/peer-id'
5252

5353
const log = logger('libp2p')
5454

@@ -354,11 +354,7 @@ export class Libp2pNode extends EventEmitter<Libp2pEvents> implements Libp2p {
354354
}
355355

356356
async dial (peer: PeerId | Multiaddr, options: AbortOptions = {}): Promise<Connection> {
357-
const { id, multiaddrs } = getPeer(peer)
358-
359-
await this.components.peerStore.addressBook.add(id, multiaddrs)
360-
361-
return await this.components.connectionManager.openConnection(id, options)
357+
return await this.components.connectionManager.openConnection(peer, options)
362358
}
363359

364360
async dialProtocol (peer: PeerId | Multiaddr, protocols: string | string[], options: AbortOptions = {}) {
@@ -386,9 +382,11 @@ export class Libp2pNode extends EventEmitter<Libp2pEvents> implements Libp2p {
386382
}
387383

388384
async hangUp (peer: PeerId | Multiaddr): Promise<void> {
389-
const { id } = getPeer(peer)
385+
if (isMultiaddr(peer)) {
386+
peer = peerIdFromString(peer.getPeerId() ?? '')
387+
}
390388

391-
await this.components.connectionManager.closeConnections(id)
389+
await this.components.connectionManager.closeConnections(peer)
392390
}
393391

394392
/**
@@ -431,23 +429,23 @@ export class Libp2pNode extends EventEmitter<Libp2pEvents> implements Libp2p {
431429
}
432430

433431
async fetch (peer: PeerId | Multiaddr, key: string, options: AbortOptions = {}): Promise<Uint8Array | null> {
434-
const { id, multiaddrs } = getPeer(peer)
435-
436-
if (multiaddrs != null) {
437-
await this.components.peerStore.addressBook.add(id, multiaddrs)
432+
if (isMultiaddr(peer)) {
433+
const peerId = peerIdFromString(peer.getPeerId() ?? '')
434+
await this.components.peerStore.addressBook.add(peerId, [peer])
435+
peer = peerId
438436
}
439437

440-
return await this.fetchService.fetch(id, key, options)
438+
return await this.fetchService.fetch(peer, key, options)
441439
}
442440

443441
async ping (peer: PeerId | Multiaddr, options: AbortOptions = {}): Promise<number> {
444-
const { id, multiaddrs } = getPeer(peer)
445-
446-
if (multiaddrs.length > 0) {
447-
await this.components.peerStore.addressBook.add(id, multiaddrs)
442+
if (isMultiaddr(peer)) {
443+
const peerId = peerIdFromString(peer.getPeerId() ?? '')
444+
await this.components.peerStore.addressBook.add(peerId, [peer])
445+
peer = peerId
448446
}
449447

450-
return await this.pingService.ping(id, options)
448+
return await this.pingService.ping(peer, options)
451449
}
452450

453451
async handle (protocols: string | string[], handler: StreamHandler, options?: StreamHandlerOptions): Promise<void> {

src/upgrader.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -250,18 +250,18 @@ export class DefaultUpgrader extends EventEmitter<UpgraderEvents> implements Upg
250250
*/
251251
async upgradeOutbound (maConn: MultiaddrConnection, opts?: UpgraderOptions): Promise<Connection> {
252252
const idStr = maConn.remoteAddr.getPeerId()
253-
if (idStr == null) {
254-
throw errCode(new Error('outbound connection must have a peer id'), codes.ERR_INVALID_MULTIADDR)
255-
}
253+
let remotePeerId: PeerId | undefined
256254

257-
const remotePeerId = peerIdFromString(idStr)
255+
if (idStr != null) {
256+
remotePeerId = peerIdFromString(idStr)
258257

259-
if (await this.components.connectionGater.denyOutboundConnection(remotePeerId, maConn)) {
260-
throw errCode(new Error('The multiaddr connection is blocked by connectionGater.denyOutboundConnection'), codes.ERR_CONNECTION_INTERCEPTED)
258+
if (await this.components.connectionGater.denyOutboundConnection(remotePeerId, maConn)) {
259+
throw errCode(new Error('The multiaddr connection is blocked by connectionGater.denyOutboundConnection'), codes.ERR_CONNECTION_INTERCEPTED)
260+
}
261261
}
262262

263263
let encryptedConn
264-
let remotePeer
264+
let remotePeer: PeerId
265265
let upgradedConn
266266
let cryptoProtocol
267267
let muxerFactory
@@ -300,6 +300,10 @@ export class DefaultUpgrader extends EventEmitter<UpgraderEvents> implements Upg
300300
throw errCode(new Error('The multiaddr connection is blocked by gater.acceptEncryptedConnection'), codes.ERR_CONNECTION_INTERCEPTED)
301301
}
302302
} else {
303+
if (remotePeerId == null) {
304+
throw errCode(new Error('Encryption was skipped but no peer id was passed'), codes.ERR_INVALID_PEER)
305+
}
306+
303307
cryptoProtocol = 'native'
304308
remotePeer = remotePeerId
305309
}
@@ -593,7 +597,7 @@ export class DefaultUpgrader extends EventEmitter<UpgraderEvents> implements Upg
593597
* Attempts to encrypt the given `connection` with the provided connection encrypters.
594598
* The first `ConnectionEncrypter` module to succeed will be used
595599
*/
596-
async _encryptOutbound (connection: MultiaddrConnection, remotePeerId: PeerId): Promise<CryptoResult> {
600+
async _encryptOutbound (connection: MultiaddrConnection, remotePeerId?: PeerId): Promise<CryptoResult> {
597601
const protocols = Array.from(this.connectionEncryption.keys())
598602
log('selecting outbound crypto protocol', protocols)
599603

0 commit comments

Comments
 (0)