|
1 | | -import { isIPv4 } from '@chainsafe/is-ip' |
2 | 1 | import { IpNet } from '@chainsafe/netmask' |
3 | | -import { base32 } from 'multiformats/bases/base32' |
4 | | -import { bases } from 'multiformats/basics' |
5 | | -import { concat as uint8ArrayConcat } from 'uint8arrays/concat' |
6 | 2 | import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' |
7 | 3 | import { toString as uint8ArrayToString } from 'uint8arrays/to-string' |
8 | | -import { InvalidMultiaddrError } from './errors.ts' |
9 | 4 | import { registry } from './registry.js' |
10 | 5 | import type { Multiaddr } from './index.ts' |
11 | | -import type { MultibaseCodec } from 'multiformats' |
12 | | -import type { SupportedEncodings } from 'uint8arrays/to-string' |
13 | | - |
14 | | -export function bytesToString (base: SupportedEncodings): (buf: Uint8Array) => string { |
15 | | - return (buf) => { |
16 | | - return uint8ArrayToString(buf, base) |
17 | | - } |
18 | | -} |
19 | | - |
20 | | -export function stringToBytes (base: SupportedEncodings): (value: string) => Uint8Array { |
21 | | - return (buf) => { |
22 | | - return uint8ArrayFromString(buf, base) |
23 | | - } |
24 | | -} |
25 | | - |
26 | | -export function bytes2port (buf: Uint8Array): string { |
27 | | - const view = new DataView(buf.buffer) |
28 | | - return view.getUint16(buf.byteOffset).toString() |
29 | | -} |
30 | | - |
31 | | -export function port2bytes (port: string | number): Uint8Array { |
32 | | - const buf = new ArrayBuffer(2) |
33 | | - const view = new DataView(buf) |
34 | | - view.setUint16(0, typeof port === 'string' ? parseInt(port) : port) |
35 | | - |
36 | | - return new Uint8Array(buf) |
37 | | -} |
38 | | - |
39 | | -export function onion2bytes (str: string): Uint8Array { |
40 | | - const addr = str.split(':') |
41 | | - |
42 | | - if (addr.length !== 2) { |
43 | | - throw new Error(`failed to parse onion addr: ["'${addr.join('", "')}'"]' does not contain a port number`) |
44 | | - } |
45 | | - |
46 | | - if (addr[0].length !== 16) { |
47 | | - throw new Error(`failed to parse onion addr: ${addr[0]} not a Tor onion address.`) |
48 | | - } |
49 | | - |
50 | | - // onion addresses do not include the multibase prefix, add it before decoding |
51 | | - const buf = uint8ArrayFromString(addr[0], 'base32') |
52 | | - |
53 | | - // onion port number |
54 | | - const port = parseInt(addr[1], 10) |
55 | | - |
56 | | - if (port < 1 || port > 65536) { |
57 | | - throw new Error('Port number is not in range(1, 65536)') |
58 | | - } |
59 | | - |
60 | | - const portBuf = port2bytes(port) |
61 | | - |
62 | | - return uint8ArrayConcat([buf, portBuf], buf.length + portBuf.length) |
63 | | -} |
64 | | - |
65 | | -export function onion32bytes (str: string): Uint8Array { |
66 | | - const addr = str.split(':') |
67 | | - |
68 | | - if (addr.length !== 2) { |
69 | | - throw new Error(`failed to parse onion addr: ["'${addr.join('", "')}'"]' does not contain a port number`) |
70 | | - } |
71 | | - |
72 | | - if (addr[0].length !== 56) { |
73 | | - throw new Error(`failed to parse onion addr: ${addr[0]} not a Tor onion3 address.`) |
74 | | - } |
75 | | - |
76 | | - // onion addresses do not include the multibase prefix, add it before decoding |
77 | | - const buf = base32.decode(`b${addr[0]}`) |
78 | | - |
79 | | - // onion port number |
80 | | - const port = parseInt(addr[1], 10) |
81 | | - |
82 | | - if (port < 1 || port > 65536) { |
83 | | - throw new Error('Port number is not in range(1, 65536)') |
84 | | - } |
85 | | - |
86 | | - const portBuf = port2bytes(port) |
87 | | - |
88 | | - return uint8ArrayConcat([buf, portBuf], buf.length + portBuf.length) |
89 | | -} |
90 | | - |
91 | | -export function bytes2onion (buf: Uint8Array): string { |
92 | | - const addrBytes = buf.subarray(0, buf.length - 2) |
93 | | - const portBytes = buf.subarray(buf.length - 2) |
94 | | - const addr = uint8ArrayToString(addrBytes, 'base32') |
95 | | - const port = bytes2port(portBytes) |
96 | | - return `${addr}:${port}` |
97 | | -} |
98 | | - |
99 | | -// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L7 |
100 | | -// but with buf/offset args removed because we don't use them |
101 | | -export const ip4ToBytes = function (ip: string): Uint8Array { |
102 | | - ip = ip.toString().trim() |
103 | | - |
104 | | - const bytes = new Uint8Array(4) |
105 | | - |
106 | | - ip.split(/\./g).forEach((byte, index) => { |
107 | | - const value = parseInt(byte, 10) |
108 | | - |
109 | | - if (isNaN(value) || value < 0 || value > 0xff) { |
110 | | - throw new InvalidMultiaddrError('Invalid byte value in IP address') |
111 | | - } |
112 | | - |
113 | | - bytes[index] = value |
114 | | - }) |
115 | | - |
116 | | - return bytes |
117 | | -} |
118 | | - |
119 | | -// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L7 |
120 | | -// but with buf/offset args removed because we don't use them |
121 | | -export const ip6ToBytes = function (ip: string): Uint8Array { |
122 | | - let offset = 0 |
123 | | - ip = ip.toString().trim() |
124 | | - |
125 | | - const sections = ip.split(':', 8) |
126 | | - |
127 | | - let i |
128 | | - for (i = 0; i < sections.length; i++) { |
129 | | - const isv4 = isIPv4(sections[i]) |
130 | | - let v4Buffer: Uint8Array | undefined |
131 | | - |
132 | | - if (isv4) { |
133 | | - v4Buffer = ip4ToBytes(sections[i]) |
134 | | - sections[i] = uint8ArrayToString(v4Buffer.subarray(0, 2), 'base16') |
135 | | - } |
136 | | - |
137 | | - if (v4Buffer != null && ++i < 8) { |
138 | | - sections.splice(i, 0, uint8ArrayToString(v4Buffer.subarray(2, 4), 'base16')) |
139 | | - } |
140 | | - } |
141 | | - |
142 | | - if (sections[0] === '') { |
143 | | - while (sections.length < 8) { sections.unshift('0') } |
144 | | - } else if (sections[sections.length - 1] === '') { |
145 | | - while (sections.length < 8) { sections.push('0') } |
146 | | - } else if (sections.length < 8) { |
147 | | - for (i = 0; i < sections.length && sections[i] !== ''; i++) { } |
148 | | - const argv: [number, number, ...string[]] = [i, 1] |
149 | | - for (i = 9 - sections.length; i > 0; i--) { |
150 | | - argv.push('0') |
151 | | - } |
152 | | - sections.splice.apply(sections, argv) |
153 | | - } |
154 | | - |
155 | | - const bytes = new Uint8Array(offset + 16) |
156 | | - |
157 | | - for (i = 0; i < sections.length; i++) { |
158 | | - if (sections[i] === '') { |
159 | | - sections[i] = '0' |
160 | | - } |
161 | | - |
162 | | - const word = parseInt(sections[i], 16) |
163 | | - |
164 | | - if (isNaN(word) || word < 0 || word > 0xffff) { |
165 | | - throw new InvalidMultiaddrError('Invalid byte value in IP address') |
166 | | - } |
167 | | - |
168 | | - bytes[offset++] = (word >> 8) & 0xff |
169 | | - bytes[offset++] = word & 0xff |
170 | | - } |
171 | | - |
172 | | - return bytes |
173 | | -} |
174 | | - |
175 | | -// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L63 |
176 | | -export const ip4ToString = function (buf: Uint8Array): string { |
177 | | - if (buf.byteLength !== 4) { |
178 | | - throw new InvalidMultiaddrError('IPv4 address was incorrect length') |
179 | | - } |
180 | | - |
181 | | - const result = [] |
182 | | - |
183 | | - for (let i = 0; i < buf.byteLength; i++) { |
184 | | - result.push(buf[i]) |
185 | | - } |
186 | | - |
187 | | - return result.join('.') |
188 | | -} |
189 | | - |
190 | | -export const ip6ToString = function (buf: Uint8Array): string { |
191 | | - if (buf.byteLength !== 16) { |
192 | | - throw new InvalidMultiaddrError('IPv6 address was incorrect length') |
193 | | - } |
194 | | - |
195 | | - const result: string[] = [] |
196 | | - |
197 | | - for (let i = 0; i < buf.byteLength; i += 2) { |
198 | | - const byte1 = buf[i] |
199 | | - const byte2 = buf[i + 1] |
200 | | - |
201 | | - const tuple = `${byte1.toString(16).padStart(2, '0')}${byte2.toString(16).padStart(2, '0')}` |
202 | | - |
203 | | - result.push(tuple) |
204 | | - } |
205 | | - |
206 | | - const ip = result.join(':') |
207 | | - |
208 | | - try { |
209 | | - const url = new URL(`http://[${ip}]`) |
210 | | - |
211 | | - return url.hostname.substring(1, url.hostname.length - 1) |
212 | | - } catch { |
213 | | - throw new InvalidMultiaddrError(`Invalid IPv6 address "${ip}"`) |
214 | | - } |
215 | | -} |
216 | | - |
217 | | -export function ip6StringToValue (str: string): string { |
218 | | - try { |
219 | | - const url = new URL(`http://[${str}]`) |
220 | | - |
221 | | - return url.hostname.substring(1, url.hostname.length - 1) |
222 | | - } catch { |
223 | | - throw new InvalidMultiaddrError(`Invalid IPv6 address "${str}"`) |
224 | | - } |
225 | | -} |
226 | | - |
227 | | -const decoders = Object.values(bases).map((c) => c.decoder) |
228 | | -const anybaseDecoder = (function () { |
229 | | - let acc = decoders[0].or(decoders[1]) |
230 | | - decoders.slice(2).forEach((d) => (acc = acc.or(d))) |
231 | | - return acc |
232 | | -})() |
233 | | - |
234 | | -export function mb2bytes (mbstr: string): Uint8Array { |
235 | | - return anybaseDecoder.decode(mbstr) |
236 | | -} |
237 | | - |
238 | | -export function bytes2mb (base: MultibaseCodec<any>): (buf: Uint8Array) => string { |
239 | | - return (buf) => { |
240 | | - return base.encoder.encode(buf) |
241 | | - } |
242 | | -} |
243 | 6 |
|
244 | 7 | export function convertToIpNet (multiaddr: Multiaddr): IpNet { |
245 | 8 | let mask: string | undefined |
|
0 commit comments