import { isIP } from "node:net";
const BLOCKED_HOSTNAMES = /* @__PURE__ */ new Set([
  "localhost",
  "ip6-localhost",
  "ip6-loopback",
  "broadcasthost"
]);
function isPrivateIpAddress(address) {
  const family = isIP(address);
  if (family === 4) return isPrivateIPv4(address);
  if (family === 6) return isPrivateIPv6(address);
  return false;
}
function isBlockedHostname(hostname) {
  const normalized = normalizeHostname(hostname);
  if (!normalized) return true;
  if (BLOCKED_HOSTNAMES.has(normalized)) return true;
  if (normalized.endsWith(".localhost")) return true;
  if (normalized.endsWith(".internal")) return true;
  if (normalized.endsWith(".local")) return true;
  return false;
}
function isPrivateUrl(rawUrl) {
  const urlToParse = rawUrl.replace(/\[([0-9a-fA-F:]+)%25[^\]]*\]/g, "[$1]");
  let url;
  try {
    url = new URL(urlToParse);
  } catch {
    return false;
  }
  const hostname = normalizeHostname(url.hostname);
  if (isBlockedHostname(hostname)) return true;
  return isPrivateIpAddress(hostname);
}
function normalizeHostname(hostname) {
  let normalized = hostname.trim().toLowerCase();
  if (normalized.startsWith("[") && normalized.endsWith("]")) {
    normalized = normalized.slice(1, -1);
  }
  while (normalized.endsWith(".")) {
    normalized = normalized.slice(0, -1);
  }
  return normalized;
}
function isPrivateIPv4(address) {
  const parts = address.split(".");
  if (parts.length !== 4) return false;
  const octets = parts.map((part) => Number(part));
  if (octets.some((o) => !Number.isInteger(o) || o < 0 || o > 255)) return false;
  const [a, b, c] = octets;
  if (a === 0) return true;
  if (a === 10) return true;
  if (a === 100 && b >= 64 && b <= 127) return true;
  if (a === 127) return true;
  if (a === 169 && b === 254) return true;
  if (a === 172 && b >= 16 && b <= 31) return true;
  if (a === 192 && b === 0 && (c === 0 || c === 2)) return true;
  if (a === 192 && b === 168) return true;
  if (a === 198 && (b === 18 || b === 19)) return true;
  if (a === 198 && b === 51 && c === 100) return true;
  if (a === 203 && b === 0 && c === 113) return true;
  if (a >= 224) return true;
  return false;
}
function isPrivateIPv6(address) {
  const segments = expandIPv6(address);
  if (!segments) return false;
  if (segments.every((segment) => segment === 0)) return true;
  if (segments.slice(0, 7).every((segment) => segment === 0) && segments[7] === 1) return true;
  if (isIPv4CompatibleIPv6(segments)) return true;
  const embedded = embeddedIPv4FromIPv6(segments);
  if (embedded) return isPrivateIPv4(embedded);
  const [a] = segments;
  if ((a & 65024) === 64512) return true;
  if ((a & 65472) === 65152) return true;
  if ((a & 65280) === 65280) return true;
  if (a === 256 && segments[1] === 0 && segments[2] === 0 && segments[3] === 0) {
    return true;
  }
  if (a === 8193 && (segments[1] & 65024) === 0) {
    return !isGloballyReachableIetfProtocolAssignment(segments);
  }
  if (a === 8193 && segments[1] === 3512) return true;
  if ((a & 65520) === 16368) return true;
  if ((a & 57344) !== 8192) return true;
  return false;
}
function isGloballyReachableIetfProtocolAssignment(segments) {
  const [, b, c, d, e, f, g, h] = segments;
  const trailingZero = c === 0 && d === 0 && e === 0 && f === 0 && g === 0;
  if (b === 1 && trailingZero && h >= 1 && h <= 3) return true;
  if (b === 3) return true;
  if (b === 4 && c === 274) return true;
  if ((b & 65520) === 32) return true;
  if ((b & 65520) === 48) return true;
  return false;
}
function expandIPv6(address) {
  let normalized = address.toLowerCase();
  if (normalized.includes(".")) {
    const lastColon = normalized.lastIndexOf(":");
    if (lastColon === -1) return null;
    const ipv4Segments = ipv4ToHexSegments(normalized.slice(lastColon + 1));
    if (!ipv4Segments) return null;
    normalized = `${normalized.slice(0, lastColon)}:${ipv4Segments[0].toString(16)}:${ipv4Segments[1].toString(16)}`;
  }
  const doubleColonParts = normalized.split("::");
  if (doubleColonParts.length > 2) return null;
  const head = doubleColonParts[0] ? doubleColonParts[0].split(":") : [];
  const tail = doubleColonParts[1] ? doubleColonParts[1].split(":") : [];
  const headSegments = parseIPv6SegmentList(head);
  const tailSegments = parseIPv6SegmentList(tail);
  if (!headSegments || !tailSegments) return null;
  if (doubleColonParts.length === 1) {
    return headSegments.length === 8 ? headSegments : null;
  }
  const missing = 8 - headSegments.length - tailSegments.length;
  if (missing < 1) return null;
  return [
    ...headSegments,
    ...Array.from({ length: missing }, () => 0),
    ...tailSegments
  ];
}
function parseIPv6SegmentList(parts) {
  const segments = [];
  for (const part of parts) {
    if (!/^[0-9a-f]{1,4}$/.test(part)) return null;
    const value = Number.parseInt(part, 16);
    if (!Number.isInteger(value) || value < 0 || value > 65535) return null;
    segments.push(value);
  }
  return segments;
}
function ipv4ToHexSegments(address) {
  const octets = address.split(".").map((part) => Number(part));
  if (octets.length !== 4) return null;
  if (octets.some((octet) => !Number.isInteger(octet) || octet < 0 || octet > 255)) return null;
  return [
    (octets[0] << 8) + octets[1],
    (octets[2] << 8) + octets[3]
  ];
}
function embeddedIPv4FromIPv6(segments) {
  const firstFiveZero = segments.slice(0, 5).every((segment) => segment === 0);
  const isMapped = firstFiveZero && segments[5] === 65535;
  const isNat64 = segments[0] === 100 && segments[1] === 65435 && segments.slice(2, 6).every((segment) => segment === 0);
  const isSixToFour = segments[0] === 8194;
  if (isMapped || isNat64) {
    return ipv4FromHexSegments(segments[6], segments[7]);
  }
  if (isSixToFour) {
    return ipv4FromHexSegments(segments[1], segments[2]);
  }
  return null;
}
function isIPv4CompatibleIPv6(segments) {
  return segments.slice(0, 6).every((segment) => segment === 0);
}
function ipv4FromHexSegments(high, low) {
  return [
    high >> 8 & 255,
    high & 255,
    low >> 8 & 255,
    low & 255
  ].join(".");
}
export {
  isBlockedHostname,
  isPrivateIPv4,
  isPrivateIPv6,
  isPrivateIpAddress,
  isPrivateUrl
};
//# sourceMappingURL=network.js.map
