diff --git a/doc/CONFIGURATION.md b/doc/CONFIGURATION.md index dcf85756..a1a5149e 100644 --- a/doc/CONFIGURATION.md +++ b/doc/CONFIGURATION.md @@ -525,6 +525,7 @@ Dialing in libp2p can be configured to limit the rate of dialing, and how long d | Name | Type | Description | |------|------|-------------| | maxParallelDials | `number` | How many multiaddrs we can dial in parallel. | +| maxAddrsToDial | `number` | How many multiaddrs is the dial allowed to dial for a single peer. | | maxDialsPerPeer | `number` | How many multiaddrs we can dial per peer, in parallel. | | dialTimeout | `number` | Second dial timeout per peer in ms. | | resolvers | `object` | Dial [Resolvers](https://github.com/multiformats/js-multiaddr/blob/master/src/resolvers/index.js) for resolving multiaddrs | @@ -549,6 +550,7 @@ const node = await Libp2p.create({ }, dialer: { maxParallelDials: 100, + maxAddrsToDial: 25, maxDialsPerPeer: 4, dialTimeout: 30e3, resolvers: { diff --git a/src/constants.js b/src/constants.js index 08ad1569..76a2abd8 100644 --- a/src/constants.js +++ b/src/constants.js @@ -4,6 +4,7 @@ module.exports = { DIAL_TIMEOUT: 30e3, // How long in ms a dial attempt is allowed to take MAX_PARALLEL_DIALS: 100, // Maximum allowed concurrent dials MAX_PER_PEER_DIALS: 4, // Allowed parallel dials per DialRequest + MAX_ADDRS_TO_DIAL: 25, // Maximum number of allowed addresses to attempt to dial METRICS: { computeThrottleMaxQueueSize: 1000, computeThrottleTimeout: 2000, diff --git a/src/dialer/index.js b/src/dialer/index.js index 3be5c36b..65afe266 100644 --- a/src/dialer/index.js +++ b/src/dialer/index.js @@ -19,7 +19,8 @@ const { codes } = require('../errors') const { DIAL_TIMEOUT, MAX_PARALLEL_DIALS, - MAX_PER_PEER_DIALS + MAX_PER_PEER_DIALS, + MAX_ADDRS_TO_DIAL } = require('../constants') /** @@ -40,6 +41,7 @@ const { * @typedef {Object} DialerOptions * @property {(addresses: Address[]) => Address[]} [options.addressSorter = publicAddressesFirst] - Sort the known addresses of a peer before trying to dial. * @property {number} [maxParallelDials = MAX_PARALLEL_DIALS] - Number of max concurrent dials. + * @property {number} [maxAddrsToDial = MAX_ADDRS_TO_DIAL] - Number of max addresses to dial for a given peer. * @property {number} [maxDialsPerPeer = MAX_PER_PEER_DIALS] - Number of max concurrent dials per peer. * @property {number} [dialTimeout = DIAL_TIMEOUT] - How long a dial attempt is allowed to take. * @property {Record} [resolvers = {}] - multiaddr resolvers to use when dialing @@ -65,6 +67,7 @@ class Dialer { peerStore, addressSorter = publicAddressesFirst, maxParallelDials = MAX_PARALLEL_DIALS, + maxAddrsToDial = MAX_ADDRS_TO_DIAL, dialTimeout = DIAL_TIMEOUT, maxDialsPerPeer = MAX_PER_PEER_DIALS, resolvers = {} @@ -73,6 +76,7 @@ class Dialer { this.peerStore = peerStore this.addressSorter = addressSorter this.maxParallelDials = maxParallelDials + this.maxAddrsToDial = maxAddrsToDial this.timeout = dialTimeout this.maxDialsPerPeer = maxDialsPerPeer this.tokens = [...new Array(maxParallelDials)].map((_, index) => index) @@ -198,6 +202,11 @@ class Dialer { // Multiaddrs not supported by the available transports will be filtered out. const supportedAddrs = addrs.filter(a => this.transportManager.transportForMultiaddr(a)) + if (supportedAddrs.length > this.maxAddrsToDial) { + this.peerStore.delete(id) + throw errCode(new Error('dial with more addresses than allowed'), codes.ERR_TOO_MANY_ADDRESSES) + } + return { id: id.toB58String(), addrs: supportedAddrs diff --git a/src/errors.js b/src/errors.js index 0b73b983..5b4d070f 100644 --- a/src/errors.js +++ b/src/errors.js @@ -16,6 +16,7 @@ exports.codes = { ERR_CONNECTION_FAILED: 'ERR_CONNECTION_FAILED', ERR_NODE_NOT_STARTED: 'ERR_NODE_NOT_STARTED', ERR_ALREADY_ABORTED: 'ERR_ALREADY_ABORTED', + ERR_TOO_MANY_ADDRESSES: 'ERR_TOO_MANY_ADDRESSES', ERR_NO_VALID_ADDRESSES: 'ERR_NO_VALID_ADDRESSES', ERR_RELAYED_DIAL: 'ERR_RELAYED_DIAL', ERR_DIALED_SELF: 'ERR_DIALED_SELF', diff --git a/test/dialing/direct.spec.js b/test/dialing/direct.spec.js index 1dc29544..a24a1ca2 100644 --- a/test/dialing/direct.spec.js +++ b/test/dialing/direct.spec.js @@ -177,6 +177,26 @@ describe('Dialing (direct, WebSockets)', () => { .and.to.have.property('code', ErrorCodes.ERR_TIMEOUT) }) + it('should throw when a peer advertises more than the allowed number of peers', async () => { + const spy = sinon.spy() + const dialer = new Dialer({ + transportManager: localTM, + maxAddrsToDial: 10, + peerStore: { + delete: spy, + addressBook: { + add: () => { }, + getMultiaddrsForPeer: () => Array.from({ length: 11 }, (_, i) => new Multiaddr(`/ip4/127.0.0.1/tcp/1500${i}/ws/p2p/12D3KooWHFKTMzwerBtsVmtz4ZZEQy2heafxzWw6wNn5PPYkBxJ5`)) + } + } + }) + + await expect(dialer.connectToPeer(remoteAddr)) + .to.eventually.be.rejected() + .and.to.have.property('code', ErrorCodes.ERR_TOO_MANY_ADDRESSES) + expect(spy.calledOnce).to.be.true() + }) + it('should sort addresses on dial', async () => { const peerMultiaddrs = [ new Multiaddr('/ip4/127.0.0.1/tcp/15001/ws'),