diff --git a/browser/wsproxy.js b/browser/wsproxy.js index ec99ce91..8d25738c 100644 --- a/browser/wsproxy.js +++ b/browser/wsproxy.js @@ -5,7 +5,7 @@ const net = require('net'); const EventEmitter = require('events'); const bsock = require('bsock'); const digest = require('bcrypto/lib/digest'); -const IP = require('../lib/utils/ip'); +const IP = require('binet'); const BufferWriter = require('bbuf/lib/writer'); const TARGET = Buffer.from( diff --git a/lib/net/bip150.js b/lib/net/bip150.js index ac2f5d57..389dc661 100644 --- a/lib/net/bip150.js +++ b/lib/net/bip150.js @@ -21,7 +21,7 @@ const secp256k1 = require('bcrypto/lib/secp256k1'); const StaticWriter = require('bbuf/lib/staticwriter'); const base58 = require('bstr/lib/base58'); const encoding = require('bbuf/lib/encoding'); -const IP = require('../utils/ip'); +const IP = require('binet'); const dns = require('./dns'); const Logger = require('../node/logger'); diff --git a/lib/net/external.js b/lib/net/external.js index af2b084b..c104c95b 100644 --- a/lib/net/external.js +++ b/lib/net/external.js @@ -7,7 +7,7 @@ 'use strict'; const breq = require('breq'); -const IP = require('../utils/ip'); +const IP = require('binet'); /** * @exports net/external diff --git a/lib/net/hostlist.js b/lib/net/hostlist.js index 4677bae7..243ca814 100644 --- a/lib/net/hostlist.js +++ b/lib/net/hostlist.js @@ -11,7 +11,7 @@ const path = require('path'); const fs = require('bfile'); const murmur3 = require('bfilter/lib/murmur3'); const util = require('../utils/util'); -const IP = require('../utils/ip'); +const IP = require('binet'); const co = require('../utils/co'); const Network = require('../protocol/network'); const NetAddress = require('../primitives/netaddress'); diff --git a/lib/net/pool.js b/lib/net/pool.js index 6c27acef..8a8639c9 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -11,7 +11,7 @@ const assert = require('assert'); const EventEmitter = require('events'); const AsyncObject = require('../utils/asyncobject'); const util = require('../utils/util'); -const IP = require('../utils/ip'); +const IP = require('binet'); const co = require('../utils/co'); const common = require('./common'); const chainCommon = require('../blockchain/common'); diff --git a/lib/net/socks.js b/lib/net/socks.js index 3812d853..80fa5c19 100644 --- a/lib/net/socks.js +++ b/lib/net/socks.js @@ -14,7 +14,7 @@ const assert = require('assert'); const EventEmitter = require('events'); const net = require('net'); const {format} = require('util'); -const IP = require('../utils/ip'); +const IP = require('binet'); const StaticWriter = require('bbuf/lib/staticwriter'); const BufferReader = require('bbuf/lib/reader'); diff --git a/lib/net/upnp.js b/lib/net/upnp.js index 72993b7a..edad311e 100644 --- a/lib/net/upnp.js +++ b/lib/net/upnp.js @@ -12,7 +12,7 @@ const url = require('url'); const breq = require('breq'); const co = require('../utils/co'); const Lock = require('../utils/lock'); -const IP = require('../utils/ip'); +const IP = require('binet'); /** * UPNP diff --git a/lib/node/rpc.js b/lib/node/rpc.js index 138cc32a..a9076eda 100644 --- a/lib/node/rpc.js +++ b/lib/node/rpc.js @@ -28,7 +28,7 @@ const Network = require('../protocol/network'); const Outpoint = require('../primitives/outpoint'); const Output = require('../primitives/output'); const TX = require('../primitives/tx'); -const IP = require('../utils/ip'); +const IP = require('binet'); const encoding = require('bbuf/lib/encoding'); const consensus = require('../protocol/consensus'); const Validator = require('../utils/validator'); diff --git a/lib/primitives/netaddress.js b/lib/primitives/netaddress.js index 072e8b60..8f26421e 100644 --- a/lib/primitives/netaddress.js +++ b/lib/primitives/netaddress.js @@ -10,7 +10,7 @@ const assert = require('assert'); const common = require('../net/common'); const Network = require('../protocol/network'); const util = require('../utils/util'); -const IP = require('../utils/ip'); +const IP = require('binet'); const StaticWriter = require('bbuf/lib/staticwriter'); const BufferReader = require('bbuf/lib/reader'); diff --git a/lib/utils/base32.js b/lib/utils/base32.js deleted file mode 100644 index 94a58150..00000000 --- a/lib/utils/base32.js +++ /dev/null @@ -1,191 +0,0 @@ -/*! - * base32.js - base32 for bcoin - * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). - * https://github.com/bcoin-org/bcoin - */ - -'use strict'; - -/** - * @module utils/base32 - */ - -const assert = require('assert'); -const base32 = 'abcdefghijklmnopqrstuvwxyz234567'; -const padding = [0, 6, 4, 3, 1]; -const unbase32 = {}; - -for (let i = 0; i < base32.length; i++) - unbase32[base32[i]] = i; - -/** - * Encode a base32 string. - * @param {Buffer} data - * @returns {String} - */ - -exports.encode = function encode(data) { - assert(Buffer.isBuffer(data)); - - let str = ''; - let mode = 0; - let left = 0; - - for (let i = 0; i < data.length; i++) { - const ch = data[i]; - switch (mode) { - case 0: - str += base32[ch >>> 3]; - left = (ch & 7) << 2; - mode = 1; - break; - case 1: - str += base32[left | (ch >>> 6)]; - str += base32[(ch >>> 1) & 31]; - left = (ch & 1) << 4; - mode = 2; - break; - case 2: - str += base32[left | (ch >>> 4)]; - left = (ch & 15) << 1; - mode = 3; - break; - case 3: - str += base32[left | (ch >>> 7)]; - str += base32[(ch >>> 2) & 31]; - left = (ch & 3) << 3; - mode = 4; - break; - case 4: - str += base32[left | (ch >>> 5)]; - str += base32[ch & 31]; - mode = 0; - break; - } - } - - if (mode > 0) { - str += base32[left]; - for (let i = 0; i < padding[mode]; i++) - str += '='; - } - - return str; -}; - -/** - * Decode a base32 string. - * @param {String} str - * @returns {Buffer} - */ - -exports.decode = function decode(str) { - assert(typeof str === 'string'); - - const data = Buffer.allocUnsafe(str.length * 5 / 8 | 0); - let mode = 0; - let left = 0; - let j = 0; - let i; - - for (i = 0; i < str.length; i++) { - const ch = unbase32[str[i]]; - - if (ch == null) - break; - - switch (mode) { - case 0: - left = ch; - mode = 1; - break; - case 1: - data[j++] = (left << 3) | (ch >>> 2); - left = ch & 3; - mode = 2; - break; - case 2: - left = left << 5 | ch; - mode = 3; - break; - case 3: - data[j++] = (left << 1) | (ch >>> 4); - left = ch & 15; - mode = 4; - break; - case 4: - data[j++] = (left << 4) | (ch >>> 1); - left = ch & 1; - mode = 5; - break; - case 5: - left = left << 5 | ch; - mode = 6; - break; - case 6: - data[j++] = (left << 2) | (ch >>> 3); - left = ch & 7; - mode = 7; - break; - case 7: - data[j++] = (left << 5) | ch; - mode = 0; - break; - } - } - - switch (mode) { - case 0: - break; - case 1: - case 3: - case 6: - throw new Error('Invalid base32 string.'); - case 2: - if (left > 0) - throw new Error('Invalid padding.'); - - if (str.slice(i, i + 6) !== '======') - throw new Error('Invalid base32 character.'); - - if (unbase32[str[i + 6]] != null) - throw new Error('Invalid padding.'); - - break; - case 4: - if (left > 0) - throw new Error('Invalid padding.'); - - if (str.slice(i, i + 4) !== '====') - throw new Error('Invalid base32 character.'); - - if (unbase32[str[i + 4]] != null) - throw new Error('Invalid padding.'); - - break; - case 5: - if (left > 0) - throw new Error('Invalid padding.'); - - if (str.slice(i, i + 3) !== '===') - throw new Error('Invalid base32 character.'); - - if (unbase32[str[i + 3]] != null) - throw new Error('Invalid padding.'); - - break; - case 7: - if (left > 0) - throw new Error('Invalid padding.'); - - if (str[i] !== '=') - throw new Error('Invalid base32 character.'); - - if (unbase32[str[i + 1]] != null) - throw new Error('Invalid padding.'); - - break; - } - - return data.slice(0, j); -}; diff --git a/lib/utils/hashwriter.js b/lib/utils/hashwriter.js deleted file mode 100644 index 6229bfae..00000000 --- a/lib/utils/hashwriter.js +++ /dev/null @@ -1,500 +0,0 @@ -/*! - * staticwriter.js - buffer writer for bcoin - * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). - * https://github.com/bcoin-org/bcoin - */ - -'use strict'; - -const assert = require('assert'); -const encoding = require('bbuf/lib/encoding'); - -const POOL0 = Buffer.allocUnsafe(0); -const POOL8 = Buffer.allocUnsafe(1); -const POOL16 = Buffer.allocUnsafe(2); -const POOL24 = Buffer.allocUnsafe(3); -const POOL32 = Buffer.allocUnsafe(4); -const POOL40 = Buffer.allocUnsafe(5); -const POOL48 = Buffer.allocUnsafe(6); -const POOL56 = Buffer.allocUnsafe(7); -const POOL64 = Buffer.allocUnsafe(8); -const POOL72 = Buffer.allocUnsafe(9); -const POOL256 = Buffer.allocUnsafe(32); - -const poolBySize = [ - POOL0, - POOL8, - POOL16, - POOL24, - POOL32, - POOL40, - POOL48, - POOL56, - POOL64, - POOL72 -]; - -/** - * Statically allocated buffer writer. - * @alias module:utils.HashWriter - * @constructor - * @param {Number} size - */ - -function HashWriter(Hash, ...args) { - if (!(this instanceof HashWriter)) - return new HashWriter(Hash, ...args); - - this.ctx = new Hash().init(...args); -} - -/** - * Allocate and render the final buffer. - * @returns {Buffer} Rendered buffer. - */ - -HashWriter.prototype.init = function init(...args) { - return this.ctx.init(...args); -}; - -/** - * Allocate and render the final buffer. - * @returns {Buffer} Rendered buffer. - */ - -HashWriter.prototype.update = function update(data) { - return this.ctx.update(data); -}; - -/** - * Allocate and render the final buffer. - * @returns {Buffer} Rendered buffer. - */ - -HashWriter.prototype.final = function final() { - return this.ctx.final(); -}; - -/** - * Get size of data written so far. - * @returns {Number} - */ - -HashWriter.prototype.getSize = function getSize() { - assert(false); -}; - -/** - * Seek to relative offset. - * @param {Number} offset - */ - -HashWriter.prototype.seek = function seek(offset) { - assert(false); -}; - -/** - * Destroy the buffer writer. - */ - -HashWriter.prototype.destroy = function destroy() { - assert(false); -}; - -/** - * Write uint8. - * @param {Number} value - */ - -HashWriter.prototype.writeU8 = function writeU8(value) { - POOL8.writeUInt8(value, 0, true); - this.ctx.update(POOL8); -}; - -/** - * Write uint16le. - * @param {Number} value - */ - -HashWriter.prototype.writeU16 = function writeU16(value) { - POOL16.writeUInt16LE(value, 0, true); - this.ctx.update(POOL16); -}; - -/** - * Write uint16be. - * @param {Number} value - */ - -HashWriter.prototype.writeU16BE = function writeU16BE(value) { - POOL16.writeUInt16BE(value, 0, true); - this.ctx.update(POOL16); -}; - -/** - * Write uint32le. - * @param {Number} value - */ - -HashWriter.prototype.writeU32 = function writeU32(value) { - POOL32.writeUInt32LE(value, 0, true); - this.ctx.update(POOL32); -}; - -/** - * Write uint32be. - * @param {Number} value - */ - -HashWriter.prototype.writeU32BE = function writeU32BE(value) { - POOL32.writeUInt32BE(value, 0, true); - this.ctx.update(POOL32); -}; - -/** - * Write uint64le. - * @param {Number} value - */ - -HashWriter.prototype.writeU64 = function writeU64(value) { - encoding.writeU64(POOL64, value, 0); - this.ctx.update(POOL64); -}; - -/** - * Write uint64be. - * @param {Number} value - */ - -HashWriter.prototype.writeU64BE = function writeU64BE(value) { - encoding.writeU64BE(POOL64, value, 0); - this.ctx.update(POOL64); -}; - -/** - * Write uint64le. - * @param {U64} value - */ - -HashWriter.prototype.writeU64N = function writeU64N(value) { - encoding.writeU64N(POOL64, value, 0); - this.ctx.update(POOL64); -}; - -/** - * Write uint64be. - * @param {U64} value - */ - -HashWriter.prototype.writeU64BEN = function writeU64BEN(value) { - encoding.writeU64BEN(POOL64, value, 0); - this.ctx.update(POOL64); -}; - -/** - * Write int8. - * @param {Number} value - */ - -HashWriter.prototype.writeI8 = function writeI8(value) { - POOL8.writeInt8(value, 0, true); - this.ctx.update(POOL8); -}; - -/** - * Write int16le. - * @param {Number} value - */ - -HashWriter.prototype.writeI16 = function writeI16(value) { - POOL16.writeInt16LE(value, 0, true); - this.ctx.update(POOL16); -}; - -/** - * Write int16be. - * @param {Number} value - */ - -HashWriter.prototype.writeI16BE = function writeI16BE(value) { - POOL16.writeInt16BE(value, 0, true); - this.ctx.update(POOL16); -}; - -/** - * Write int32le. - * @param {Number} value - */ - -HashWriter.prototype.writeI32 = function writeI32(value) { - POOL32.writeInt32LE(value, 0, true); - this.ctx.update(POOL32); -}; - -/** - * Write int32be. - * @param {Number} value - */ - -HashWriter.prototype.writeI32BE = function writeI32BE(value) { - POOL32.writeInt32BE(value, 0, true); - this.ctx.update(POOL32); -}; - -/** - * Write int64le. - * @param {Number} value - */ - -HashWriter.prototype.writeI64 = function writeI64(value) { - encoding.writeI64(POOL64, value, 0); - this.ctx.update(POOL64); -}; - -/** - * Write int64be. - * @param {Number} value - */ - -HashWriter.prototype.writeI64BE = function writeI64BE(value) { - encoding.writeI64BE(POOL64, value, 0); - this.ctx.update(POOL64); -}; - -/** - * Write int64le. - * @param {I64} value - */ - -HashWriter.prototype.writeI64N = function writeI64N(value) { - encoding.writeI64N(POOL64, value, 0); - this.ctx.update(POOL64); -}; - -/** - * Write int64be. - * @param {I64} value - */ - -HashWriter.prototype.writeI64BEN = function writeI64BEN(value) { - encoding.writeI64BEN(POOL64, value, 0); - this.ctx.update(POOL64); -}; - -/** - * Write float le. - * @param {Number} value - */ - -HashWriter.prototype.writeFloat = function writeFloat(value) { - POOL32.writeFloatLE(value, 0, true); - this.ctx.update(POOL32); -}; - -/** - * Write float be. - * @param {Number} value - */ - -HashWriter.prototype.writeFloatBE = function writeFloatBE(value) { - POOL32.writeFloatBE(value, 0, true); - this.ctx.update(POOL32); -}; - -/** - * Write double le. - * @param {Number} value - */ - -HashWriter.prototype.writeDouble = function writeDouble(value) { - POOL64.writeDoubleLE(value, 0, true); - this.ctx.update(POOL64); -}; - -/** - * Write double be. - * @param {Number} value - */ - -HashWriter.prototype.writeDoubleBE = function writeDoubleBE(value) { - POOL64.writeDoubleBE(value, 0, true); - this.ctx.update(POOL64); -}; - -/** - * Write a varint. - * @param {Number} value - */ - -HashWriter.prototype.writeVarint = function writeVarint(value) { - const size = encoding.sizeVarint(value); - const pool = poolBySize[size]; - encoding.writeVarint(pool, value, 0); - this.ctx.update(pool); -}; - -/** - * Write a varint. - * @param {U64} value - */ - -HashWriter.prototype.writeVarintN = function writeVarintN(value) { - const size = encoding.sizeVarintN(value); - const pool = poolBySize[size]; - encoding.writeVarintN(pool, value, 0); - this.ctx.update(pool); -}; - -/** - * Write a varint (type 2). - * @param {Number} value - */ - -HashWriter.prototype.writeVarint2 = function writeVarint2(value) { - const size = encoding.sizeVarint2(value); - const pool = poolBySize[size]; - encoding.writeVarint2(pool, value, 0); - this.ctx.update(pool); -}; - -/** - * Write a varint (type 2). - * @param {U64} value - */ - -HashWriter.prototype.writeVarint2N = function writeVarint2N(value) { - const size = encoding.sizeVarint2N(value); - const pool = poolBySize[size]; - encoding.writeVarint2N(pool, value, 0); - this.ctx.update(pool); -}; - -/** - * Write bytes. - * @param {Buffer} value - */ - -HashWriter.prototype.writeBytes = function writeBytes(value) { - this.ctx.update(value); -}; - -/** - * Write bytes with a varint length before them. - * @param {Buffer} value - */ - -HashWriter.prototype.writeVarBytes = function writeVarBytes(value) { - this.writeVarint(value.length); - this.writeBytes(value); -}; - -/** - * Copy bytes. - * @param {Buffer} value - * @param {Number} start - * @param {Number} end - */ - -HashWriter.prototype.copy = function copy(value, start, end) { - this.ctx.update(value.slice(start, end)); -}; - -/** - * Write string to buffer. - * @param {String} value - * @param {String?} enc - Any buffer-supported encoding. - */ - -HashWriter.prototype.writeString = function writeString(value, enc) { - if (value.length === 0) - return; - - if (typeof value === 'string') - value = Buffer.from(value, enc); - - this.ctx.update(value); -}; - -/** - * Write a 32 byte hash. - * @param {Hash} value - */ - -HashWriter.prototype.writeHash = function writeHash(value) { - if (typeof value !== 'string') { - assert(value.length === 32); - this.writeBytes(value); - return; - } - assert(value.length === 64); - POOL256.write(value, 0, 'hex'); - this.ctx.update(POOL256); -}; - -/** - * Write a string with a varint length before it. - * @param {String} - * @param {String?} enc - Any buffer-supported encoding. - */ - -HashWriter.prototype.writeVarString = function writeVarString(value, enc) { - if (value.length === 0) { - this.writeVarint(0); - return; - } - - const size = Buffer.byteLength(value, enc); - - this.writeVarint(size); - this.ctx.update(value, enc); -}; - -/** - * Write a null-terminated string. - * @param {String|Buffer} - * @param {String?} enc - Any buffer-supported encoding. - */ - -HashWriter.prototype.writeNullString = function writeNullString(value, enc) { - this.writeString(value, enc); - this.writeU8(0); -}; - -/** - * Calculate and write a checksum for the data written so far. - */ - -HashWriter.prototype.writeChecksum = function writeChecksum() { - assert(false); -}; - -/** - * Fill N bytes with value. - * @param {Number} value - * @param {Number} size - */ - -HashWriter.prototype.fill = function fill(value, size) { - assert(size >= 0); - - if (size === 0) - return; - - if (size <= 32) { - const data = POOL256.slice(0, size); - data.fill(value); - this.ctx.update(data); - return; - } - - const data = Buffer.allocUnsafe(size); - data.fill(value); - - this.ctx.update(data); -}; - -/* - * Expose - */ - -module.exports = HashWriter; diff --git a/lib/utils/index.js b/lib/utils/index.js index 6c765545..e561c415 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -12,13 +12,11 @@ exports.AsyncEmitter = require('./asyncemitter'); exports.AsyncObject = require('./asyncobject'); -exports.base32 = require('./base32'); exports.binary = require('./binary'); exports.co = require('./co'); exports.enforce = require('./enforce'); exports.fixed = require('./fixed'); exports.Heap = require('./heap'); -exports.IP = require('./ip'); exports.List = require('./list'); exports.Lock = require('./lock'); exports.LRU = require('./lru'); diff --git a/lib/utils/int64.js b/lib/utils/int64.js deleted file mode 100644 index 2cf24b0d..00000000 --- a/lib/utils/int64.js +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * int64.js - int64s for bcoin - * Copyright (c) 2017, Christopher Jeffrey (MIT License). - * https://github.com/bcoin-org/bcoin - */ - -'use strict'; - -module.exports = require('n64'); diff --git a/lib/utils/ip.js b/lib/utils/ip.js deleted file mode 100644 index dc84b915..00000000 --- a/lib/utils/ip.js +++ /dev/null @@ -1,1067 +0,0 @@ -/*! - * ip.js - ip utils for bcoin - * Copyright (c) 2014-2015, Fedor Indutny (MIT License). - * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). - * https://github.com/bcoin-org/bcoin - * - * Parts of this software are based on node-ip. - * https://github.com/indutny/node-ip - * Copyright (c) 2012, Fedor Indutny (MIT License). - */ - -/* eslint no-unreachable: "off" */ - -'use strict'; - -const assert = require('assert'); -const os = require('os'); -const base32 = require('./base32'); - -/** - * @exports utils/ip - */ - -const IP = exports; - -/* - * Constants - */ - -const ZERO_IP = Buffer.from('00000000000000000000000000000000', 'hex'); -const LOCAL_IP = Buffer.from('00000000000000000000000000000001', 'hex'); -const RFC6052 = Buffer.from('0064ff9b0000000000000000', 'hex'); -const RFC4862 = Buffer.from('fe80000000000000', 'hex'); -const RFC6145 = Buffer.from('0000000000000000ffff0000', 'hex'); -const TOR_ONION = Buffer.from('fd87d87eeb43', 'hex'); -const SHIFTED = Buffer.from('00000000000000ffff', 'hex'); - -const IPV4_REGEX = /^(\d{1,3}\.){3}\d{1,3}$/; -const IPV6_REGEX = - /^(::)?(((\d{1,3}\.){3}(\d{1,3}){1})?([0-9a-f]){0,4}:{0,2}){1,8}(::)?$/i; - -/** - * IP address of all zeroes. - * @const {Buffer} - */ - -IP.ZERO_IP = ZERO_IP; - -/** - * Address types. - * @enum {Number} - */ - -IP.types = { - DNS: -1, - IPV4: 4, - IPV6: 6, - ONION: 10, - TEREDO: 11 -}; - -/** - * Parse a hostname. - * @param {String} addr - * @param {Number?} fallback - Fallback port. - * @returns {Object} Contains `host`, `port`, and `type`. - */ - -IP.fromHostname = function fromHostname(addr, fallback) { - assert(typeof addr === 'string'); - assert(addr.length > 0, 'Bad address.'); - - let host, port; - if (addr[0] === '[') { - if (addr[addr.length - 1] === ']') { - // Case: - // [::1] - host = addr.slice(1, -1); - port = null; - } else { - // Case: - // [::1]:80 - addr = addr.slice(1); - const parts = addr.split(']:'); - assert(parts.length === 2, 'Bad IPv6 address.'); - host = parts[0]; - port = parts[1]; - } - } else { - const parts = addr.split(':'); - switch (parts.length) { - case 2: - // Cases: - // 127.0.0.1:80 - // localhost:80 - host = parts[0]; - port = parts[1]; - break; - case 1: - // Cases: - // 127.0.0.1 - // localhost - host = parts[0]; - port = null; - break; - default: - // Case: - // ::1 - assert(IP.isV6String(addr), 'Bad IPv6 address.'); - host = addr; - port = null; - break; - } - } - - assert(host.length > 0, 'Bad host.'); - - if (port != null) { - assert(port.length <= 5, 'Bad port.'); - assert(/^\d+$/.test(port), 'Bad port.'); - port = parseInt(port, 10); - assert(port <= 0xffff); - } else { - port = fallback || 0; - } - - const type = IP.getStringType(host); - - let raw; - if (type !== IP.types.DNS) { - raw = IP.toBuffer(host); - host = IP.toString(raw); - } - - let hostname; - if (type === IP.types.IPV6) - hostname = `[${host}]:${port}`; - else - hostname = `${host}:${port}`; - - return new Address(host, port, type, hostname, raw); -}; - -/** - * Concatenate a host and port. - * @param {String} host - * @param {Number} port - * @returns {String} - */ - -IP.toHostname = function toHostname(host, port) { - assert(typeof host === 'string'); - assert(host.length > 0); - assert(typeof port === 'number'); - assert(port >= 0 && port <= 0xffff); - - assert(!/[\[\]]/.test(host), 'Bad host.'); - - const type = IP.getStringType(host); - - if (host.indexOf(':') !== -1) - assert(type === IP.types.IPV6, 'Bad host.'); - - if (type !== IP.types.DNS) - host = IP.normalize(host); - - if (type === IP.types.IPV6) - return `[${host}]:${port}`; - - return `${host}:${port}`; -}; - -/** - * Get address type (-1=dns, 4=ipv4, 6=ipv6, 10=tor). - * @param {String?} str - * @returns {Number} - */ - -IP.getStringType = function getStringType(str) { - if (IP.isV4String(str)) - return IP.types.IPV4; - - if (IP.isV6String(str)) - return IP.types.IPV6; - - if (IP.isOnionString(str)) - return IP.types.ONION; - - return IP.types.DNS; -}; - -/** - * Test whether a string is IPv4. - * @param {String?} str - * @returns {Boolean} - */ - -IP.isV4String = function isV4String(str) { - assert(typeof str === 'string'); - - if (str.length < 7) - return false; - - if (str.length > 15) - return false; - - return IPV4_REGEX.test(str); -}; - -/** - * Test whether a string is IPv6. - * @param {String?} str - * @returns {Boolean} - */ - -IP.isV6String = function isV6String(str) { - assert(typeof str === 'string'); - - if (str.length < 2) - return false; - - if (str.length > 39) - return false; - - return IPV6_REGEX.test(str); -}; - -/** - * Test whether a string is an onion address. - * @param {String?} str - * @returns {Boolean} - */ - -IP.isOnionString = function isOnionString(str) { - assert(typeof str === 'string'); - - if (str.length < 7) - return false; - - return str.slice(-6) === '.onion'; -}; - -/** - * Test whether a buffer is an ipv4-mapped ipv6 address. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isMapped = function isMapped(raw) { - assert(Buffer.isBuffer(raw)); - assert(raw.length === 16); - - return raw[0] === 0x00 - && raw[1] === 0x00 - && raw[2] === 0x00 - && raw[3] === 0x00 - && raw[4] === 0x00 - && raw[5] === 0x00 - && raw[6] === 0x00 - && raw[7] === 0x00 - && raw[8] === 0x00 - && raw[9] === 0x00 - && raw[10] === 0xff - && raw[11] === 0xff; -}; - -/** - * Parse an IP string and return a buffer. - * @param {String} str - * @returns {Buffer} - */ - -IP.toBuffer = function toBuffer(str) { - assert(typeof str === 'string'); - - const raw = Buffer.allocUnsafe(16); - - if (IP.isV4String(str)) { - raw.fill(0); - raw[10] = 0xff; - raw[11] = 0xff; - return IP.parseV4(str, raw, 12); - } - - if (IP.isOnionString(str)) { - const prefix = TOR_ONION; - prefix.copy(raw, 0); - const data = base32.decode(str.slice(0, -6)); - assert(data.length === 10, 'Invalid onion address.'); - data.copy(raw, 6); - return raw; - } - - return IP.parseV6(str, raw, 0); -}; - -/** - * Convert an IPv4 string to a buffer. - * @private - * @param {String} str - * @param {Buffer} raw - * @param {Number} offset - * @returns {Buffer} - */ - -IP.parseV4 = function parseV4(str, raw, offset) { - const parts = str.split('.'); - - assert(parts.length === 4); - - for (let ch of parts) { - assert(ch.length > 0); - assert(ch.length <= 3); - ch = parseInt(ch, 10); - assert(ch >= 0 && ch <= 255); - raw[offset++] = ch; - } - - return raw; -}; - -/** - * Convert an IPv6 string to a buffer. - * @private - * @param {String} str - * @param {Buffer} raw - * @param {Number} offset - * @returns {Buffer} - */ - -IP.parseV6 = function parseV6(str, raw, offset) { - const parts = str.split(':'); - let missing = 8 - parts.length; - - assert(parts.length >= 2, 'Not an IPv6 address.'); - - for (const word of parts) { - if (IP.isV4String(word)) - missing--; - } - - const start = offset; - let colon = false; - - for (let i = 0; i < parts.length; i++) { - let word = parts[i]; - - if (word.length === 0) { - assert(!colon, 'Overuse of double colon in IPv6 address.'); - - colon = true; - missing += 1; - - // Eat extra colons. - // e.g. :::0 - while (i + 1 < parts.length) { - word = parts[i + 1]; - if (word.length !== 0) - break; - missing += 1; - i++; - } - - while (missing > 0) { - raw[offset++] = 0; - raw[offset++] = 0; - missing--; - } - - continue; - } - - if (IP.isV4String(word)) { - IP.parseV4(word, raw, offset); - offset += 4; - continue; - } - - assert(word.length <= 4); - - word = parseInt(word, 16); - - assert(word === word, 'Non-number in IPv6 address.'); - - raw[offset++] = (word >> 8) & 0xff; - raw[offset++] = word & 0xff; - } - - assert(missing === 0, 'IPv6 address has missing sections.'); - assert.strictEqual(offset, start + 16); - - return raw; -}; - -/** - * Convert a buffer to an ip string. - * @param {Buffer} raw - * @returns {String} - */ - -IP.toString = function toString(raw) { - assert(Buffer.isBuffer(raw)); - - if (raw.length === 4) { - let host = ''; - host += raw[0]; - host += '.' + raw[1]; - host += '.' + raw[2]; - host += '.' + raw[3]; - return host; - } - - if (raw.length === 16) { - if (IP.isMapped(raw)) { - let host = ''; - host += raw[12]; - host += '.' + raw[13]; - host += '.' + raw[14]; - host += '.' + raw[15]; - return host; - } - - if (IP.isOnion(raw)) { - const host = base32.encode(raw.slice(6)); - return `${host}.onion`; - } - - let host = ''; - - host += raw.readUInt16BE(0, true).toString(16); - - for (let i = 2; i < 16; i += 2) { - host += ':'; - host += raw.readUInt16BE(i, true).toString(16); - } - - host = host.replace(/(^|:)0(:0)*:0(:|$)/, '$1::$3'); - host = host.replace(/:{3,4}/, '::'); - - return host; - } - - throw new Error(`Invalid IP address: ${raw.toString('hex')}.`); -}; - -/** - * Normalize an ip. - * @param {String} str - * @returns {String} - */ - -IP.normalize = function normalize(str) { - return IP.toString(IP.toBuffer(str)); -}; - -/** - * Test whether the address is IPv4. - * @returns {Boolean} - */ - -IP.isIPv4 = function isIPv4(raw) { - return IP.isMapped(raw); -}; - -/** - * Test whether the address is IPv6. - * @returns {Boolean} - */ - -IP.isIPv6 = function isIPv6(raw) { - return !IP.isMapped(raw) && !IP.isOnion(raw); -}; - -/** - * Get address type. - * @param {Buffer} raw - * @returns {Number} - */ - -IP.getType = function getType(raw) { - if (IP.isIPv4(raw)) - return IP.types.IPV4; - - if (IP.isIPv6(raw)) - return IP.types.IPV6; - - if (IP.isOnion(raw)) - return IP.types.ONION; - - throw new Error('Unknown type.'); -}; - -/** - * Get addr network. Similar to - * type, but includes teredo. - * @param {Buffer} raw - * @returns {Number} - */ - -IP.getNetwork = function getNetwork(raw) { - if (IP.isRFC4380(raw)) - return IP.types.TEREDO; - - return IP.getType(raw); -}; - -/** - * Test whether the host is null. - * @returns {Boolean} - */ - -IP.isNull = function isNull(raw) { - if (IP.isIPv4(raw)) { - // 0.0.0.0 - return raw[12] === 0 - && raw[13] === 0 - && raw[14] === 0 - && raw[15] === 0; - } - // :: - return IP.isEqual(raw, ZERO_IP); -}; - -/** - * Test whether the host is a broadcast address. - * @returns {Boolean} - */ - -IP.isBroadcast = function isBroadcast(raw) { - if (IP.isIPv4(raw)) { - // 255.255.255.255 - return raw[12] === 255 - && raw[13] === 255 - && raw[14] === 255 - && raw[15] === 255; - } - return false; -}; - -/** - * Test whether the ip is RFC 1918. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isRFC1918 = function isRFC1918(raw) { - if (!IP.isIPv4(raw)) - return false; - - if (raw[12] === 10) - return true; - - if (raw[12] === 192 && raw[13] === 168) - return true; - - if (raw[12] === 172 && (raw[13] >= 16 && raw[13] <= 31)) - return true; - - return false; -}; - -/** - * Test whether the ip is RFC 2544. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isRFC2544 = function isRFC2544(raw) { - if (!IP.isIPv4(raw)) - return false; - - if (raw[12] === 198 && (raw[13] === 18 || raw[13] === 19)) - return true; - - if (raw[12] === 169 && raw[13] === 254) - return true; - - return false; -}; - -/** - * Test whether the ip is RFC 3927. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isRFC3927 = function isRFC3927(raw) { - if (!IP.isIPv4(raw)) - return false; - - if (raw[12] === 169 && raw[13] === 254) - return true; - - return false; -}; - -/** - * Test whether the ip is RFC 6598. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isRFC6598 = function isRFC6598(raw) { - if (!IP.isIPv4(raw)) - return false; - - if (raw[12] === 100 - && (raw[13] >= 64 && raw[13] <= 127)) { - return true; - } - - return false; -}; - -/** - * Test whether the ip is RFC 5737. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isRFC5737 = function isRFC5737(raw) { - if (!IP.isIPv4(raw)) - return false; - - if (raw[12] === 192 - && (raw[13] === 0 && raw[14] === 2)) { - return true; - } - - if (raw[12] === 198 && raw[13] === 51 && raw[14] === 100) - return true; - - if (raw[12] === 203 && raw[13] === 0 && raw[14] === 113) - return true; - - return false; -}; - -/** - * Test whether the ip is RFC 3849. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isRFC3849 = function isRFC3849(raw) { - if (raw[0] === 0x20 && raw[1] === 0x01 - && raw[2] === 0x0d && raw[3] === 0xb8) { - return true; - } - - return false; -}; - -/** - * Test whether the ip is RFC 3964. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isRFC3964 = function isRFC3964(raw) { - if (raw[0] === 0x20 && raw[1] === 0x02) - return true; - - return false; -}; - -/** - * Test whether the ip is RFC 6052. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isRFC6052 = function isRFC6052(raw) { - return IP.hasPrefix(raw, RFC6052); -}; - -/** - * Test whether the ip is RFC 4380. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isRFC4380 = function isRFC4380(raw) { - if (raw[0] === 0x20 && raw[1] === 0x01 - && raw[2] === 0x00 && raw[3] === 0x00) { - return true; - } - - return false; -}; - -/** - * Test whether the ip is RFC 4862. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isRFC4862 = function isRFC4862(raw) { - return IP.hasPrefix(raw, RFC4862); -}; - -/** - * Test whether the ip is RFC 4193. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isRFC4193 = function isRFC4193(raw) { - if ((raw[0] & 0xfe) === 0xfc) - return true; - - return false; -}; - -/** - * Test whether the ip is RFC 6145. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isRFC6145 = function isRFC6145(raw) { - return IP.hasPrefix(raw, RFC6145); -}; - -/** - * Test whether the ip is RFC 4843. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isRFC4843 = function isRFC4843(raw) { - if (raw[0] === 0x20 && raw[1] === 0x01 - && raw[2] === 0x00 && (raw[3] & 0xf0) === 0x10) { - return true; - } - - return false; -}; - -/** - * Test whether the ip has a tor onion prefix. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isOnion = function isOnion(raw) { - return IP.hasPrefix(raw, TOR_ONION); -}; - -/** - * Test whether the ip is local. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isLocal = function isLocal(raw) { - if (IP.isIPv4(raw)) { - if (raw[12] === 127 && raw[13] === 0) - return true; - return false; - } - - if (IP.isEqual(raw, LOCAL_IP)) - return true; - - return false; -}; - -/** - * Test whether the ip is a multicast address. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isMulticast = function isMulticast(raw) { - if (IP.isIPv4(raw)) { - if ((raw[12] & 0xf0) === 0xe0) - return true; - return false; - } - return raw[0] === 0xff; -}; - -/** - * Test whether the ip is valid. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isValid = function isValid(raw) { - if (IP.hasPrefix(raw, SHIFTED)) - return false; - - if (IP.isNull(raw)) - return false; - - if (IP.isBroadcast(raw)) - return false; - - if (IP.isRFC3849(raw)) - return false; - - return true; -}; - -/** - * Test whether the ip is routable. - * @param {Buffer} raw - * @returns {Boolean} - */ - -IP.isRoutable = function isRoutable(raw) { - if (!IP.isValid(raw)) - return false; - - if (IP.isRFC1918(raw)) - return false; - - if (IP.isRFC2544(raw)) - return false; - - if (IP.isRFC3927(raw)) - return false; - - if (IP.isRFC4862(raw)) - return false; - - if (IP.isRFC6598(raw)) - return false; - - if (IP.isRFC5737(raw)) - return false; - - if (IP.isRFC4193(raw) && !IP.isOnion(raw)) - return false; - - if (IP.isRFC4843(raw)) - return false; - - if (IP.isLocal(raw)) - return false; - - return true; -}; - -/** - * Calculate reachable score from source to destination. - * @param {Buffer} src - * @param {Buffer} dest - * @returns {Number} Ranges from 0-6. - */ - -IP.getReachability = function getReachability(src, dest) { - const UNREACHABLE = 0; - const DEFAULT = 1; - const TEREDO = 2; - const IPV6_WEAK = 3; - const IPV4 = 4; - const IPV6_STRONG = 5; - const PRIVATE = 6; - - if (!IP.isRoutable(src)) - return UNREACHABLE; - - const srcNet = IP.getNetwork(src); - const destNet = IP.getNetwork(dest); - const types = IP.types; - - switch (destNet) { - case types.IPV4: - switch (srcNet) { - case types.IPV4: - return IPV4; - default: - return DEFAULT; - } - break; - case types.IPV6: - switch (srcNet) { - case types.TEREDO: - return TEREDO; - case types.IPV4: - return IPV4; - case types.IPV6: - if (IP.isRFC3964(src) - || IP.isRFC6052(src) - || IP.isRFC6145(src)) { - // tunnel - return IPV6_WEAK; - } - return IPV6_STRONG; - default: - return DEFAULT; - } - break; - case types.ONION: - switch (srcNet) { - case types.IPV4: - return IPV4; - case types.ONION: - return PRIVATE; - default: - return DEFAULT; - } - break; - case types.TEREDO: - switch (srcNet) { - case types.TEREDO: - return TEREDO; - case types.IPV6: - return IPV6_WEAK; - case types.IPV4: - return IPV4; - default: - return DEFAULT; - } - break; - default: - switch (srcNet) { - case types.TEREDO: - return TEREDO; - case types.IPV6: - return IPV6_WEAK; - case types.IPV4: - return IPV4; - case types.ONION: - return PRIVATE; - default: - return DEFAULT; - } - break; - } -}; - -/** - * Test whether an IP has a prefix. - * @param {Buffer} raw - * @param {Buffer} prefix - * @returns {Boolean} - */ - -IP.hasPrefix = function hasPrefix(raw, prefix) { - assert(Buffer.isBuffer(raw)); - assert(Buffer.isBuffer(prefix)); - assert(raw.length >= prefix.length); - - for (let i = 0; i < prefix.length; i++) { - if (raw[i] !== prefix[i]) - return false; - } - - return true; -}; - -/** - * Test whether two IPs are equal. - * @param {Buffer} a - * @param {Buffer} b - * @returns {Boolean} - */ - -IP.isEqual = function isEqual(a, b) { - assert(a.length === 16); - assert(b.length === 16); - return a.equals(b); -}; - -/** - * Get IP address from network interfaces. - * @param {String?} name - `public` or `private`. - * @param {String?} family - IP family name. - * @returns {String} - */ - -IP.getInterfaces = function getInterfaces(name, family) { - const interfaces = os.networkInterfaces(); - const result = []; - - for (const key of Object.keys(interfaces)) { - const items = interfaces[key]; - - for (const details of items) { - const type = details.family.toLowerCase(); - - if (family && type !== family) - continue; - - if (details.internal) - continue; - - let raw; - try { - raw = IP.toBuffer(details.address); - } catch (e) { - continue; - } - - if (IP.isNull(raw)) - continue; - - if (IP.isLocal(raw)) - continue; - - if (name === 'public') { - if (!IP.isRoutable(raw)) - continue; - } else if (name === 'private') { - if (IP.isRoutable(raw)) - continue; - } - - result.push(IP.toString(raw)); - } - } - - return result; -}; - -/** - * Get private IP from network interfaces. - * @param {String?} family - IP family name. - * @returns {String} - */ - -IP.getPrivate = function getPrivate(family) { - return IP.getInterfaces('private', family); -}; - -/** - * Get public IP from network interfaces. - * @param {String?} family - IP family name. - * @returns {String} - */ - -IP.getPublic = function getPublic(family) { - return IP.getInterfaces('public', family); -}; - -/** - * Represents a parsed address. - * @constructor - * @alias module:utils/ip.Address - * @param {String} host - * @param {Number} port - * @param {Number} type - * @param {String} hostname - * @param {Buffer|null} raw - * @property {String} host - * @property {Number} port - * @property {Number} type - * @property {String} hostname - * @property {Buffer} raw - */ - -function Address(host, port, type, hostname, raw) { - this.host = host || '0.0.0.0'; - this.port = port || 0; - this.type = type || IP.types.IPV4; - this.hostname = hostname || '0.0.0.0:0'; - this.raw = raw || ZERO_IP; -} - -/* - * Expose - */ - -IP.Address = Address; diff --git a/package.json b/package.json index db64439b..4d875a90 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "bdb": "^0.0.1", "bfile": "^0.0.1", "bfilter": "^0.0.1", + "binet": "^0.0.1", "breq": "^0.0.1", "bsock": "^0.0.1", "bstr": "^0.0.1",