is-private-host
Version:
Check if a hostname or URL resolves to a private IP address
70 lines (69 loc) • 1.97 kB
JavaScript
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