UNPKG

@multiformats/multiaddr

Version:

multiaddr implementation (binary + string representation of network addresses)

189 lines 6.82 kB
import { isIPv4 } from '@chainsafe/is-ip'; import { base32 } from 'multiformats/bases/base32'; import { bases } from 'multiformats/basics'; import { concat as uint8ArrayConcat } from 'uint8arrays/concat'; import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'; import { toString as uint8ArrayToString } from 'uint8arrays/to-string'; import { InvalidMultiaddrError } from "./errors.js"; export function bytesToString(base) { return (buf) => { return uint8ArrayToString(buf, base); }; } export function stringToBytes(base) { return (buf) => { return uint8ArrayFromString(buf, base); }; } export function bytes2port(buf) { const view = new DataView(buf.buffer); return view.getUint16(buf.byteOffset).toString(); } export function port2bytes(port) { const buf = new ArrayBuffer(2); const view = new DataView(buf); view.setUint16(0, typeof port === 'string' ? parseInt(port) : port); return new Uint8Array(buf); } export function onion2bytes(str) { const addr = str.split(':'); if (addr.length !== 2) { throw new Error(`failed to parse onion addr: ["'${addr.join('", "')}'"]' does not contain a port number`); } if (addr[0].length !== 16) { throw new Error(`failed to parse onion addr: ${addr[0]} not a Tor onion address.`); } // onion addresses do not include the multibase prefix, add it before decoding const buf = uint8ArrayFromString(addr[0], 'base32'); // onion port number const port = parseInt(addr[1], 10); if (port < 1 || port > 65536) { throw new Error('Port number is not in range(1, 65536)'); } const portBuf = port2bytes(port); return uint8ArrayConcat([buf, portBuf], buf.length + portBuf.length); } export function onion32bytes(str) { const addr = str.split(':'); if (addr.length !== 2) { throw new Error(`failed to parse onion addr: ["'${addr.join('", "')}'"]' does not contain a port number`); } if (addr[0].length !== 56) { throw new Error(`failed to parse onion addr: ${addr[0]} not a Tor onion3 address.`); } // onion addresses do not include the multibase prefix, add it before decoding const buf = base32.decode(`b${addr[0]}`); // onion port number const port = parseInt(addr[1], 10); if (port < 1 || port > 65536) { throw new Error('Port number is not in range(1, 65536)'); } const portBuf = port2bytes(port); return uint8ArrayConcat([buf, portBuf], buf.length + portBuf.length); } export function bytes2onion(buf) { const addrBytes = buf.subarray(0, buf.length - 2); const portBytes = buf.subarray(buf.length - 2); const addr = uint8ArrayToString(addrBytes, 'base32'); const port = bytes2port(portBytes); return `${addr}:${port}`; } // Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L7 // but with buf/offset args removed because we don't use them export const ip4ToBytes = function (ip) { ip = ip.toString().trim(); const bytes = new Uint8Array(4); ip.split(/\./g).forEach((byte, index) => { const value = parseInt(byte, 10); if (isNaN(value) || value < 0 || value > 0xff) { throw new InvalidMultiaddrError('Invalid byte value in IP address'); } bytes[index] = value; }); return bytes; }; // Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L7 // but with buf/offset args removed because we don't use them export const ip6ToBytes = function (ip) { let offset = 0; ip = ip.toString().trim(); const sections = ip.split(':', 8); let i; for (i = 0; i < sections.length; i++) { const isv4 = isIPv4(sections[i]); let v4Buffer; if (isv4) { v4Buffer = ip4ToBytes(sections[i]); sections[i] = uint8ArrayToString(v4Buffer.subarray(0, 2), 'base16'); } if (v4Buffer != null && ++i < 8) { sections.splice(i, 0, uint8ArrayToString(v4Buffer.subarray(2, 4), 'base16')); } } if (sections[0] === '') { while (sections.length < 8) { sections.unshift('0'); } } else if (sections[sections.length - 1] === '') { while (sections.length < 8) { sections.push('0'); } } else if (sections.length < 8) { for (i = 0; i < sections.length && sections[i] !== ''; i++) { } const argv = [i, 1]; for (i = 9 - sections.length; i > 0; i--) { argv.push('0'); } sections.splice.apply(sections, argv); } const bytes = new Uint8Array(offset + 16); for (i = 0; i < sections.length; i++) { if (sections[i] === '') { sections[i] = '0'; } const word = parseInt(sections[i], 16); if (isNaN(word) || word < 0 || word > 0xffff) { throw new InvalidMultiaddrError('Invalid byte value in IP address'); } bytes[offset++] = (word >> 8) & 0xff; bytes[offset++] = word & 0xff; } return bytes; }; // Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L63 export const ip4ToString = function (buf) { if (buf.byteLength !== 4) { throw new InvalidMultiaddrError('IPv4 address was incorrect length'); } const result = []; for (let i = 0; i < buf.byteLength; i++) { result.push(buf[i]); } return result.join('.'); }; export const ip6ToString = function (buf) { if (buf.byteLength !== 16) { throw new InvalidMultiaddrError('IPv6 address was incorrect length'); } const result = []; for (let i = 0; i < buf.byteLength; i += 2) { const byte1 = buf[i]; const byte2 = buf[i + 1]; const tuple = `${byte1.toString(16).padStart(2, '0')}${byte2.toString(16).padStart(2, '0')}`; result.push(tuple); } const ip = result.join(':'); try { const url = new URL(`http://[${ip}]`); return url.hostname.substring(1, url.hostname.length - 1); } catch { throw new InvalidMultiaddrError(`Invalid IPv6 address "${ip}"`); } }; export function ip6StringToValue(str) { try { const url = new URL(`http://[${str}]`); return url.hostname.substring(1, url.hostname.length - 1); } catch { throw new InvalidMultiaddrError(`Invalid IPv6 address "${str}"`); } } const decoders = Object.values(bases).map((c) => c.decoder); const anybaseDecoder = (function () { let acc = decoders[0].or(decoders[1]); decoders.slice(2).forEach((d) => (acc = acc.or(d))); return acc; })(); export function mb2bytes(mbstr) { return anybaseDecoder.decode(mbstr); } export function bytes2mb(base) { return (buf) => { return base.encoder.encode(buf); }; } //# sourceMappingURL=utils.js.map