ip/netaddress: better ip parsing.

This commit is contained in:
Christopher Jeffrey 2017-01-23 19:13:47 -08:00
parent 2d49e38580
commit 016d0da096
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
5 changed files with 536 additions and 170 deletions

View File

@ -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'

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}