UNPKG

is-private-host

Version:

Check if a hostname or URL resolves to a private IP address

70 lines (69 loc) 1.97 kB
import { lookup } from "node:dns/promises"; import { parse, parseCIDR } from "ipaddr.js"; const PRIVATE_IPV4_RANGES = [ // Unspecified "0.0.0.0/8", // Private Class A "10.0.0.0/8", // CGNAT (Optional) "100.64.0.0/10", // Loopback "127.0.0.0/8", // Link-local "169.254.0.0/16", // Private Class B "172.16.0.0/12", // Private Class C "192.168.0.0/16" ], PRIVATE_IPV6_RANGES = [ // Unspecified "::/128", // Loopback "::1/128", // IPv4-Mapped IPv6 "::ffff:0:0/96", // NAT64 "64:ff9b::/96", // Unique Local Addresses (ULA) "fc00::/7", // Link-local "fe80::/10" ], privateRanges = { ipv4: PRIVATE_IPV4_RANGES, ipv6: PRIVATE_IPV6_RANGES }; function isPrivateIP(ipAddress) { try { const parsedIP = parse(ipAddress); return (parsedIP.kind() === "ipv4" ? privateRanges.ipv4 : privateRanges.ipv6).some((range) => { const subnet = parseCIDR(range); return parsedIP.match(subnet); }); } catch { throw new Error(`Invalid IP address "${ipAddress}"`); } } async function isPrivateHost(hostname, options) { try { const addresses = await lookup(hostname, { // Check both IPv4 and IPv6 records since we don't know which one will be used in the end all: !0 }), isPrivate = addresses.some(({ address }) => isPrivateIP(address)); return options && options.withResolveInfo ? { isPrivate, addresses } : isPrivate; } catch (error) { throw new Error( `Error resolving hostname "${hostname}": ${error instanceof Error ? error.message : error}`, { cause: error } ); } } async function isPrivateUrl(url, options) { const parsedUrl = new URL(url), { isPrivate, addresses } = await isPrivateHost(parsedUrl.hostname, { withResolveInfo: !0 }), resolveInfo = { isPrivate, parsedUrl, addresses }; return options && options.withResolveInfo ? resolveInfo : isPrivate; } export { isPrivateHost, isPrivateIP, isPrivateUrl }; //# sourceMappingURL=index.js.map