subnet-check
Version:
Check if an IPv4 or IPv6 address is contained in the given CIDR subnet
142 lines • 5.48 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var net = require("./net");
// Note: Profiling shows that on recent versions of Node, string.split(RegExp) is faster
// than string.split(string).
var dot = /\./;
var mappedIpv4 = /^(.+:ffff:)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:%.+)?$/;
var colon = /:/;
var doubleColon = /::/;
/**
* Given a mapped IPv4 address (e.g. ::ffff:203.0.113.38 or similar), convert it to the
* equivalent standard IPv6 address.
* @param ip the IPv4-to-IPv6 mapped address
*/
function mappedIpv4ToIpv6(ip) {
var matches = ip.match(mappedIpv4);
if (!matches || !net.isIPv4(matches[2])) {
throw new Error("not a mapped IPv4 address: " + ip);
}
// mapped IPv4 address
var prefix = matches[1];
var ipv4 = matches[2];
var parts = ipv4.split(dot).map(function (x) { return parseInt(x, 10); });
var segment7 = ((parts[0] << 8) + parts[1]).toString(16);
var segment8 = ((parts[2] << 8) + parts[3]).toString(16);
return "" + prefix + segment7 + ":" + segment8;
}
/**
* Given a mapped IPv4 address, return the bare IPv4 equivalent.
*/
function extractMappedIpv4(ip) {
var matches = ip.match(mappedIpv4);
if (!matches || !net.isIPv4(matches[2])) {
throw new Error("not a mapped IPv4 address: " + ip);
}
return matches[2];
}
exports.extractMappedIpv4 = extractMappedIpv4;
/**
* Given an IP address that may have double-colons, expand all segments and return them
* as an array of 8 segments (16-bit words). As a peformance enhancement (indicated by
* profiling), for any segment that was missing but should be a '0', returns undefined.
* @param ip the IPv6 address to expand
* @throws if the string is not a valid IPv6 address
*/
function getIpv6Segments(ip) {
if (!net.isIPv6(ip)) {
throw new Error("not a valid IPv6 address: " + ip);
}
if (dot.test(ip)) {
return getIpv6Segments(mappedIpv4ToIpv6(ip));
}
// break it into an array, including missing "::" segments
var _a = ip.split(doubleColon), beforeChunk = _a[0], afterChunk = _a[1];
var beforeParts = (beforeChunk && beforeChunk.split(colon)) || [];
var afterParts = (afterChunk && afterChunk.split(colon)) || [];
var missingSegments = new Array(8 - (beforeParts.length + afterParts.length));
return beforeParts.concat(missingSegments, afterParts);
}
/**
* Test if the given IPv6 address is contained in the specified subnet.
* @param address the IPv6 address to check
* @param subnet the IPv6 CIDR to test (or an array of them)
* @throws if the address or subnet are not valid IP addresses, or the CIDR prefix length
* is not valid
*/
function isInSubnet(address, subnetOrSubnets) {
if (Array.isArray(subnetOrSubnets)) {
return subnetOrSubnets.some(function (subnet) { return isInSubnet(address, subnet); });
}
var subnet = subnetOrSubnets;
var _a = subnet.split('/'), subnetAddress = _a[0], prefixLengthString = _a[1];
var prefixLength = parseInt(prefixLengthString, 10);
if (!subnetAddress || !Number.isInteger(prefixLength)) {
throw new Error("not a valid IPv6 CIDR subnet: " + subnet);
}
if (prefixLength < 0 || prefixLength > 128) {
throw new Error("not a valid IPv6 prefix length: " + prefixLength + " (from " + subnet + ")");
}
// the next two lines throw if the addresses are not valid IPv6 addresses
var addressSegments = getIpv6Segments(address);
var subnetSegments = getIpv6Segments(subnetAddress);
for (var i = 0; i < 8; ++i) {
var bitCount = Math.min(prefixLength - i * 16, 16);
if (bitCount <= 0) {
break;
}
var subnetPrefix = ((subnetSegments[i] && parseInt(subnetSegments[i], 16)) || 0) >> (16 - bitCount);
var addressPrefix = ((addressSegments[i] && parseInt(addressSegments[i], 16)) || 0) >> (16 - bitCount);
if (subnetPrefix !== addressPrefix) {
return false;
}
}
return true;
}
exports.isInSubnet = isInSubnet;
/** Test if the given IP address is a private/internal IP address. */
function isPrivate(address) {
return isInSubnet(address, [
'fe80::/10',
'fc00::/7' // unique local address
]);
}
exports.isPrivate = isPrivate;
/** Test if the given IP address is a localhost address. */
function isLocalhost(address) {
return isInSubnet(address, '::1/128');
}
exports.isLocalhost = isLocalhost;
/** Test if the given IP address is an IPv4 address mapped onto IPv6 */
function isIPv4MappedAddress(address) {
if (isInSubnet(address, '::ffff:0:0/96')) {
var matches = address.match(mappedIpv4);
return Boolean(matches && net.isIPv4(matches[2]));
}
return false;
}
exports.isIPv4MappedAddress = isIPv4MappedAddress;
/** Test if the given IP address is in a known reserved range and not a normal host IP */
function isReserved(address) {
return isInSubnet(address, [
'::/128',
'64:ff9b::/96',
'100::/64',
'2001::/32',
'2001:10::/28',
'2001:20::/28',
'2001:db8::/32',
'2002::/16',
'ff00::/8' // multicast
]);
}
exports.isReserved = isReserved;
/**
* Test if the given IP address is a special address of any kind (private, reserved,
* localhost)
*/
function isSpecial(address) {
return isPrivate(address) || isLocalhost(address) || isReserved(address);
}
exports.isSpecial = isSpecial;
//# sourceMappingURL=ipv6.js.map