diff --git a/src/transport.js b/src/transport.js index a22b7b5..0937483 100644 --- a/src/transport.js +++ b/src/transport.js @@ -203,23 +203,61 @@ class TransportManager { if (!transport || !transport.filter) return [] const transportAddrs = transport.filter(multiaddrs) - if (!peerInfo) { + if (!peerInfo || !transportAddrs.length) { return transportAddrs } - const ourAddrs = peerInfo.multiaddrs.toArray() - return transportAddrs.filter((addr) => { + const ourAddrs = ourAddresses(peerInfo) + + const result = transportAddrs.filter(transportAddr => { // If our address is in the destination address, filter it out - return !ourAddrs.find((pAddr) => { - try { - addr.decapsulate(pAddr) - } catch (err) { - return false - } - return true - }) + return !ourAddrs.some(a => getDestination(transportAddr).startsWith(a)) }) + + return result } } +/** + * Expand addresses in peer info into array of addresses with and without peer + * ID suffix. + * + * @param {PeerInfo} peerInfo Our peer info object + * @returns {String[]} + */ +function ourAddresses (peerInfo) { + const ourPeerId = peerInfo.id.toB58String() + return peerInfo.multiaddrs.toArray() + .reduce((ourAddrs, addr) => { + const peerId = addr.getPeerId() + addr = addr.toString() + const otherAddr = peerId + ? addr.slice(0, addr.lastIndexOf(`/ipfs/${peerId}`)) + : `${addr}/ipfs/${ourPeerId}` + return ourAddrs.concat([addr, otherAddr]) + }, []) + .concat(`/ipfs/${ourPeerId}`) +} + +const RelayProtos = [ + 'p2p-circuit', + 'p2p-websocket-star', + 'p2p-webrtc-star', + 'p2p-stardust' +] + +/** + * Get the destination address of a (possibly relay) multiaddr as a string + * + * @param {Multiaddr} addr + * @returns {String} + */ +function getDestination (addr) { + const protos = addr.protoNames().reverse() + const splitProto = protos.find(p => RelayProtos.includes(p)) + addr = addr.toString() + if (!splitProto) return addr + return addr.slice(addr.lastIndexOf(splitProto) + splitProto.length) +} + module.exports = TransportManager diff --git a/test/transport-manager.spec.js b/test/transport-manager.spec.js new file mode 100644 index 0000000..901c303 --- /dev/null +++ b/test/transport-manager.spec.js @@ -0,0 +1,127 @@ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +const dirtyChai = require('dirty-chai') +const expect = chai.expect +chai.use(dirtyChai) +const Multiaddr = require('multiaddr') +const PeerInfo = require('peer-info') + +const TransportManager = require('../src/transport') + +describe('Transport Manager', () => { + describe('dialables', () => { + let peerInfo + const dialAllTransport = { filter: addrs => addrs } + + beforeEach(done => { + PeerInfo.create((err, info) => { + if (err) return done(err) + peerInfo = info + done() + }) + }) + + it('should return all transport addresses when peer info has 0 addrs', () => { + const queryAddrs = [ + '/ip4/127.0.0.1/tcp/4002', + '/ip4/192.168.0.3/tcp/4002', + '/ip6/::1/tcp/4001' + ].map(a => Multiaddr(a)) + + const dialableAddrs = TransportManager.dialables(dialAllTransport, queryAddrs, peerInfo) + + expect(dialableAddrs).to.have.length(queryAddrs.length) + + queryAddrs.forEach(qa => { + expect(dialableAddrs.some(da => da.equals(qa))).to.be.true() + }) + }) + + it('should return all transport addresses when we pass no peer info', () => { + const queryAddrs = [ + '/ip4/127.0.0.1/tcp/4002', + '/ip4/192.168.0.3/tcp/4002', + '/ip6/::1/tcp/4001' + ].map(a => Multiaddr(a)) + + const dialableAddrs = TransportManager.dialables(dialAllTransport, queryAddrs) + + expect(dialableAddrs).to.have.length(queryAddrs.length) + + queryAddrs.forEach(qa => { + expect(dialableAddrs.some(da => da.equals(qa))).to.be.true() + }) + }) + + it('should filter our addresses', () => { + const queryAddrs = [ + '/ip4/127.0.0.1/tcp/4002', + '/ip4/192.168.0.3/tcp/4002', + '/ip6/::1/tcp/4001' + ].map(a => Multiaddr(a)) + + const ourAddrs = [ + '/ip4/127.0.0.1/tcp/4002', + '/ip4/192.168.0.3/tcp/4002' + ] + + ourAddrs.forEach(a => peerInfo.multiaddrs.add(a)) + + const dialableAddrs = TransportManager.dialables(dialAllTransport, queryAddrs, peerInfo) + + expect(dialableAddrs).to.have.length(1) + expect(dialableAddrs[0].toString()).to.equal('/ip6/::1/tcp/4001') + }) + + it('should filter our addresses with peer ID suffix', () => { + const queryAddrs = [ + '/ip4/127.0.0.1/tcp/4002/ipfs/QmebzNV1kSzLfaYpSZdShuiABNUxoKT1vJmCdxM2iWsM2j', + '/ip4/192.168.0.3/tcp/4002', + '/ip6/::1/tcp/4001' + ].map(a => Multiaddr(a)) + + const ourAddrs = [ + `/ip4/127.0.0.1/tcp/4002`, + `/ip4/192.168.0.3/tcp/4002/ipfs/${peerInfo.id.toB58String()}` + ] + + ourAddrs.forEach(a => peerInfo.multiaddrs.add(a)) + + const dialableAddrs = TransportManager.dialables(dialAllTransport, queryAddrs, peerInfo) + + expect(dialableAddrs).to.have.length(1) + expect(dialableAddrs[0].toString()).to.equal('/ip6/::1/tcp/4001') + }) + + it('should filter our addresses over relay/rendezvous', () => { + const peerId = peerInfo.id.toB58String() + const queryAddrs = [ + `/p2p-circuit/ipfs/${peerId}`, + `/p2p-circuit/ip4/127.0.0.1/tcp/4002`, + `/p2p-circuit/ip4/192.168.0.3/tcp/4002`, + `/p2p-circuit/ip4/127.0.0.1/tcp/4002/ipfs/${peerId}`, + `/p2p-circuit/ip4/192.168.0.3/tcp/4002/ipfs/${peerId}`, + `/p2p-circuit/ip4/127.0.0.1/tcp/4002/ipfs/QmebzNV1kSzLfaYpSZdShuiABNUxoKT1vJmCdxM2iWsM2j`, + `/p2p-circuit/ip4/192.168.0.3/tcp/4002/ipfs/QmebzNV1kSzLfaYpSZdShuiABNUxoKT1vJmCdxM2iWsM2j`, + `/p2p-webrtc-star/ipfs/${peerId}`, + `/p2p-websocket-star/ipfs/${peerId}`, + `/p2p-stardust/ipfs/${peerId}`, + '/ip6/::1/tcp/4001' + ].map(a => Multiaddr(a)) + + const ourAddrs = [ + `/ip4/127.0.0.1/tcp/4002`, + `/ip4/192.168.0.3/tcp/4002/ipfs/${peerInfo.id.toB58String()}` + ] + + ourAddrs.forEach(a => peerInfo.multiaddrs.add(a)) + + const dialableAddrs = TransportManager.dialables(dialAllTransport, queryAddrs, peerInfo) + + expect(dialableAddrs).to.have.length(1) + expect(dialableAddrs[0].toString()).to.equal('/ip6/::1/tcp/4001') + }) + }) +})