UNPKG

@open-condo/miniapp-utils

Version:

A set of helper functions / components / hooks used to build new condo apps fast

421 lines (415 loc) 12.9 kB
var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // src/helpers/ip/ipv4.ts var ipv4_exports = {}; __export(ipv4_exports, { createChecker: () => createChecker, isInSubnet: () => isInSubnet, isLocalhost: () => isLocalhost, isPrivate: () => isPrivate, isReserved: () => isReserved, isSpecial: () => isSpecial }); // src/helpers/ip/ranges.ts var ranges_default = { /** localhost IP ranges */ localhost: { /** the localhost address ranges for IPv4 */ ipv4: ["127.0.0.0/8"], /** the localhost address ranges for IPv6 */ ipv6: ["::1/128"] }, /** private IP ranges */ private: { /** private address ranges for IPv4 */ ipv4: [ "10.0.0.0/8", // RFC 1918 "172.16.0.0/12", // RFC 1918 "192.168.0.0/16" // RFC 1918 ], /** private address ranges for IPv6 */ ipv6: [ "fe80::/10", // link-local address "fc00::/7" // unique local address (ULA) ] }, /** reserved IP ranges */ reserved: { /** reserved address ranges for IPv4 */ ipv4: [ "0.0.0.0/8", // broadcast "this" "100.64.0.0/10", // carrier-grade NAT "169.254.0.0/16", // DHCP fallback "192.0.0.0/24", // IANA Special Purpose Address Registry "192.0.2.0/24", // TEST-NET-1 for documentation examples "192.88.99.0/24", // deprecated 6to4 anycast relays "198.18.0.0/15", // for testing inter-network comms between two subnets "198.51.100.0/24", // TEST-NET-2 for documentation examples "203.0.113.0/24", // TEST-NET-3 for documentation examples "224.0.0.0/4", // multicast "240.0.0.0/4", // reserved unspecified "255.255.255.255/32" // limited broadcast address ], /** reserved address ranges for IPv6 */ ipv6: [ "::/128", // unspecified address "64:ff9b::/96", // IPv4/IPv6 translation "100::/64", // discard prefix "2001::/32", // Teredo tunneling "2001:10::/28", // deprecated "2001:20::/28", // ORCHIDv2 "2001:db8::/32", // for documentation and examples "2002::/16", // 6to4 "ff00::/8" // multicast ] } }; // src/helpers/ip/utils.ts var v4Seg = "(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"; var v4Str = `(${v4Seg}[.]){3}${v4Seg}`; var IPv4Reg = new RegExp(`^${v4Str}$`); var v6Seg = "(?:[0-9a-fA-F]{1,4})"; var IPv6Reg = new RegExp( `^((?:${v6Seg}:){7}(?:${v6Seg}|:)|(?:${v6Seg}:){6}(?:${v4Str}|:${v6Seg}|:)|(?:${v6Seg}:){5}(?::${v4Str}|(:${v6Seg}){1,2}|:)|(?:${v6Seg}:){4}(?:(:${v6Seg}){0,1}:${v4Str}|(:${v6Seg}){1,3}|:)|(?:${v6Seg}:){3}(?:(:${v6Seg}){0,2}:${v4Str}|(:${v6Seg}){1,4}|:)|(?:${v6Seg}:){2}(?:(:${v6Seg}){0,3}:${v4Str}|(:${v6Seg}){1,5}|:)|(?:${v6Seg}:){1}(?:(:${v6Seg}){0,4}:${v4Str}|(:${v6Seg}){1,6}|:)|(?::((?::${v6Seg}){0,5}:${v4Str}|(?::${v6Seg}){1,7}|:)))(%[0-9a-zA-Z]{1,})?$` ); function isIPv4(s) { return IPv4Reg.test(s); } function isIPv6(s) { return IPv6Reg.test(s); } function isIP(s) { if (isIPv4(s)) return 4; if (isIPv6(s)) return 6; return 0; } // src/helpers/ip/ipv4.ts function ipv4ToLong(ip) { if (!isIPv4(ip)) { throw new Error(`not a valid IPv4 address: ${ip}`); } const octets = ip.split("."); return (parseInt(octets[0], 10) << 24) + (parseInt(octets[1], 10) << 16) + (parseInt(octets[2], 10) << 8) + parseInt(octets[3], 10) >>> 0; } function createLongChecker(subnet) { const [subnetAddress, prefixLengthString] = subnet.split("/"); const prefixLength = parseInt(prefixLengthString, 10); if (!subnetAddress || !Number.isInteger(prefixLength)) { throw new Error(`not a valid IPv4 subnet: ${subnet}`); } if (prefixLength < 0 || prefixLength > 32) { throw new Error(`not a valid IPv4 prefix length: ${prefixLength} (from ${subnet})`); } const subnetLong = ipv4ToLong(subnetAddress); return (addressLong) => { if (prefixLength === 0) { return true; } const subnetPrefix = subnetLong >> 32 - prefixLength; const addressPrefix = addressLong >> 32 - prefixLength; return subnetPrefix === addressPrefix; }; } function createChecker(subnetOrSubnets) { if (Array.isArray(subnetOrSubnets)) { const checks = subnetOrSubnets.map((subnet) => createLongChecker(subnet)); return (address) => { const addressLong = ipv4ToLong(address); return checks.some((check3) => check3(addressLong)); }; } const check2 = createLongChecker(subnetOrSubnets); return (address) => { const addressLong = ipv4ToLong(address); return check2(addressLong); }; } function isInSubnet(address, subnetOrSubnets) { return createChecker(subnetOrSubnets)(address); } var specialNetsCache = {}; function isPrivate(address) { if (!("private" in specialNetsCache)) { specialNetsCache["private"] = createChecker(ranges_default.private.ipv4); } return specialNetsCache["private"](address); } function isLocalhost(address) { if (!("localhost" in specialNetsCache)) { specialNetsCache["localhost"] = createChecker(ranges_default.localhost.ipv4); } return specialNetsCache["localhost"](address); } function isReserved(address) { if (!("reserved" in specialNetsCache)) { specialNetsCache["reserved"] = createChecker(ranges_default.reserved.ipv4); } return specialNetsCache["reserved"](address); } function isSpecial(address) { if (!("special" in specialNetsCache)) { specialNetsCache["special"] = createChecker([ ...ranges_default.private.ipv4, ...ranges_default.localhost.ipv4, ...ranges_default.reserved.ipv4 ]); } return specialNetsCache["special"](address); } // src/helpers/ip/ipv6.ts var ipv6_exports = {}; __export(ipv6_exports, { createChecker: () => createChecker2, extractMappedIpv4: () => extractMappedIpv4, isIPv4MappedAddress: () => isIPv4MappedAddress, isInSubnet: () => isInSubnet2, isLocalhost: () => isLocalhost2, isPrivate: () => isPrivate2, isReserved: () => isReserved2, isSpecial: () => isSpecial2 }); var dot = /\./; var mappedIpv4 = /^(.+:ffff:)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:%.+)?$/; var colon = /:/; var doubleColon = /::/; function mappedIpv4ToIpv6(ip) { const matches = ip.match(mappedIpv4); if (!matches || !isIPv4(matches[2])) { throw new Error(`not a mapped IPv4 address: ${ip}`); } const prefix = matches[1]; const ipv4 = matches[2]; const parts = ipv4.split(dot).map((x) => parseInt(x, 10)); const segment7 = ((parts[0] << 8) + parts[1]).toString(16); const segment8 = ((parts[2] << 8) + parts[3]).toString(16); return `${prefix}${segment7}:${segment8}`; } function extractMappedIpv4(ip) { const matches = ip.match(mappedIpv4); if (!matches || !isIPv4(matches[2])) { throw new Error(`not a mapped IPv4 address: ${ip}`); } return matches[2]; } function getIpv6Segments(ip) { if (!isIPv6(ip)) { throw new Error(`not a valid IPv6 address: ${ip}`); } if (dot.test(ip)) { return getIpv6Segments(mappedIpv4ToIpv6(ip)); } const [beforeChunk, afterChunk] = ip.split(doubleColon); const beforeParts = beforeChunk && beforeChunk.split(colon) || []; const afterParts = afterChunk && afterChunk.split(colon) || []; const missingSegments = new Array(8 - (beforeParts.length + afterParts.length)); return beforeParts.concat(missingSegments, afterParts); } function isInSubnet2(address, subnetOrSubnets) { return createChecker2(subnetOrSubnets)(address); } function createChecker2(subnetOrSubnets) { if (Array.isArray(subnetOrSubnets)) { const checks = subnetOrSubnets.map((subnet) => createSegmentChecker(subnet)); return (address) => { const segments = getIpv6Segments(address); return checks.some((check3) => check3(segments)); }; } const check2 = createSegmentChecker(subnetOrSubnets); return (address) => { const segments = getIpv6Segments(address); return check2(segments); }; } function createSegmentChecker(subnet) { const [subnetAddress, prefixLengthString] = subnet.split("/"); const 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})`); } const subnetSegments = getIpv6Segments(subnetAddress); return (addressSegments) => { for (let i = 0; i < 8; ++i) { const bitCount = Math.min(prefixLength - i * 16, 16); if (bitCount <= 0) { break; } const subnetPrefix = (subnetSegments[i] && parseInt(subnetSegments[i], 16) || 0) >> 16 - bitCount; const addressPrefix = (addressSegments[i] && parseInt(addressSegments[i], 16) || 0) >> 16 - bitCount; if (subnetPrefix !== addressPrefix) { return false; } } return true; }; } var specialNetsCache2 = {}; function isPrivate2(address) { if (!("private" in specialNetsCache2)) { specialNetsCache2["private"] = createChecker2(ranges_default.private.ipv6); } return specialNetsCache2["private"](address); } function isLocalhost2(address) { if (!("localhost" in specialNetsCache2)) { specialNetsCache2["localhost"] = createChecker2(ranges_default.localhost.ipv6); } return specialNetsCache2["localhost"](address); } function isIPv4MappedAddress(address) { if (!("mapped" in specialNetsCache2)) { specialNetsCache2["mapped"] = createChecker2("::ffff:0:0/96"); } if (specialNetsCache2["mapped"](address)) { const matches = address.match(mappedIpv4); return Boolean(matches && isIPv4(matches[2])); } return false; } function isReserved2(address) { if (!("reserved" in specialNetsCache2)) { specialNetsCache2["reserved"] = createChecker2(ranges_default.reserved.ipv6); } return specialNetsCache2["reserved"](address); } function isSpecial2(address) { if (!("special" in specialNetsCache2)) { specialNetsCache2["special"] = createChecker2([ ...ranges_default.private.ipv6, ...ranges_default.localhost.ipv6, ...ranges_default.reserved.ipv6 ]); } return specialNetsCache2["special"](address); } // src/helpers/ip/index.ts function isInSubnet3(address, subnetOrSubnets) { return createChecker3(subnetOrSubnets)(address); } function createChecker3(subnetOrSubnets) { if (!Array.isArray(subnetOrSubnets)) { return createChecker3([subnetOrSubnets]); } const subnetsByVersion = subnetOrSubnets.reduce( (acc, subnet) => { const ip = subnet.split("/")[0]; acc[isIP(ip)].push(subnet); return acc; }, { 0: [], 4: [], 6: [] } ); if (subnetsByVersion[0].length !== 0) { throw new Error(`some subnets are not valid IP addresses: ${subnetsByVersion[0]}`); } const check4 = createChecker(subnetsByVersion[4]); const check6 = createChecker2(subnetsByVersion[6]); return (address) => { if (!isIP(address)) { throw new Error(`not a valid IPv4 or IPv6 address: ${address}`); } if (isIPv6(address) && isIPv4MappedAddress(address)) { return check6(address) || check4(extractMappedIpv4(address)); } if (isIPv6(address)) { return check6(address); } else { return check4(address); } }; } function isPrivate3(address) { if (isIPv6(address)) { if (isIPv4MappedAddress(address)) { return isPrivate(extractMappedIpv4(address)); } return isPrivate2(address); } else { return isPrivate(address); } } function isLocalhost3(address) { if (isIPv6(address)) { if (isIPv4MappedAddress(address)) { return isLocalhost(extractMappedIpv4(address)); } return isLocalhost2(address); } else { return isLocalhost(address); } } function isIPv4MappedAddress2(address) { if (isIPv6(address)) { return isIPv4MappedAddress(address); } else { return false; } } function isReserved3(address) { if (isIPv6(address)) { if (isIPv4MappedAddress(address)) { return isReserved(extractMappedIpv4(address)); } return isReserved2(address); } else { return isReserved(address); } } function isSpecial3(address) { if (isIPv6(address)) { if (isIPv4MappedAddress(address)) { return isSpecial(extractMappedIpv4(address)); } return isSpecial2(address); } else { return isSpecial(address); } } var check = isInSubnet3; export { ipv4_exports as IPv4, ipv6_exports as IPv6, check, createChecker3 as createChecker, isIP, isIPv4, isIPv4MappedAddress2 as isIPv4MappedAddress, isIPv6, isInSubnet3 as isInSubnet, isLocalhost3 as isLocalhost, isPrivate3 as isPrivate, isReserved3 as isReserved, isSpecial3 as isSpecial }; //# sourceMappingURL=index.mjs.map