ip/netaddress: better ip parsing.
This commit is contained in:
parent
2d49e38580
commit
016d0da096
@ -110,7 +110,7 @@ WSProxy.prototype._handleResolve = function _handleResolve(ws, name, record, cal
|
||||
WSProxy.prototype._handleConnect = function _handleConnect(ws, port, host, nonce) {
|
||||
var self = this;
|
||||
var state = this.sockets.get(ws);
|
||||
var socket, pow;
|
||||
var socket, pow, raw;
|
||||
|
||||
if (state.socket) {
|
||||
this.log('Client is trying to reconnect (%s).', state.host);
|
||||
@ -149,7 +149,9 @@ WSProxy.prototype._handleConnect = function _handleConnect(ws, port, host, nonce
|
||||
}
|
||||
}
|
||||
|
||||
if (IP.version(host) === -1) {
|
||||
try {
|
||||
raw = IP.toBuffer(host);
|
||||
} catch (e) {
|
||||
this.log('Client gave a bad host: %s (%s).', host, state.host);
|
||||
ws.emit('tcp error', {
|
||||
message: 'EHOSTUNREACH',
|
||||
@ -159,8 +161,10 @@ WSProxy.prototype._handleConnect = function _handleConnect(ws, port, host, nonce
|
||||
return;
|
||||
}
|
||||
|
||||
if (IP.isPrivate(host)) {
|
||||
this.log('Client is trying to connect to a private ip (%s).', state.host);
|
||||
if (!IP.isRoutable(raw)) {
|
||||
this.log(
|
||||
'Client is trying to connect to a bad ip: %s (%s).',
|
||||
host, state.host);
|
||||
ws.emit('tcp error', {
|
||||
message: 'ENETUNREACH',
|
||||
code: 'ENETUNREACH'
|
||||
|
||||
@ -1759,7 +1759,7 @@ HTTPOptions.prototype.fromOptions = function fromOptions(options) {
|
||||
|
||||
if (options.host != null) {
|
||||
assert(typeof options.host === 'string');
|
||||
this.host = options.host;
|
||||
this.host = IP.normalize(options.host);
|
||||
}
|
||||
|
||||
if (options.port != null) {
|
||||
@ -1786,7 +1786,7 @@ HTTPOptions.prototype.fromOptions = function fromOptions(options) {
|
||||
// Allow no-auth implicitly
|
||||
// if we're listening locally.
|
||||
if (!options.apiKey && !options.serviceKey && options.noAuth == null) {
|
||||
if (IP.isLoopback(this.host))
|
||||
if (this.host === '127.0.0.1' || this.host === '::1')
|
||||
this.noAuth = true;
|
||||
}
|
||||
|
||||
|
||||
@ -3153,10 +3153,13 @@ Pool.prototype.getIP = co(function* getIP() {
|
||||
|
||||
ip = res.body.trim();
|
||||
|
||||
if (IP.version(ip) === -1)
|
||||
try {
|
||||
ip = IP.normalize(ip);
|
||||
} catch (e) {
|
||||
return yield this.getIP2();
|
||||
}
|
||||
|
||||
return IP.normalize(ip);
|
||||
return ip;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -3184,9 +3187,6 @@ Pool.prototype.getIP2 = co(function* getIP2() {
|
||||
|
||||
ip = match[1];
|
||||
|
||||
if (IP.version(ip) === -1)
|
||||
throw new Error('Could not parse IP.');
|
||||
|
||||
return IP.normalize(ip);
|
||||
});
|
||||
|
||||
@ -3311,10 +3311,10 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) {
|
||||
|
||||
if (options.host != null) {
|
||||
assert(typeof options.host === 'string');
|
||||
assert(IP.version(options.host) !== -1, '`host` must be an IP.');
|
||||
this.host = IP.normalize(options.host);
|
||||
if (IP.isRoutable(this.host))
|
||||
this.address.setHost(this.host);
|
||||
this.host = options.host;
|
||||
this.address.setHost(this.host);
|
||||
if (!this.address.isRoutable())
|
||||
this.address.setNull();
|
||||
}
|
||||
|
||||
if (options.port != null) {
|
||||
|
||||
@ -38,6 +38,7 @@ function NetAddress(options) {
|
||||
this.services = 0;
|
||||
this.ts = 0;
|
||||
this.hostname = '0.0.0.0:0';
|
||||
this.raw = IP.ZERO_IP;
|
||||
|
||||
if (options)
|
||||
this.fromOptions(options);
|
||||
@ -65,9 +66,8 @@ NetAddress.prototype.fromOptions = function fromOptions(options) {
|
||||
assert(typeof options.host === 'string');
|
||||
assert(typeof options.port === 'number');
|
||||
|
||||
assert(IP.version(options.host) !== -1);
|
||||
|
||||
this.host = IP.normalize(options.host);
|
||||
this.raw = IP.toBuffer(options.host);
|
||||
this.host = IP.toString(this.raw);
|
||||
this.port = options.port;
|
||||
|
||||
if (options.services) {
|
||||
@ -105,22 +105,31 @@ NetAddress.prototype.hasServices = function hasServices(services) {
|
||||
return (this.services & services) === services;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the address is IPv4.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
NetAddress.isIPv4 = function isIPv4() {
|
||||
return IP.isIPv4(this.raw);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the address is IPv6.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
NetAddress.isIPv6 = function isIPv6() {
|
||||
return !IP.isIPv6(this.raw);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the host is null.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
NetAddress.prototype.isNull = function isNull() {
|
||||
return IP.isNull(this.host);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the host is a broadcast address.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
NetAddress.prototype.isBroadcast = function isBroadcast() {
|
||||
return IP.isBroadcast(this.host);
|
||||
return IP.isNull(this.raw);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -128,17 +137,8 @@ NetAddress.prototype.isBroadcast = function isBroadcast() {
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
NetAddress.prototype.isLoopback = function isLoopback() {
|
||||
return IP.isLoopback(this.host);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the host is a private address.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
NetAddress.prototype.isPrivate = function isPrivate() {
|
||||
return IP.isPrivate(this.host);
|
||||
NetAddress.prototype.isLocal = function isLocal() {
|
||||
return IP.isLocal(this.raw);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -147,7 +147,7 @@ NetAddress.prototype.isPrivate = function isPrivate() {
|
||||
*/
|
||||
|
||||
NetAddress.prototype.isValid = function isValid() {
|
||||
return IP.isValid(this.host);
|
||||
return IP.isValid(this.raw);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -156,7 +156,26 @@ NetAddress.prototype.isValid = function isValid() {
|
||||
*/
|
||||
|
||||
NetAddress.prototype.isRoutable = function isRoutable() {
|
||||
return IP.isRoutable(this.host);
|
||||
return IP.isRoutable(this.raw);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the host is an onion address.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
NetAddress.prototype.isTor = function isTor() {
|
||||
return IP.isTor(this.raw);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set null host.
|
||||
*/
|
||||
|
||||
NetAddress.prototype.setNull = function setNull() {
|
||||
this.raw = IP.ZERO_IP;
|
||||
this.host = '0.0.0.0';
|
||||
this.hostname = IP.hostname(this.host, this.port);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -165,8 +184,9 @@ NetAddress.prototype.isRoutable = function isRoutable() {
|
||||
*/
|
||||
|
||||
NetAddress.prototype.setHost = function setHost(host) {
|
||||
this.host = host;
|
||||
this.hostname = IP.hostname(host, this.port);
|
||||
this.raw = IP.toBuffer(host);
|
||||
this.host = IP.toString(this.raw);
|
||||
this.hostname = IP.hostname(this.host, this.port);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -190,9 +210,8 @@ NetAddress.prototype.setPort = function setPort(port) {
|
||||
NetAddress.prototype.fromHost = function fromHost(host, port, network) {
|
||||
network = Network.get(network);
|
||||
|
||||
assert(IP.version(host) !== -1);
|
||||
|
||||
this.host = host;
|
||||
this.raw = IP.toBuffer(host);
|
||||
this.host = IP.toString(this.raw);
|
||||
this.port = port || network.port;
|
||||
this.services = NetAddress.DEFAULT_SERVICES;
|
||||
this.ts = network.now();
|
||||
@ -284,7 +303,8 @@ NetAddress.prototype.fromReader = function fromReader(br, full) {
|
||||
// are currently unused.
|
||||
br.readU32();
|
||||
|
||||
this.host = IP.toString(br.readBytes(16, true));
|
||||
this.raw = br.readBytes(16);
|
||||
this.host = IP.toString(this.raw);
|
||||
this.port = br.readU16BE();
|
||||
this.hostname = IP.hostname(this.host, this.port);
|
||||
|
||||
@ -337,7 +357,7 @@ NetAddress.prototype.toWriter = function toWriter(bw, full) {
|
||||
|
||||
bw.writeU32(this.services);
|
||||
bw.writeU32(0);
|
||||
bw.writeBytes(IP.toBuffer(this.host));
|
||||
bw.writeBytes(this.raw);
|
||||
bw.writeU16BE(this.port);
|
||||
|
||||
return bw;
|
||||
@ -385,11 +405,11 @@ NetAddress.prototype.toJSON = function toJSON() {
|
||||
*/
|
||||
|
||||
NetAddress.prototype.fromJSON = function fromJSON(json) {
|
||||
assert(IP.version(json.host) !== -1);
|
||||
assert(util.isNumber(json.port));
|
||||
assert(json.port >= 0 && json.port <= 0xffff);
|
||||
assert(util.isNumber(json.services));
|
||||
assert(util.isNumber(json.ts));
|
||||
this.raw = IP.toBuffer(json.host);
|
||||
this.host = json.host;
|
||||
this.port = json.port;
|
||||
this.services = json.services;
|
||||
|
||||
584
lib/utils/ip.js
584
lib/utils/ip.js
@ -18,10 +18,26 @@ var IP = exports;
|
||||
* Constants
|
||||
*/
|
||||
|
||||
var ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
||||
var ipv6Regex =
|
||||
var ZERO_IP = new Buffer('00000000000000000000000000000000', 'hex');
|
||||
var LOCAL_IP = new Buffer('00000000000000000000000000000001', 'hex');
|
||||
var RFC6052 = new Buffer('0064ff9b0000000000000000', 'hex');
|
||||
var RFC4862 = new Buffer('fe80000000000000', 'hex');
|
||||
var RFC6145 = new Buffer('0000000000000000ffff0000', 'hex');
|
||||
var TOR_ONION = new Buffer('fd87d87eeb43', 'hex');
|
||||
var V4MAP = new Buffer('00000000000000000000ffff', 'hex');
|
||||
var SHIFTED = new Buffer('00000000000000ffff', 'hex');
|
||||
|
||||
var IPV4_REGEX = /^(\d{1,3}\.){3}\d{1,3}$/;
|
||||
var 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;
|
||||
|
||||
/**
|
||||
* Parse a hostname.
|
||||
* @param {String} addr
|
||||
@ -30,7 +46,7 @@ var ipv6Regex =
|
||||
*/
|
||||
|
||||
IP.parseHost = function parseHost(addr, fallback) {
|
||||
var parts, host, port, version, hostname;
|
||||
var parts, host, port, version, hostname, raw;
|
||||
|
||||
assert(typeof addr === 'string');
|
||||
|
||||
@ -91,8 +107,10 @@ IP.parseHost = function parseHost(addr, fallback) {
|
||||
|
||||
version = IP.version(host);
|
||||
|
||||
if (version !== -1)
|
||||
host = IP.normalize(host);
|
||||
if (version !== -1) {
|
||||
raw = IP.toBuffer(host);
|
||||
host = IP.toString(raw);
|
||||
}
|
||||
|
||||
hostname = host;
|
||||
|
||||
@ -101,7 +119,7 @@ IP.parseHost = function parseHost(addr, fallback) {
|
||||
|
||||
hostname += ':' + port;
|
||||
|
||||
return new Address(host, port, version, hostname);
|
||||
return new Address(host, port, version, hostname, raw);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -166,7 +184,7 @@ IP.isV4Format = function isV4Format(str) {
|
||||
if (str.length > 15)
|
||||
return false;
|
||||
|
||||
return ipv4Regex.test(str);
|
||||
return IPV4_REGEX.test(str);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -184,66 +202,64 @@ IP.isV6Format = function isV6Format(str) {
|
||||
if (str.length > 39)
|
||||
return false;
|
||||
|
||||
return ipv6Regex.test(str);
|
||||
return IPV6_REGEX.test(str);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether a buffer is an ipv4-mapped ipv6 address.
|
||||
* @param {Buffer} buf
|
||||
* @param {Buffer} raw
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
IP.isMapped = function isMapped(buf) {
|
||||
var i;
|
||||
IP.isMapped = function isMapped(raw) {
|
||||
assert(Buffer.isBuffer(raw));
|
||||
assert(raw.length === 16);
|
||||
|
||||
assert(Buffer.isBuffer(buf));
|
||||
assert(buf.length === 16);
|
||||
|
||||
if (buf[10] !== 0xff || buf[11] !== 0xff)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (buf[i] !== 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert an IP string to a buffer.
|
||||
* Parse an IP string and return a buffer.
|
||||
* @param {String} str
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
IP.toBuffer = function toBuffer(str) {
|
||||
var buf = new Buffer(16);
|
||||
var raw = new Buffer(16);
|
||||
|
||||
assert(typeof str === 'string');
|
||||
|
||||
if (IP.isV4Format(str)) {
|
||||
buf.fill(0);
|
||||
buf[10] = 0xff;
|
||||
buf[11] = 0xff;
|
||||
return IP.parseV4(str, buf, 12);
|
||||
raw.fill(0);
|
||||
raw[10] = 0xff;
|
||||
raw[11] = 0xff;
|
||||
return IP.parseV4(str, raw, 12);
|
||||
}
|
||||
|
||||
if (IP.isV6Format(str))
|
||||
return IP.parseV6(str, buf, 0);
|
||||
|
||||
throw Error('Invalid IP address: ' + str);
|
||||
return IP.parseV6(str, raw, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert an IPv4 string to a buffer.
|
||||
* @private
|
||||
* @param {String} str
|
||||
* @param {Buffer} buf
|
||||
* @param {Buffer} raw
|
||||
* @param {Number} offset
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
IP.parseV4 = function parseV4(str, buf, offset) {
|
||||
IP.parseV4 = function parseV4(str, raw, offset) {
|
||||
var parts = str.split('.');
|
||||
var i, ch;
|
||||
|
||||
@ -255,29 +271,28 @@ IP.parseV4 = function parseV4(str, buf, offset) {
|
||||
assert(ch.length <= 3);
|
||||
ch = parseInt(ch, 10);
|
||||
assert(ch >= 0 && ch <= 255);
|
||||
buf[offset++] = ch;
|
||||
raw[offset++] = ch;
|
||||
}
|
||||
|
||||
return buf;
|
||||
return raw;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert an IPv6 string to a buffer.
|
||||
* @private
|
||||
* @param {String} str
|
||||
* @param {Buffer} buf
|
||||
* @param {Buffer} raw
|
||||
* @param {Number} offset
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
IP.parseV6 = function parseV6(str, buf, offset) {
|
||||
IP.parseV6 = function parseV6(str, raw, offset) {
|
||||
var parts = str.split(':');
|
||||
var missing = 8 - parts.length;
|
||||
var start = offset;
|
||||
var colon = false;
|
||||
var i, word;
|
||||
|
||||
assert(missing >= 0, 'IPv6 address is too long.');
|
||||
assert(parts.length >= 2, 'Not an IPv6 address.');
|
||||
|
||||
for (i = 0; i < parts.length; i++) {
|
||||
@ -286,8 +301,6 @@ IP.parseV6 = function parseV6(str, buf, offset) {
|
||||
missing--;
|
||||
}
|
||||
|
||||
assert(missing >= 0, 'IPv6 address is too long.');
|
||||
|
||||
for (i = 0; i < parts.length; i++) {
|
||||
word = parts[i];
|
||||
|
||||
@ -308,8 +321,8 @@ IP.parseV6 = function parseV6(str, buf, offset) {
|
||||
}
|
||||
|
||||
while (missing > 0) {
|
||||
buf[offset++] = 0;
|
||||
buf[offset++] = 0;
|
||||
raw[offset++] = 0;
|
||||
raw[offset++] = 0;
|
||||
missing--;
|
||||
}
|
||||
|
||||
@ -317,7 +330,7 @@ IP.parseV6 = function parseV6(str, buf, offset) {
|
||||
}
|
||||
|
||||
if (IP.isV4Format(word)) {
|
||||
IP.parseV4(word, buf, offset);
|
||||
IP.parseV4(word, raw, offset);
|
||||
offset += 4;
|
||||
continue;
|
||||
}
|
||||
@ -328,50 +341,50 @@ IP.parseV6 = function parseV6(str, buf, offset) {
|
||||
|
||||
assert(word === word, 'Non-number in IPv6 address.');
|
||||
|
||||
buf[offset++] = (word >> 8) & 0xff;
|
||||
buf[offset++] = word & 0xff;
|
||||
raw[offset++] = (word >> 8) & 0xff;
|
||||
raw[offset++] = word & 0xff;
|
||||
}
|
||||
|
||||
assert(missing === 0, 'IPv6 address has missing sections.');
|
||||
assert.equal(offset, start + 16);
|
||||
|
||||
return buf;
|
||||
return raw;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a buffer to an ip string.
|
||||
* @param {Buffer} buf
|
||||
* @param {Buffer} raw
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
IP.toString = function toString(buf) {
|
||||
IP.toString = function toString(raw) {
|
||||
var str = '';
|
||||
var i;
|
||||
|
||||
assert(Buffer.isBuffer(buf));
|
||||
assert(Buffer.isBuffer(raw));
|
||||
|
||||
if (buf.length === 4) {
|
||||
str += buf[0];
|
||||
str += '.' + buf[1];
|
||||
str += '.' + buf[2];
|
||||
str += '.' + buf[3];
|
||||
if (raw.length === 4) {
|
||||
str += raw[0];
|
||||
str += '.' + raw[1];
|
||||
str += '.' + raw[2];
|
||||
str += '.' + raw[3];
|
||||
return str;
|
||||
}
|
||||
|
||||
if (buf.length === 16) {
|
||||
if (IP.isMapped(buf)) {
|
||||
str += buf[12];
|
||||
str += '.' + buf[13];
|
||||
str += '.' + buf[14];
|
||||
str += '.' + buf[15];
|
||||
if (raw.length === 16) {
|
||||
if (IP.isMapped(raw)) {
|
||||
str += raw[12];
|
||||
str += '.' + raw[13];
|
||||
str += '.' + raw[14];
|
||||
str += '.' + raw[15];
|
||||
return str;
|
||||
}
|
||||
|
||||
str += buf.readUInt16BE(0, true).toString(16);
|
||||
str += raw.readUInt16BE(0, true).toString(16);
|
||||
|
||||
for (i = 2; i < 16; i += 2) {
|
||||
str += ':';
|
||||
str += buf.readUInt16BE(i, true).toString(16);
|
||||
str += raw.readUInt16BE(i, true).toString(16);
|
||||
}
|
||||
|
||||
str = str.replace(/(^|:)0(:0)*:0(:|$)/, '$1::$3');
|
||||
@ -380,7 +393,31 @@ IP.toString = function toString(buf) {
|
||||
return str;
|
||||
}
|
||||
|
||||
throw Error('Invalid IP address: ' + buf.toString('hex'));
|
||||
throw Error('Invalid IP address: ' + raw.toString('hex'));
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a buffer to an ip string or .onion.
|
||||
* @param {Buffer} raw
|
||||
* @returns {String}
|
||||
*/
|
||||
|
||||
IP.toHost = function toHost(raw) {
|
||||
var i, ch, host;
|
||||
|
||||
if (IP.isTor(raw)) {
|
||||
host = '';
|
||||
for (i = 6; i < raw.length; i += 2) {
|
||||
ch = raw[i];
|
||||
ch = ch.toString(32);
|
||||
while (ch.length < 4)
|
||||
ch = '0' + ch;
|
||||
host += ch;
|
||||
}
|
||||
return host + '.onion';
|
||||
}
|
||||
|
||||
return IP.toString(raw);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -394,116 +431,421 @@ IP.normalize = function normalize(str) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether an IP is null.
|
||||
* @param {String} str - Normalized ip.
|
||||
* Test whether the address is IPv4.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
IP.isNull = function isNull(str) {
|
||||
return str === '0.0.0.0' || str === '::';
|
||||
IP.isIPv4 = function isIPv4(raw) {
|
||||
return IP.isMapped(raw);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the IP is a broadcast address.
|
||||
* @param {String} str - Normalized ip.
|
||||
* Test whether the address is IPv6.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
IP.isBroadcast = function isBroadcast(str) {
|
||||
return str === '255.255.255.255'
|
||||
|| str === '::ffff:ffff:ffff';
|
||||
IP.isIPv6 = function isIPv6(raw) {
|
||||
return !IP.isMapped(raw) && !IP.isTor(raw);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the IP is valid.
|
||||
* @param {String} str - Normalized ip.
|
||||
* Test whether the host is null.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
IP.isValid = function isValid(str) {
|
||||
return !IP.isNull(str) && !IP.isBroadcast(str);
|
||||
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 IP is routable.
|
||||
* @param {String} str - Normalized ip.
|
||||
* Test whether the host is a broadcast address.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
IP.isRoutable = function isRoutable(str) {
|
||||
return IP.isValid(str) && !IP.isLoopback(str);
|
||||
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 a string is a private address.
|
||||
* @param {String} str
|
||||
* Test whether the ip is RFC 1918.
|
||||
* @param {Buffer} raw
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
IP.isPrivate = function isPrivate(str) {
|
||||
assert(typeof str === 'string');
|
||||
IP.isRFC1918 = function isRFC1918(raw) {
|
||||
if (!IP.isIPv4(raw))
|
||||
return false;
|
||||
|
||||
return /^(::f{4}:)?10\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(str)
|
||||
|| /^(::f{4}:)?192\.168\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(str)
|
||||
|| /^(::f{4}:)?172\.(1[6-9]|2\d|30|31)\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(str)
|
||||
|| /^(::f{4}:)?127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(str)
|
||||
|| /^(::f{4}:)?169\.254\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(str)
|
||||
|| /^f[cd][0-9a-f]{2}:/i.test(str)
|
||||
|| /^fe80:/i.test(str)
|
||||
|| /^::1$/.test(str)
|
||||
|| /^::$/.test(str);
|
||||
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 a string is a public address.
|
||||
* @param {String} str
|
||||
* Test whether the ip is RFC 2544.
|
||||
* @param {Buffer} raw
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
IP.isPublic = function isPublic(str) {
|
||||
return !IP.isPrivate(str);
|
||||
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 a string is a loopback address.
|
||||
* @param {String} str
|
||||
* Test whether the ip is RFC 3927.
|
||||
* @param {Buffer} raw
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
IP.isLoopback = function isLoopback(str) {
|
||||
assert(typeof str === 'string');
|
||||
IP.isRFC3927 = function isRFC3927(raw) {
|
||||
if (!IP.isIPv4(raw))
|
||||
return false;
|
||||
|
||||
return /^(::f{4}:)?127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/.test(str)
|
||||
|| /^fe80::1$/.test(str)
|
||||
|| /^::1$/.test(str)
|
||||
|| /^::$/.test(str);
|
||||
if (raw[12] === 169 && raw[13] === 254)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get loopback address for ip family.
|
||||
* @param {String} family - ipv4 or ipv6.
|
||||
* @returns {String}
|
||||
* Test whether the ip is RFC 6598.
|
||||
* @param {Buffer} raw
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
IP.loopback = function loopback(family) {
|
||||
if (!family)
|
||||
family = 'ipv4';
|
||||
IP.isRFC6598 = function isRFC6598(raw) {
|
||||
if (!IP.isIPv4(raw))
|
||||
return false;
|
||||
|
||||
family = family.toLowerCase();
|
||||
if (raw[12] === 100
|
||||
&& (raw[13] >= 64 && raw[13] <= 127)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (family !== 'ipv4' && family !== 'ipv6')
|
||||
throw new Error('Family must be ipv4 or ipv6.');
|
||||
|
||||
return family === 'ipv4' ? '127.0.0.1' : 'fe80::1';
|
||||
return false;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
/**
|
||||
* Test whether the ip is RFC 5737.
|
||||
* @param {Buffer} raw
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
function Address(host, port, version, hostname) {
|
||||
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.isTor = function isTor(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.isTor(raw))
|
||||
return false;
|
||||
|
||||
if (IP.isRFC4843(raw))
|
||||
return false;
|
||||
|
||||
if (IP.isLocal(raw))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether an IP has a prefix.
|
||||
* @param {Buffer} raw
|
||||
* @param {Buffer} prefix
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
IP.hasPrefix = function hasPrefix(raw, prefix) {
|
||||
var i;
|
||||
|
||||
assert(Buffer.isBuffer(raw));
|
||||
assert(Buffer.isBuffer(prefix));
|
||||
assert(raw.length >= prefix.length);
|
||||
|
||||
for (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) {
|
||||
var i;
|
||||
|
||||
assert(a.length === 16);
|
||||
assert(b.length === 16);
|
||||
|
||||
if (a.compare)
|
||||
return a.compare(b) === 0;
|
||||
|
||||
for (i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a parsed address.
|
||||
* @param {String} host
|
||||
* @param {Number} port
|
||||
* @param {Number} version
|
||||
* @param {String} hostname
|
||||
* @param {Buffer} raw
|
||||
*/
|
||||
|
||||
function Address(host, port, version, hostname, raw) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.version = version;
|
||||
this.hostname = hostname;
|
||||
this.raw = raw || ZERO_IP;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user