1 | 'use strict'
|
2 |
|
3 | const ip = require('./ip')
|
4 | const protocols = require('./protocols-table')
|
5 | const { CID } = require('multiformats/cid')
|
6 | const { base32 } = require('multiformats/bases/base32')
|
7 | const { base58btc } = require('multiformats/bases/base58')
|
8 | const Digest = require('multiformats/hashes/digest')
|
9 | const varint = require('varint')
|
10 | const { toString: uint8ArrayToString } = require('uint8arrays/to-string')
|
11 | const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string')
|
12 | const { concat: uint8ArrayConcat } = require('uint8arrays/concat')
|
13 |
|
14 | module.exports = Convert
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | function Convert (proto, a) {
|
22 | if (a instanceof Uint8Array) {
|
23 | return Convert.toString(proto, a)
|
24 | } else {
|
25 | return Convert.toBytes(proto, a)
|
26 | }
|
27 | }
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 | Convert.toString = function convertToString (proto, buf) {
|
37 | const protocol = protocols(proto)
|
38 | switch (protocol.code) {
|
39 | case 4:
|
40 | case 41:
|
41 | return bytes2ip(buf)
|
42 |
|
43 | case 6:
|
44 | case 273:
|
45 | case 33:
|
46 | case 132:
|
47 | return bytes2port(buf).toString()
|
48 |
|
49 | case 53:
|
50 | case 54:
|
51 | case 55:
|
52 | case 56:
|
53 | case 400:
|
54 | case 777:
|
55 | return bytes2str(buf)
|
56 |
|
57 | case 421:
|
58 | return bytes2mh(buf)
|
59 | case 444:
|
60 | return bytes2onion(buf)
|
61 | case 445:
|
62 | return bytes2onion(buf)
|
63 | default:
|
64 | return uint8ArrayToString(buf, 'base16')
|
65 | }
|
66 | }
|
67 |
|
68 | Convert.toBytes = function convertToBytes (/** @type {string | number } */ proto, /** @type {string} */ str) {
|
69 | const protocol = protocols(proto)
|
70 | switch (protocol.code) {
|
71 | case 4:
|
72 | return ip2bytes(str)
|
73 | case 41:
|
74 | return ip2bytes(str)
|
75 |
|
76 | case 6:
|
77 | case 273:
|
78 | case 33:
|
79 | case 132:
|
80 | return port2bytes(parseInt(str, 10))
|
81 |
|
82 | case 53:
|
83 | case 54:
|
84 | case 55:
|
85 | case 56:
|
86 | case 400:
|
87 | case 777:
|
88 | return str2bytes(str)
|
89 |
|
90 | case 421:
|
91 | return mh2bytes(str)
|
92 | case 444:
|
93 | return onion2bytes(str)
|
94 | case 445:
|
95 | return onion32bytes(str)
|
96 | default:
|
97 | return uint8ArrayFromString(str, 'base16')
|
98 | }
|
99 | }
|
100 |
|
101 |
|
102 |
|
103 |
|
104 | function ip2bytes (ipString) {
|
105 | if (!ip.isIP(ipString)) {
|
106 | throw new Error('invalid ip address')
|
107 | }
|
108 | return ip.toBytes(ipString)
|
109 | }
|
110 |
|
111 |
|
112 |
|
113 |
|
114 | function bytes2ip (ipBuff) {
|
115 | const ipString = ip.toString(ipBuff)
|
116 | if (!ipString || !ip.isIP(ipString)) {
|
117 | throw new Error('invalid ip address')
|
118 | }
|
119 | return ipString
|
120 | }
|
121 |
|
122 |
|
123 |
|
124 |
|
125 | function port2bytes (port) {
|
126 | const buf = new ArrayBuffer(2)
|
127 | const view = new DataView(buf)
|
128 | view.setUint16(0, port)
|
129 |
|
130 | return new Uint8Array(buf)
|
131 | }
|
132 |
|
133 |
|
134 |
|
135 |
|
136 | function bytes2port (buf) {
|
137 | const view = new DataView(buf.buffer)
|
138 | return view.getUint16(buf.byteOffset)
|
139 | }
|
140 |
|
141 |
|
142 |
|
143 |
|
144 | function str2bytes (str) {
|
145 | const buf = uint8ArrayFromString(str)
|
146 | const size = Uint8Array.from(varint.encode(buf.length))
|
147 | return uint8ArrayConcat([size, buf], size.length + buf.length)
|
148 | }
|
149 |
|
150 |
|
151 |
|
152 |
|
153 | function bytes2str (buf) {
|
154 | const size = varint.decode(buf)
|
155 | buf = buf.slice(varint.decode.bytes)
|
156 |
|
157 | if (buf.length !== size) {
|
158 | throw new Error('inconsistent lengths')
|
159 | }
|
160 |
|
161 | return uint8ArrayToString(buf)
|
162 | }
|
163 |
|
164 |
|
165 |
|
166 |
|
167 | function mh2bytes (hash) {
|
168 | let mh
|
169 |
|
170 | if (hash[0] === 'Q' || hash[0] === '1') {
|
171 | mh = Digest.decode(base58btc.decode(`z${hash}`)).bytes
|
172 | } else {
|
173 | mh = CID.parse(hash).multihash.bytes
|
174 | }
|
175 |
|
176 |
|
177 | const size = Uint8Array.from(varint.encode(mh.length))
|
178 | return uint8ArrayConcat([size, mh], size.length + mh.length)
|
179 | }
|
180 |
|
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 | function bytes2mh (buf) {
|
188 | const size = varint.decode(buf)
|
189 | const address = buf.slice(varint.decode.bytes)
|
190 |
|
191 | if (address.length !== size) {
|
192 | throw new Error('inconsistent lengths')
|
193 | }
|
194 |
|
195 | return uint8ArrayToString(address, 'base58btc')
|
196 | }
|
197 |
|
198 |
|
199 |
|
200 |
|
201 | function onion2bytes (str) {
|
202 | const addr = str.split(':')
|
203 | if (addr.length !== 2) {
|
204 | throw new Error('failed to parse onion addr: ' + addr + ' does not contain a port number')
|
205 | }
|
206 | if (addr[0].length !== 16) {
|
207 | throw new Error('failed to parse onion addr: ' + addr[0] + ' not a Tor onion address.')
|
208 | }
|
209 |
|
210 |
|
211 | const buf = base32.decode('b' + addr[0])
|
212 |
|
213 |
|
214 | const port = parseInt(addr[1], 10)
|
215 | if (port < 1 || port > 65536) {
|
216 | throw new Error('Port number is not in range(1, 65536)')
|
217 | }
|
218 | const portBuf = port2bytes(port)
|
219 | return uint8ArrayConcat([buf, portBuf], buf.length + portBuf.length)
|
220 | }
|
221 |
|
222 |
|
223 |
|
224 |
|
225 | function onion32bytes (str) {
|
226 | const addr = str.split(':')
|
227 | if (addr.length !== 2) {
|
228 | throw new Error('failed to parse onion addr: ' + addr + ' does not contain a port number')
|
229 | }
|
230 | if (addr[0].length !== 56) {
|
231 | throw new Error('failed to parse onion addr: ' + addr[0] + ' not a Tor onion3 address.')
|
232 | }
|
233 |
|
234 | const buf = base32.decode('b' + addr[0])
|
235 |
|
236 |
|
237 | const port = parseInt(addr[1], 10)
|
238 | if (port < 1 || port > 65536) {
|
239 | throw new Error('Port number is not in range(1, 65536)')
|
240 | }
|
241 | const portBuf = port2bytes(port)
|
242 | return uint8ArrayConcat([buf, portBuf], buf.length + portBuf.length)
|
243 | }
|
244 |
|
245 |
|
246 |
|
247 |
|
248 | function bytes2onion (buf) {
|
249 | const addrBytes = buf.slice(0, buf.length - 2)
|
250 | const portBytes = buf.slice(buf.length - 2)
|
251 | const addr = uint8ArrayToString(addrBytes, 'base32')
|
252 | const port = bytes2port(portBytes)
|
253 | return addr + ':' + port
|
254 | }
|