diff --git a/src/codec.js b/src/codec.js index 835bdeeb..0a4a8d6f 100644 --- a/src/codec.js +++ b/src/codec.js @@ -52,6 +52,18 @@ function stringToStringTuples (str) { throw ParseError('invalid address: ' + str) } + // if it's a path proto, take the rest + if (proto.path) { + tuples.push([ + part, + // TODO: should we need to check each path part to see if it's a proto? + // This would allow for other protocols to be added after a unix path, + // however it would have issues if the path had a protocol name in the path + cleanPath(parts.slice(p).join('/')) + ]) + break + } + tuples.push([part, parts[p]]) } @@ -69,7 +81,7 @@ function stringTuplesToString (tuples) { } }) - return '/' + parts.join('/') + return cleanPath(parts.join('/')) } // [[str name, str addr]... ] -> [[int code, Buffer]... ] diff --git a/src/convert.js b/src/convert.js index 4ee56049..4fa5279b 100644 --- a/src/convert.js +++ b/src/convert.js @@ -30,9 +30,11 @@ Convert.toString = function convertToString (proto, buf) { case 132: // sctp return buf2port(buf) + case 53: // dns case 54: // dns4 case 55: // dns6 case 56: // dnsaddr + case 400: // unix return buf2str(buf) case 421: // ipfs @@ -56,9 +58,11 @@ Convert.toBuffer = function convertToBuffer (proto, str) { case 132: // sctp return port2buf(parseInt(str, 10)) + case 53: // dns case 54: // dns4 case 55: // dns6 case 56: // dnsaddr + case 400: // unix return str2buf(str) case 421: // ipfs diff --git a/src/index.js b/src/index.js index ade68969..9c5772fb 100644 --- a/src/index.js +++ b/src/index.js @@ -255,6 +255,33 @@ Multiaddr.prototype.getPeerId = function getPeerId () { return b58str } +/** + * Extract the path if the multiaddr contains one + * + * @return {String|null} path - The path of the multiaddr, or null if no path protocol is present + * @example + * const mh1 = Multiaddr('/ip4/8.8.8.8/tcp/1080/unix/tmp/p2p.sock') + * // + * + * // should return utf8 string or null if the id is missing or invalid + * const path = mh1.getPath() + */ +Multiaddr.prototype.getPath = function getPath () { + let path = null + try { + path = this.stringTuples().filter((tuple) => { + const proto = protocols(tuple[0]) + if (proto.path) { + return true + } + })[0][1] + } catch (e) { + path = null + } + + return path +} + /** * Checks if two Multiaddrs are the same * diff --git a/src/protocols-table.js b/src/protocols-table.js index 975ec97c..5eba5d04 100644 --- a/src/protocols-table.js +++ b/src/protocols-table.js @@ -25,32 +25,38 @@ Protocols.V = V Protocols.table = [ [4, 32, 'ip4'], [6, 16, 'tcp'], - [273, 16, 'udp'], [33, 16, 'dccp'], [41, 128, 'ip6'], + [42, V, 'ip6zone'], + [53, V, 'dns', 'resolvable'], [54, V, 'dns4', 'resolvable'], [55, V, 'dns6', 'resolvable'], [56, V, 'dnsaddr', 'resolvable'], [132, 16, 'sctp'], - // all of the below use varint for size + [273, 16, 'udp'], + [275, 0, 'p2p-webrtc-star'], + [276, 0, 'p2p-webrtc-direct'], + [277, 0, 'p2p-stardust'], + [290, 0, 'p2p-circuit'], + [301, 0, 'udt'], [302, 0, 'utp'], + [400, V, 'unix', false, 'path'], // `p2p` is the preferred name for 421 - [421, Protocols.lengthPrefixedVarSize, 'p2p'], + [421, V, 'p2p'], // `ipfs` has been added after `p2p` so that it is used by default. // The reason for this is to provide better backwards support for // code bases that do not yet support the `p2p` proto name. Eventually // `p2p` should become the default. - [421, Protocols.lengthPrefixedVarSize, 'ipfs'], - [480, 0, 'http'], + [421, V, 'ipfs'], [443, 0, 'https'], + [444, 96, 'onion'], + [445, 296, 'onion3'], + [446, V, 'garlic64'], [460, 0, 'quic'], [477, 0, 'ws'], [478, 0, 'wss'], [479, 0, 'p2p-websocket-star'], - [277, 0, 'p2p-stardust'], - [275, 0, 'p2p-webrtc-star'], - [276, 0, 'p2p-webrtc-direct'], - [290, 0, 'p2p-circuit'] + [480, 0, 'http'] ] Protocols.names = {} @@ -65,12 +71,13 @@ Protocols.table.map(row => { Protocols.object = p -function p (code, size, name, resolvable) { +function p (code, size, name, resolvable, path) { return { code: code, size: size, name: name, - resolvable: Boolean(resolvable) + resolvable: Boolean(resolvable), + path: Boolean(path) } } diff --git a/test/index.spec.js b/test/index.spec.js index cca79f41..e91bd9d4 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -260,6 +260,13 @@ describe('variants', () => { expect(addr.toString()).to.equal(str) }) + it('ip4 + tcp + unix', () => { + const str = '/ip4/127.0.0.1/tcp/80/unix/a/b/c/d/e/f' + const addr = multiaddr(str) + expect(addr).to.have.property('buffer') + expect(addr.toString()).to.equal(str) + }) + it('ip6 + tcp + http', () => { const str = '/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/http' const addr = multiaddr(str) @@ -267,6 +274,13 @@ describe('variants', () => { expect(addr.toString()).to.equal(str) }) + it('ip6 + tcp + unix', () => { + const str = '/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/unix/a/b/c/d/e/f' + const addr = multiaddr(str) + expect(addr).to.have.property('buffer') + expect(addr.toString()).to.equal(str) + }) + it('ip4 + tcp + https', () => { const str = '/ip4/127.0.0.1/tcp/8000/https' const addr = multiaddr(str) @@ -323,6 +337,13 @@ describe('variants', () => { expect(addr.toString()).to.equal(str.replace('/p2p/', '/ipfs/')) }) + it('unix', () => { + const str = '/unix/a/b/c/d/e' + const addr = multiaddr(str) + expect(addr).to.have.property('buffer') + expect(addr.toString()).to.equal(str) + }) + it('p2p', () => { const str = '/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC' const addr = multiaddr(str) @@ -413,11 +434,13 @@ describe('helpers', () => { .to.eql([{ code: 4, name: 'ip4', + path: false, size: 32, resolvable: false }, { code: 302, name: 'utp', + path: false, size: 0, resolvable: false }]) @@ -429,16 +452,19 @@ describe('helpers', () => { ).to.be.eql([{ code: 4, name: 'ip4', + path: false, size: 32, resolvable: false }, { code: 302, name: 'utp', + path: false, size: 0, resolvable: false }, { code: 421, name: 'ipfs', + path: false, size: -1, resolvable: false }]) @@ -450,16 +476,43 @@ describe('helpers', () => { ).to.be.eql([{ code: 4, name: 'ip4', + path: false, size: 32, resolvable: false }, { code: 302, name: 'utp', + path: false, size: 0, resolvable: false }, { code: 421, name: 'ipfs', + path: false, + size: -1, + resolvable: false + }]) + }) + + it('works with unix', () => { + expect( + multiaddr('/ip4/0.0.0.0/tcp/8000/unix/tmp/p2p.sock').protos() + ).to.be.eql([{ + code: 4, + name: 'ip4', + path: false, + size: 32, + resolvable: false + }, { + code: 6, + name: 'tcp', + path: false, + size: 16, + resolvable: false + }, { + code: 400, + name: 'unix', + path: true, size: -1, resolvable: false }]) @@ -663,6 +716,26 @@ describe('helpers', () => { }) }) + describe('.getPath', () => { + it('should return a path for unix', () => { + expect( + multiaddr('/unix/tmp/p2p.sock').getPath() + ).to.eql('/tmp/p2p.sock') + }) + + it('should return a path for unix when other protos exist', () => { + expect( + multiaddr('/ip4/0.0.0.0/tcp/1234/unix/tmp/p2p.sock').getPath() + ).to.eql('/tmp/p2p.sock') + }) + + it('should not return a path when no path proto exists', () => { + expect( + multiaddr('/ip4/0.0.0.0/tcp/1234/p2p-circuit/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC').getPath() + ).to.eql(null) + }) + }) + describe('multiaddr.isMultiaddr', () => { it('handles different inputs', () => { expect(multiaddr.isMultiaddr(multiaddr('/'))).to.be.eql(true) @@ -676,6 +749,12 @@ describe('helpers', () => { describe('resolvable multiaddrs', () => { describe('.isName', () => { it('valid name dns', () => { + const str = '/dns/ipfs.io' + const addr = multiaddr(str) + expect(multiaddr.isName(addr)).to.equal(true) + }) + + it('valid name dnsaddr', () => { const str = '/dnsaddr/ipfs.io' const addr = multiaddr(str) expect(multiaddr.isName(addr)).to.equal(true)