Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 0 additions & 237 deletions src/convert.ts
Original file line number Diff line number Diff line change
@@ -1,245 +1,8 @@
import { isIPv4 } from '@chainsafe/is-ip'
import { IpNet } from '@chainsafe/netmask'
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.ts'
import { registry } from './registry.js'
import type { Multiaddr } from './index.ts'
import type { MultibaseCodec } from 'multiformats'
import type { SupportedEncodings } from 'uint8arrays/to-string'

export function bytesToString (base: SupportedEncodings): (buf: Uint8Array) => string {
return (buf) => {
return uint8ArrayToString(buf, base)
}
}

export function stringToBytes (base: SupportedEncodings): (value: string) => Uint8Array {
return (buf) => {
return uint8ArrayFromString(buf, base)
}
}

export function bytes2port (buf: Uint8Array): string {
const view = new DataView(buf.buffer)
return view.getUint16(buf.byteOffset).toString()
}

export function port2bytes (port: string | number): Uint8Array {
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: string): Uint8Array {
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: string): Uint8Array {
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: Uint8Array): string {
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: string): Uint8Array {
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: string): Uint8Array {
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: Uint8Array | undefined

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: [number, number, ...string[]] = [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: Uint8Array): string {
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: Uint8Array): string {
if (buf.byteLength !== 16) {
throw new InvalidMultiaddrError('IPv6 address was incorrect length')
}

const result: string[] = []

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: string): string {
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: string): Uint8Array {
return anybaseDecoder.decode(mbstr)
}

export function bytes2mb (base: MultibaseCodec<any>): (buf: Uint8Array) => string {
return (buf) => {
return base.encoder.encode(buf)
}
}

export function convertToIpNet (multiaddr: Multiaddr): IpNet {
let mask: string | undefined
Expand Down
2 changes: 1 addition & 1 deletion src/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { isIPv4, isIPv6 } from '@chainsafe/is-ip'
import { CID } from 'multiformats'
import { base64url } from 'multiformats/bases/base64'
import { CODE_CERTHASH, CODE_DCCP, CODE_DNS, CODE_DNS4, CODE_DNS6, CODE_DNSADDR, CODE_GARLIC32, CODE_GARLIC64, CODE_HTTP, CODE_HTTP_PATH, CODE_HTTPS, CODE_IP4, CODE_IP6, CODE_IP6ZONE, CODE_IPCIDR, CODE_MEMORY, CODE_NOISE, CODE_ONION, CODE_ONION3, CODE_P2P, CODE_P2P_CIRCUIT, CODE_P2P_STARDUST, CODE_P2P_WEBRTC_DIRECT, CODE_P2P_WEBRTC_STAR, CODE_P2P_WEBSOCKET_STAR, CODE_QUIC, CODE_QUIC_V1, CODE_SCTP, CODE_SNI, CODE_TCP, CODE_TLS, CODE_UDP, CODE_UDT, CODE_UNIX, CODE_UTP, CODE_WEBRTC, CODE_WEBRTC_DIRECT, CODE_WEBTRANSPORT, CODE_WS, CODE_WSS } from './constants.ts'
import { bytes2mb, bytes2onion, bytes2port, bytesToString, ip4ToBytes, ip4ToString, ip6StringToValue, ip6ToBytes, ip6ToString, mb2bytes, onion2bytes, onion32bytes, port2bytes, stringToBytes } from './convert.ts'
import { InvalidProtocolError, ValidationError } from './errors.ts'
import { bytes2mb, bytes2onion, bytes2port, bytesToString, ip4ToBytes, ip4ToString, ip6StringToValue, ip6ToBytes, ip6ToString, mb2bytes, onion2bytes, onion32bytes, port2bytes, stringToBytes } from './utils.ts'
import { validatePort } from './validation.ts'

export const V = -1
Expand Down
Loading