UNPKG

subnet-check

Version:

Check if an IPv4 or IPv6 address is contained in the given CIDR subnet

142 lines 5.48 kB
"use strict"; 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