Skip to content

Commit b606df3

Browse files
ricott1jacobheun
authored andcommitted
feat: add onion and onion3 support (#89)
1 parent 44eac00 commit b606df3

File tree

3 files changed

+102
-1
lines changed

3 files changed

+102
-1
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
"class-is": "^1.1.0",
3535
"ip": "^1.1.5",
3636
"is-ip": "^2.0.0",
37-
"varint": "^5.0.0"
37+
"varint": "^5.0.0",
38+
"hi-base32": "~0.5.0"
3839
},
3940
"devDependencies": {
4041
"aegir": "^18.2.0",
@@ -44,6 +45,7 @@
4445
},
4546
"contributors": [
4647
"Alan Shaw <alan@tableflip.io>",
48+
"Alessandro Ricottone <ricott2@gmail.com>",
4749
"Chris Anderson <jchris@gmail.com>",
4850
"David Dias <daviddias.p@gmail.com>",
4951
"Diogo Silva <fsdiogo@gmail.com>",

src/convert.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const ip = require('ip')
44
const isIp = require('is-ip')
55
const protocols = require('./protocols-table')
66
const bs58 = require('bs58')
7+
const base32 = require('hi-base32')
78
const varint = require('varint')
89

910
module.exports = Convert
@@ -39,6 +40,10 @@ Convert.toString = function convertToString (proto, buf) {
3940

4041
case 421: // ipfs
4142
return buf2mh(buf)
43+
case 444: // onion
44+
return buf2onion(buf)
45+
case 445: // onion3
46+
return buf2onion(buf)
4247
default:
4348
return buf.toString('hex') // no clue. convert to hex
4449
}
@@ -67,6 +72,10 @@ Convert.toBuffer = function convertToBuffer (proto, str) {
6772

6873
case 421: // ipfs
6974
return mh2buf(str)
75+
case 444: // onion
76+
return onion2buf(str)
77+
case 445: // onion3
78+
return onion32buf(str)
7079
default:
7180
return Buffer.from(str, 'hex') // no clue. convert from hex
7281
}
@@ -131,3 +140,49 @@ function buf2mh (buf) {
131140

132141
return bs58.encode(address)
133142
}
143+
144+
function onion2buf (str) {
145+
const addr = str.split(':')
146+
if (addr.length !== 2) {
147+
throw new Error('failed to parse onion addr: ' + addr + ' does not contain a port number')
148+
}
149+
if (addr[0].length !== 16) {
150+
throw new Error('failed to parse onion addr: ' + addr[0] + ' not a Tor onion address.')
151+
}
152+
const buf = Buffer.from(base32.decode.asBytes(addr[0].toUpperCase()))
153+
154+
// onion port number
155+
const port = parseInt(addr[1], 10)
156+
if (port < 1 || port > 65536) {
157+
throw new Error('Port number is not in range(1, 65536)')
158+
}
159+
const portBuf = port2buf(port)
160+
return Buffer.concat([buf, portBuf])
161+
}
162+
163+
function onion32buf (str) {
164+
const addr = str.split(':')
165+
if (addr.length !== 2) {
166+
throw new Error('failed to parse onion addr: ' + addr + ' does not contain a port number')
167+
}
168+
if (addr[0].length !== 56) {
169+
throw new Error('failed to parse onion addr: ' + addr[0] + ' not a Tor onion3 address.')
170+
}
171+
const buf = Buffer.from(base32.decode.asBytes(addr[0].toUpperCase()))
172+
173+
// onion port number
174+
const port = parseInt(addr[1], 10)
175+
if (port < 1 || port > 65536) {
176+
throw new Error('Port number is not in range(1, 65536)')
177+
}
178+
const portBuf = port2buf(port)
179+
return Buffer.concat([buf, portBuf])
180+
}
181+
182+
function buf2onion (buf) {
183+
const addrBytes = buf.slice(0, buf.length - 2)
184+
const portBytes = buf.slice(buf.length - 2)
185+
const addr = base32.encode(addrBytes).toString('ascii').toLowerCase()
186+
const port = buf2port(portBytes)
187+
return addr + ':' + port
188+
}

test/index.spec.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,50 @@ describe('variants', () => {
363363
expect(addr.toString()).to.equal(str)
364364
})
365365

366+
it('onion', () => {
367+
const str = '/onion/timaq4ygg2iegci7:1234'
368+
const addr = multiaddr(str)
369+
expect(addr).to.have.property('buffer')
370+
expect(addr.toString()).to.equal(str)
371+
})
372+
373+
it('onion bad length', () => {
374+
const str = '/onion/timaq4ygg2iegci:80'
375+
expect(() => multiaddr(str)).to.throw()
376+
})
377+
378+
it('onion bad port', () => {
379+
const str = '/onion/timaq4ygg2iegci7:-1'
380+
expect(() => multiaddr(str)).to.throw()
381+
})
382+
383+
it('onion no port', () => {
384+
const str = '/onion/timaq4ygg2iegci7'
385+
expect(() => multiaddr(str)).to.throw()
386+
})
387+
388+
it('onion3', () => {
389+
const str = '/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:1234'
390+
const addr = multiaddr(str)
391+
expect(addr).to.have.property('buffer')
392+
expect(addr.toString()).to.equal(str)
393+
})
394+
395+
it('onion3 bad length', () => {
396+
const str = '/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopyyd:1234'
397+
expect(() => multiaddr(str)).to.throw()
398+
})
399+
400+
it('onion3 bad port', () => {
401+
const str = '/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:-1'
402+
expect(() => multiaddr(str)).to.throw()
403+
})
404+
405+
it('onion3 no port', () => {
406+
const str = '/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd'
407+
expect(() => multiaddr(str)).to.throw()
408+
})
409+
366410
it('p2p-circuit', () => {
367411
const str = '/p2p-circuit/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC'
368412
const addr = multiaddr(str)

0 commit comments

Comments
 (0)