diff --git a/lib/bcoin/ip.js b/lib/bcoin/ip.js new file mode 100644 index 00000000..0c49a9c9 --- /dev/null +++ b/lib/bcoin/ip.js @@ -0,0 +1,197 @@ +/*! + * ip.js - ip utils for bcoin + * Copyright (c) 2014-2015, Fedor Indutny (MIT License) + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/indutny/bcoin + */ + +var IP = require('../../vendor/ip'); +var assert = require('assert'); + +/** + * Parse a hostname. + * @example + * IP.parseHost('127.0.0.1:3000'); + * @param {String} addr + * @returns {Object} Contains `host` and `port`. + */ + +exports.parseHost = function parseHost(addr) { + var parts, host, port; + + assert(addr); + + if (typeof addr === 'object') + return addr; + + if (addr[0] === '[') { + addr = addr.substring(1); + parts = addr.split(/\]:?/); + assert(parts.length === 2); + } else { + parts = addr.split(':'); + } + + host = parts[0]; + port = +parts[1] || 0; + + if (exports.version(host) !== -1) + host = exports.normalize(host); + + return { + host: host, + port: port + }; +}; + +/** + * Concatenate a host and port. + * @param {String} host + * @param {Number} port + * @returns {String} + */ + +exports.hostname = function hostname(host, port) { + if (exports.version(host) === 6) + host = '[' + host + ']'; + return host + ':' + port; +}; + +/** + * Test whether a string is an IP address. + * @param {String?} ip + * @returns {Number} IP version (4 or 6). + */ + +exports.version = function version(ip) { + if (typeof ip !== 'string') + return -1; + + if (IP.isV4Format(ip)) + return 4; + + if (IP.isV6Format(ip)) + return 6; + + return -1; +}; + +/** + * Test whether a buffer is an ipv4-mapped ipv6 address. + * @param {Buffer} ip + * @returns {Boolean} + */ + +exports.isMapped = function isMapped(ip) { + var i; + + assert(Buffer.isBuffer(ip)); + assert(ip.length === 16); + + for (i = 0; i < ip.length - 6; i++) { + if (ip[i] !== 0) + return false; + } + + if (ip[ip.length - 6] !== 0xff && ip[ip.length - 5] !== 0xff) + return false; + + return true; +}; + +/** + * Convert an IP string to a buffer. + * @param {String} ip + * @returns {Buffer} + */ + +exports.toBuffer = function toArray(ip) { + var out; + + if (Buffer.isBuffer(ip)) { + assert(ip.length === 16); + return ip; + } + + if (!ip) + return toArray('0.0.0.0'); + + assert(typeof ip === 'string'); + assert(exports.version(ip) !== -1); + + ip = IP.toBuffer(ip); + + if (ip.length === 4) { + out = new Buffer(16); + out.fill(0); + out[10] = 0xff; + out[11] = 0xff; + out[12] = ip[0]; + out[13] = ip[1]; + out[14] = ip[2]; + out[15] = ip[3]; + return out; + } + + return ip; +}; + +/** + * Convert a buffer to an ip string. + * @param {Buffer} ip + * @returns {String} + */ + +exports.toString = function toString(ip) { + if (typeof ip === 'string') { + assert(exports.version(ip) !== -1); + return ip; + } + + if (!ip) + return '0.0.0.0'; + + assert(Buffer.isBuffer(ip)); + assert(ip.length === 16); + + if (exports.isMapped(ip)) { + return ip[ip.length - 4] + + '.' + ip[ip.length - 3] + + '.' + ip[ip.length - 2] + + '.' + ip[ip.length - 1]; + } + + return IP.toString(ip); +}; + +/** + * Normalize an ip. + * @param {String} ip + * @returns {String} + */ + +exports.normalize = function normalize(ip) { + return exports.toString(exports.toBuffer(ip)); +}; + +/* + * Expose IP functions. + */ + +exports.isV4Format = IP.isV4Format; +exports.isV6Format = IP.isV6Format; +exports.fromPrefixLen = IP.fromPrefixLen; +exports.mask = IP.mask; +exports.cidr = IP.cidr; +exports.subnet = IP.subnet; +exports.cidrSubnet = IP.cidrSubnet; +exports.not = IP.not; +exports.or = IP.or; +exports.isEqual = IP.isEqual; +exports.isPrivate = IP.isPrivate; +exports.isPublic = IP.isPublic; +exports.isLoopback = IP.isLoopback; +exports.loopback = IP.loopback; +exports.address = IP.address; +exports.toLong = IP.toLong; +exports.fromLong = IP.fromLong; diff --git a/lib/bcoin/peer.js b/lib/bcoin/peer.js index 5c338104..5429f0d8 100644 --- a/lib/bcoin/peer.js +++ b/lib/bcoin/peer.js @@ -8,6 +8,7 @@ var bcoin = require('./env'); var EventEmitter = require('events').EventEmitter; var utils = require('./utils'); +var IP = require('./ip'); var assert = utils.assert; var constants = bcoin.protocol.constants; @@ -111,7 +112,7 @@ function Peer(pool, options) { this.host = this.socket.remoteAddress; this.port = this.socket.remotePort; } else if (options.seed) { - seed = utils.parseHost(options.seed); + seed = IP.parseHost(options.seed); this.host = seed.host; this.port = seed.port || this.network.port; this.socket = this.createSocket(this.port, this.host); @@ -122,7 +123,7 @@ function Peer(pool, options) { assert(typeof this.host === 'string'); assert(typeof this.port === 'number'); - this.hostname = utils.hostname(this.host, this.port); + this.hostname = IP.hostname(this.host, this.port); if (!this.socket) throw new Error('No socket'); @@ -282,7 +283,7 @@ Peer.prototype._init = function init() { Peer.prototype.createSocket = function createSocket(port, host) { var self = this; - var hostname = utils.hostname(host, port); + var hostname = IP.hostname(host, port); var socket, net; if (this._createSocket) { @@ -1456,10 +1457,13 @@ Peer.prototype._handleGetAddr = function _handleGetAddr() { return; for (i = 0; i < this.pool.seeds.length; i++) { - seed = utils.parseHost(this.pool.seeds[i]); + seed = this.pool.seeds[i]; + + assert(typeof seed === 'object'); + seed = this.pool.getPeer(seed.host) || seed; - if (utils.ip.version(seed.host) === -1) + if (IP.version(seed.host) === -1) continue; if (hosts[seed.host]) diff --git a/lib/bcoin/pool.js b/lib/bcoin/pool.js index 5d2832f7..52c81141 100644 --- a/lib/bcoin/pool.js +++ b/lib/bcoin/pool.js @@ -8,6 +8,7 @@ var bcoin = require('./env'); var EventEmitter = require('events').EventEmitter; var utils = require('./utils'); +var IP = require('./ip'); var assert = utils.assert; var constants = bcoin.protocol.constants; var VerifyError = bcoin.errors.VerifyError; @@ -103,7 +104,7 @@ function Pool(options) { if (process.env.BCOIN_SEED) seeds.unshift(process.env.BCOIN_SEED); - this.originalSeeds = seeds.map(utils.parseHost); + this.originalSeeds = seeds.map(IP.parseHost); this.seeds = []; this.hosts = {}; this.setSeeds([]); @@ -436,17 +437,17 @@ Pool.prototype.listen = function listen(callback) { return; } - host = utils.ip.normalize(socket.remoteAddress); + host = IP.normalize(socket.remoteAddress); if (self.peers.leeches.length >= self.maxLeeches) { - hostname = utils.hostname(host, socket.remotePort); + hostname = IP.hostname(host, socket.remotePort); bcoin.debug('Ignoring leech: too many leeches (%s).', hostname); socket.destroy(); return; } if (self.isMisbehaving(host)) { - hostname = utils.hostname(host, socket.remotePort); + hostname = IP.hostname(host, socket.remotePort); bcoin.debug('Ignoring misbehaving leech (%s).', hostname); socket.destroy(); return; @@ -1980,7 +1981,7 @@ Pool.prototype.setSeeds = function setSeeds(seeds) { */ Pool.prototype.addSeed = function addSeed(seed) { - seed = utils.parseHost(seed); + seed = IP.parseHost(seed); if (this.hosts[seed.host] != null) return false; @@ -2004,7 +2005,7 @@ Pool.prototype.addSeed = function addSeed(seed) { Pool.prototype.removeSeed = function removeSeed(seed) { var i; - seed = utils.parseHost(seed); + seed = IP.parseHost(seed); if (this.hosts[seed.host] == null) return false; @@ -2134,10 +2135,10 @@ Pool.prototype.getIP = function getIP(callback) { ip = body.trim(); - if (utils.ip.version(ip) == -1) + if (IP.version(ip) == -1) return self.getIP2(callback); - return callback(null, utils.ip.normalize(ip)); + return callback(null, IP.normalize(ip)); }); }; @@ -2163,10 +2164,10 @@ Pool.prototype.getIP2 = function getIP2(callback) { ip = /IP Address:\s*([0-9a-f.:]+)/i.exec(body); - if (!ip || utils.ip.version(ip[1]) === -1) + if (!ip || IP.version(ip[1]) === -1) return callback(new Error('Could not find IP.')); - return callback(null, utils.ip.normalize(ip[1])); + return callback(null, IP.normalize(ip[1])); }); }; diff --git a/lib/bcoin/protocol/framer.js b/lib/bcoin/protocol/framer.js index 35d81af8..45ac540d 100644 --- a/lib/bcoin/protocol/framer.js +++ b/lib/bcoin/protocol/framer.js @@ -8,6 +8,7 @@ var bcoin = require('../env'); var constants = require('./constants'); var utils = require('../utils'); +var IP = require('../ip'); var assert = utils.assert; var BufferWriter = require('../writer'); var DUMMY = new Buffer([]); @@ -432,7 +433,7 @@ Framer.address = function address(data, full, writer) { } p.writeU64(data.services || 0); - p.writeBytes(utils.ip.toBuffer(data.host)); + p.writeBytes(IP.toBuffer(data.host)); p.writeU16BE(data.port || 0); if (!writer) diff --git a/lib/bcoin/protocol/parser.js b/lib/bcoin/protocol/parser.js index 1bcd9bfe..aab4f17b 100644 --- a/lib/bcoin/protocol/parser.js +++ b/lib/bcoin/protocol/parser.js @@ -9,6 +9,7 @@ var bcoin = require('../env'); var bn = require('bn.js'); var EventEmitter = require('events').EventEmitter; var utils = require('../utils'); +var IP = require('../ip'); var assert = utils.assert; var constants = require('./constants'); var BufferReader = require('../reader'); @@ -1197,7 +1198,7 @@ Parser.parseAddress = function parseAddress(p, full) { return { ts: ts, services: services, - host: utils.ip.toString(ip), + host: IP.toString(ip), port: port }; }; diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index 561a1624..3a2cfbc7 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -842,178 +842,6 @@ utils.isBTC = function isBTC(value) { } }; -/** - * Parse a hostname. - * @example - * utils.parseHost('127.0.0.1:3000'); - * @param {String} addr - * @returns {Object} Contains `host` and `port`. - */ - -utils.parseHost = function parseHost(addr) { - var parts, host, port; - - assert(addr); - - if (typeof addr === 'object') - return addr; - - if (addr[0] === '[') { - addr = addr.substring(1); - parts = addr.split(/\]:?/); - assert(parts.length === 2); - } else { - parts = addr.split(':'); - } - - host = parts[0]; - port = +parts[1] || 0; - - if (utils.ip.version(host) !== -1) - host = utils.ip.normalize(host); - - return { - host: host, - port: port - }; -}; - -/** - * Concatenate a host and port. - * @param {String} host - * @param {Number} port - * @returns {String} - */ - -utils.hostname = function hostname(host, port) { - if (utils.ip.version(host) === 6) - host = '[' + host + ']'; - return host + ':' + port; -}; - -/** - * IP utilities. - */ - -utils.ip = utils.merge({}, IP); - -/** - * Test whether a string is an IP address. - * @param {String?} ip - * @returns {Number} IP version (4 or 6). - */ - -utils.ip.version = function version(ip) { - if (typeof ip !== 'string') - return -1; - - if (IP.isV4Format(ip)) - return 4; - - if (IP.isV6Format(ip)) - return 6; - - return -1; -}; - -/** - * Test whether a buffer is an ipv4-mapped ipv6 address. - * @param {Buffer} ip - * @returns {Boolean} - */ - -utils.ip.isMapped = function isMapped(ip) { - var i; - - assert(Buffer.isBuffer(ip)); - assert(ip.length === 16); - - for (i = 0; i < ip.length - 6; i++) { - if (ip[i] !== 0) - return false; - } - - if (ip[ip.length - 6] !== 0xff && ip[ip.length - 5] !== 0xff) - return false; - - return true; -}; - -/** - * Convert an IP string to a buffer. - * @param {String} ip - * @returns {Buffer} - */ - -utils.ip.toBuffer = function toArray(ip) { - var out; - - if (Buffer.isBuffer(ip)) { - assert(ip.length === 16); - return ip; - } - - if (!ip) - return toArray('0.0.0.0'); - - assert(typeof ip === 'string'); - assert(utils.ip.version(ip) !== -1); - - ip = IP.toBuffer(ip); - - if (ip.length === 4) { - out = new Buffer(16); - out.fill(0); - out[10] = 0xff; - out[11] = 0xff; - out[12] = ip[0]; - out[13] = ip[1]; - out[14] = ip[2]; - out[15] = ip[3]; - return out; - } - - return ip; -}; - -/** - * Convert a buffer to an ip string. - * @param {Buffer} ip - * @returns {String} - */ - -utils.ip.toString = function toString(ip) { - if (typeof ip === 'string') { - assert(utils.ip.version(ip) !== -1); - return ip; - } - - if (!ip) - return '0.0.0.0'; - - assert(Buffer.isBuffer(ip)); - assert(ip.length === 16); - - if (utils.ip.isMapped(ip)) { - return ip[ip.length - 4] - + '.' + ip[ip.length - 3] - + '.' + ip[ip.length - 2] - + '.' + ip[ip.length - 1]; - } - - return IP.toString(ip); -}; - -/** - * Normalize an ip. - * @param {String} ip - * @returns {String} - */ - -utils.ip.normalize = function normalize(ip) { - return utils.ip.toString(utils.ip.toBuffer(ip)); -}; - /** * util.inspect() with 20 levels of depth. * @param {Object|String} obj