diff --git a/packages/protocol-autonat/src/autonat.ts b/packages/protocol-autonat/src/autonat.ts index 8e825e34a3..3c9b04c977 100644 --- a/packages/protocol-autonat/src/autonat.ts +++ b/packages/protocol-autonat/src/autonat.ts @@ -15,7 +15,7 @@ import { } from './constants.js' import { Message } from './pb/index.js' import type { AutoNATComponents, AutoNATServiceInit } from './index.js' -import type { Logger, Connection, PeerId, PeerInfo, Startable } from '@libp2p/interface' +import type { Logger, Connection, PeerId, PeerInfo, Startable, AbortOptions } from '@libp2p/interface' import type { IncomingStreamData } from '@libp2p/interface-internal' // if more than 3 peers manage to dial us on what we believe to be our external @@ -95,9 +95,6 @@ export class AutoNATService implements Startable { // appearing in the console setMaxListeners(Infinity, signal) - const ourHosts = this.components.addressManager.getAddresses() - .map(ma => ma.toOptions().host) - try { const self = this @@ -138,193 +135,188 @@ export class AutoNATService implements Startable { return } - const dialRequest = request.dial - - if (dialRequest == null) { - self.log.error('dial was missing from message') + yield Message.encode(await self.handleAutonatMessage(request, data.connection, { + signal + })) + }, + (source) => lp.encode(source), + data.stream + ) + } catch (err) { + this.log.error('error handling incoming autonat stream', err) + } finally { + signal.removeEventListener('abort', onAbort) + } + } - yield Message.encode({ - type: Message.MessageType.DIAL_RESPONSE, - dialResponse: { - status: Message.ResponseStatus.E_BAD_REQUEST, - statusText: 'No Dial message found in message' - } - }) + _verifyExternalAddresses (): void { + void this.verifyExternalAddresses() + .catch(err => { + this.log.error('error verifying external address', err) + }) + } - return - } + private async handleAutonatMessage (message: Message, connection: Connection, options?: AbortOptions): Promise { + const ourHosts = this.components.addressManager.getAddresses() + .map(ma => ma.toOptions().host) - let peerId: PeerId - const peer = dialRequest.peer + const dialRequest = message.dial - if (peer == null || peer.id == null) { - self.log.error('PeerId missing from message') + if (dialRequest == null) { + this.log.error('dial was missing from message') - yield Message.encode({ - type: Message.MessageType.DIAL_RESPONSE, - dialResponse: { - status: Message.ResponseStatus.E_BAD_REQUEST, - statusText: 'missing peer info' - } - }) + return { + type: Message.MessageType.DIAL_RESPONSE, + dialResponse: { + status: Message.ResponseStatus.E_BAD_REQUEST, + statusText: 'No Dial message found in message' + } + } + } - return - } + let peerId: PeerId + const peer = dialRequest.peer - try { - peerId = peerIdFromBytes(peer.id) - } catch (err) { - self.log.error('invalid PeerId', err) + if (peer == null || peer.id == null) { + this.log.error('PeerId missing from message') - yield Message.encode({ - type: Message.MessageType.DIAL_RESPONSE, - dialResponse: { - status: Message.ResponseStatus.E_BAD_REQUEST, - statusText: 'bad peer id' - } - }) + return { + type: Message.MessageType.DIAL_RESPONSE, + dialResponse: { + status: Message.ResponseStatus.E_BAD_REQUEST, + statusText: 'missing peer info' + } + } + } - return - } + try { + peerId = peerIdFromBytes(peer.id) + } catch (err) { + this.log.error('invalid PeerId', err) - self.log('incoming request from %p', peerId) + return { + type: Message.MessageType.DIAL_RESPONSE, + dialResponse: { + status: Message.ResponseStatus.E_BAD_REQUEST, + statusText: 'bad peer id' + } + } + } - // reject any dial requests that arrive via relays - if (!data.connection.remotePeer.equals(peerId)) { - self.log('target peer %p did not equal sending peer %p', peerId, data.connection.remotePeer) + this.log('incoming request from %p', peerId) - yield Message.encode({ - type: Message.MessageType.DIAL_RESPONSE, - dialResponse: { - status: Message.ResponseStatus.E_BAD_REQUEST, - statusText: 'peer id mismatch' - } - }) + // reject any dial requests that arrive via relays + if (!connection.remotePeer.equals(peerId)) { + this.log('target peer %p did not equal sending peer %p', peerId, connection.remotePeer) - return - } + return { + type: Message.MessageType.DIAL_RESPONSE, + dialResponse: { + status: Message.ResponseStatus.E_BAD_REQUEST, + statusText: 'peer id mismatch' + } + } + } - // get a list of multiaddrs to dial - const multiaddrs = peer.addrs - .map(buf => multiaddr(buf)) - .filter(ma => { - const isFromSameHost = ma.toOptions().host === data.connection.remoteAddr.toOptions().host + // get a list of multiaddrs to dial + const multiaddrs = peer.addrs + .map(buf => multiaddr(buf)) + .filter(ma => { + const isFromSameHost = ma.toOptions().host === connection.remoteAddr.toOptions().host - self.log.trace('request to dial %a was sent from %a is same host %s', ma, data.connection.remoteAddr, isFromSameHost) - // skip any Multiaddrs where the target node's IP does not match the sending node's IP - return isFromSameHost - }) - .filter(ma => { - const host = ma.toOptions().host - const isPublicIp = !(isPrivateIp(host) ?? false) + this.log.trace('request to dial %a was sent from %a is same host %s', ma, connection.remoteAddr, isFromSameHost) + // skip any Multiaddrs where the target node's IP does not match the sending node's IP + return isFromSameHost + }) + .filter(ma => { + const host = ma.toOptions().host + const isPublicIp = !(isPrivateIp(host) ?? false) - self.log.trace('host %s was public %s', host, isPublicIp) - // don't try to dial private addresses - return isPublicIp - }) - .filter(ma => { - const host = ma.toOptions().host - const isNotOurHost = !ourHosts.includes(host) + this.log.trace('host %s was public %s', host, isPublicIp) + // don't try to dial private addresses + return isPublicIp + }) + .filter(ma => { + const host = ma.toOptions().host + const isNotOurHost = !ourHosts.includes(host) - self.log.trace('host %s was not our host %s', host, isNotOurHost) - // don't try to dial nodes on the same host as us - return isNotOurHost - }) - .filter(ma => { - const isSupportedTransport = Boolean(self.components.transportManager.dialTransportForMultiaddr(ma)) + this.log.trace('host %s was not our host %s', host, isNotOurHost) + // don't try to dial nodes on the same host as us + return isNotOurHost + }) + .filter(ma => { + const isSupportedTransport = Boolean(this.components.transportManager.dialTransportForMultiaddr(ma)) - self.log.trace('transport for %a is supported %s', ma, isSupportedTransport) - // skip any Multiaddrs that have transports we do not support - return isSupportedTransport - }) - .map(ma => { - if (ma.getPeerId() == null) { - // make sure we have the PeerId as part of the Multiaddr - ma = ma.encapsulate(`/p2p/${peerId.toString()}`) - } + this.log.trace('transport for %a is supported %s', ma, isSupportedTransport) + // skip any Multiaddrs that have transports we do not support + return isSupportedTransport + }) + .map(ma => { + if (ma.getPeerId() == null) { + // make sure we have the PeerId as part of the Multiaddr + ma = ma.encapsulate(`/p2p/${peerId.toString()}`) + } - return ma - }) + return ma + }) - // make sure we have something to dial - if (multiaddrs.length === 0) { - self.log('no valid multiaddrs for %p in message', peerId) + // make sure we have something to dial + if (multiaddrs.length === 0) { + this.log('no valid multiaddrs for %p in message', peerId) - yield Message.encode({ - type: Message.MessageType.DIAL_RESPONSE, - dialResponse: { - status: Message.ResponseStatus.E_DIAL_REFUSED, - statusText: 'no dialable addresses' - } - }) + return { + type: Message.MessageType.DIAL_RESPONSE, + dialResponse: { + status: Message.ResponseStatus.E_DIAL_REFUSED, + statusText: 'no dialable addresses' + } + } + } - return - } + this.log('dial multiaddrs %s for peer %p', multiaddrs.map(ma => ma.toString()).join(', '), peerId) - self.log('dial multiaddrs %s for peer %p', multiaddrs.map(ma => ma.toString()).join(', '), peerId) + let errorMessage = '' + let lastMultiaddr = multiaddrs[0] - let errorMessage = '' - let lastMultiaddr = multiaddrs[0] + for await (const multiaddr of multiaddrs) { + let connection: Connection | undefined + lastMultiaddr = multiaddr - for await (const multiaddr of multiaddrs) { - let connection: Connection | undefined - lastMultiaddr = multiaddr + try { + connection = await this.components.connectionManager.openConnection(multiaddr, options) - try { - connection = await self.components.connectionManager.openConnection(multiaddr, { - signal - }) + if (!connection.remoteAddr.equals(multiaddr)) { + this.log.error('tried to dial %a but dialed %a', multiaddr, connection.remoteAddr) + throw new Error('Unexpected remote address') + } - if (!connection.remoteAddr.equals(multiaddr)) { - self.log.error('tried to dial %a but dialed %a', multiaddr, connection.remoteAddr) - throw new Error('Unexpected remote address') - } + this.log('Success %p', peerId) - self.log('Success %p', peerId) - - yield Message.encode({ - type: Message.MessageType.DIAL_RESPONSE, - dialResponse: { - status: Message.ResponseStatus.OK, - addr: connection.remoteAddr.decapsulateCode(protocols('p2p').code).bytes - } - }) - - return - } catch (err: any) { - self.log('could not dial %p', peerId, err) - errorMessage = err.message - } finally { - if (connection != null) { - await connection.close() - } - } + return { + type: Message.MessageType.DIAL_RESPONSE, + dialResponse: { + status: Message.ResponseStatus.OK, + addr: connection.remoteAddr.decapsulateCode(protocols('p2p').code).bytes } - - yield Message.encode({ - type: Message.MessageType.DIAL_RESPONSE, - dialResponse: { - status: Message.ResponseStatus.E_DIAL_ERROR, - statusText: errorMessage, - addr: lastMultiaddr.bytes - } - }) - }, - (source) => lp.encode(source), - data.stream - ) - } catch (err) { - this.log.error('error handling incoming autonat stream', err) - } finally { - signal.removeEventListener('abort', onAbort) + } + } catch (err: any) { + this.log('could not dial %p', peerId, err) + errorMessage = err.message + } finally { + if (connection != null) { + await connection.close() + } + } } - } - _verifyExternalAddresses (): void { - void this.verifyExternalAddresses() - .catch(err => { - this.log.error('error verifying external address', err) - }) + return { + type: Message.MessageType.DIAL_RESPONSE, + dialResponse: { + status: Message.ResponseStatus.E_DIAL_ERROR, + statusText: errorMessage, + addr: lastMultiaddr.bytes + } + } } /**