mod.ts

IPv4 and IPv6 address parsing, stringifying, and CIDR utilities.

Examples

Reject a fetch target that resolves inside your own network

import { assertEquals } from "@std/assert";
import { classifyAddress } from "@hertzg/ip";

// A guard that only checks loopback and link-local misses private, CGNAT,
// and multicast ranges; classification is one label from a closed set, so
// there's no list of booleans to keep in sync
function isSafeToFetch(host: string): boolean {
  const { classification } = classifyAddress(host); // v4, v6, or mapped, one call
  return classification === "public" || classification === "global-unicast";
}

assertEquals(isSafeToFetch("8.8.8.8"), true);
assertEquals(isSafeToFetch("2001:4860:4860::8888"), true);

assertEquals(isSafeToFetch("127.0.0.1"), false);
assertEquals(isSafeToFetch("10.0.0.1"), false);
assertEquals(isSafeToFetch("169.254.169.254"), false); // cloud metadata endpoint
assertEquals(isSafeToFetch("::ffff:127.0.0.1"), false); // mapped, unwrapped first

Trusted Network Allowlist

Check if a client IP is in a set of trusted CIDR blocks

import { assert, assertEquals } from "@std/assert";
import { cidrContains, parseCidr, parseAddress } from "@hertzg/ip";

// The list may mix IP versions; each entry is only ever compared against an
// address of its own version, and a mismatch is a miss rather than an error
const trustedRanges = [
  "10.0.0.0/8",
  "172.16.0.0/12",
  "192.168.0.0/16",
  "fd00::/8",
].map((s) => parseCidr(s));

function isTrusted(ip: string): boolean {
  const address = parseAddress(ip).address;
  return trustedRanges.some((cidr) => cidrContains(cidr, address));
}

assert(isTrusted("192.168.1.100"));
assert(isTrusted("10.0.0.1"));
assert(isTrusted("::ffff:172.16.5.1")); // parseAddress unwrapped this to IPv4 first
assert(isTrusted("fd00::1")); // matched the IPv6 entry, no conversion involved

assertEquals(isTrusted("8.8.8.8"), false);
assertEquals(isTrusted("2001:db8::1"), false);

Dual-Stack Server

Normalize client addresses from a dual-stack server

import { assertEquals } from "@std/assert";
import { classifyAddress, parseAddress, stringifyAddress } from "@hertzg/ip";

// Dual-stack servers (Deno, Node) report IPv4 clients as ::ffff:x.x.x.x
// parseAddress auto-unwraps mapped addresses to their IPv4 form
const remote1 = parseAddress("::ffff:192.168.1.50").address;
assertEquals(stringifyAddress(remote1), "192.168.1.50");

// Native IPv6 clients pass through unchanged
const remote2 = parseAddress("2001:db8::1").address;
assertEquals(stringifyAddress(remote2), "2001:db8::1");

// Classification works on both
assertEquals(classifyAddress(remote1).classification, "private");
assertEquals(classifyAddress(remote2).classification, "documentation");

IP Classification

Classify addresses for logging, analytics, or input validation

import { assertEquals } from "@std/assert";
import { classifyAddress } from "@hertzg/ip";

// Classify any IP — result includes kind, numeric value, and label
const result = classifyAddress("192.168.1.1");
assertEquals(result.kind, "ipv4");
assertEquals(result.classification, "private");

assertEquals(classifyAddress("127.0.0.1").classification, "loopback");
assertEquals(classifyAddress("8.8.8.8").classification, "public");
assertEquals(classifyAddress("169.254.1.1").classification, "link-local");

// Works with IPv6 too
assertEquals(classifyAddress("::1").classification, "loopback");
assertEquals(classifyAddress("fe80::1").classification, "link-local");
assertEquals(classifyAddress("fd00::1").classification, "unique-local");

// Use with Zod as a custom validator that accepts allowed classifications:
//
// import { type Classificationv4, type Classificationv6,
//   classifyAddress } from "@hertzg/ip";
//
// function ipClassification(
//   ...allowed: (Classificationv4 | Classificationv6)[]
// ) {
//   const set = new Set(allowed);
//   return z.string().refine(
//     (val) => set.has(classifyAddress(val).classification),
//     { message: `IP must be: ${allowed.join(", ")}` },
//   );
// }
//
// const publicIp = ipClassification("public", "global-unicast");
// publicIp.parse("8.8.8.8");       // ok
// publicIp.parse("192.168.1.1");   // throws: private
//
// const internalIp = ipClassification("private", "loopback");
// internalIp.parse("10.0.0.1");    // ok
// internalIp.parse("8.8.8.8");     // throws: public

Sorting

Sort a mixed dual-stack list of addresses and CIDR blocks

import { assertEquals } from "@std/assert";
import { compareCidr, compareAddress, parseCidr, parseAddress, stringifyAddress, stringifyCidr } from "@hertzg/ip";

// All IPv4 sorts before all IPv6, numerically ascending within each version
const clients = ["2001:db8::1", "10.0.0.10", "::1", "10.0.0.2"].map((s) => parseAddress(s).address);
assertEquals(clients.toSorted(compareAddress).map(stringifyAddress), [
  "10.0.0.2",
  "10.0.0.10",
  "::1",
  "2001:db8::1",
]);

// CIDR blocks tie-break on prefix length: the larger block comes first
const ranges = ["10.0.0.0/16", "2001:db8::/32", "10.0.0.0/8"].map((s) => parseCidr(s));
assertEquals(ranges.toSorted(compareCidr).map(stringifyCidr), [
  "10.0.0.0/8",
  "10.0.0.0/16",
  "2001:db8::/32",
]);

Parsing and Stringifying

Parse and stringify IPv4 and IPv6 addresses

import { assertEquals } from "@std/assert";
import { parseAddressv4, parseAddressv6, stringifyAddressv4, stringifyAddressv6 } from "@hertzg/ip";

// IPv4: string <-> 32-bit number
const v4 = parseAddressv4("192.168.1.1").address;
assertEquals(v4, 3232235777);
assertEquals(stringifyAddressv4(v4), "192.168.1.1");
assertEquals(stringifyAddressv4(v4 + 1), "192.168.1.2");

// IPv6: string <-> 128-bit bigint
const v6 = parseAddressv6("2001:db8::1").address;
assertEquals(v6, 42540766411282592856903984951653826561n);
assertEquals(stringifyAddressv6(v6), "2001:db8::1");
assertEquals(stringifyAddressv6(v6 + 1n), "2001:db8::2");

CIDR Network Boundaries

Compute network and broadcast addresses from CIDR

import { assertEquals } from "@std/assert";
import {
  cidrv4BroadcastAddress,
  cidrv4NetworkAddress,
  cidrv4Size,
  parseCidrv4,
  stringifyAddressv4,
} from "@hertzg/ip";

const cidr = parseCidrv4("192.168.1.0/24");

assertEquals(stringifyAddressv4(cidrv4NetworkAddress(cidr)), "192.168.1.0");
assertEquals(stringifyAddressv4(cidrv4BroadcastAddress(cidr)), "192.168.1.255");
assertEquals(cidrv4Size(cidr), 256);

Containment Checking

Check if IPs fall within a CIDR block

import { assert, assertEquals } from "@std/assert";
import { cidrv4Contains, parseCidrv4, parseAddressv4 } from "@hertzg/ip";

const cidr = parseCidrv4("10.0.0.0/8");

assert(cidrv4Contains(cidr, parseAddressv4("10.0.0.1").address));
assert(cidrv4Contains(cidr, parseAddressv4("10.255.255.255").address));
assertEquals(cidrv4Contains(cidr, parseAddressv4("11.0.0.0").address), false);

Address Enumeration

Generate addresses in a CIDR block

import { assertEquals } from "@std/assert";
import { cidrv4Addresses, parseCidrv4, stringifyAddressv4 } from "@hertzg/ip";

const cidr = parseCidrv4("10.0.0.0/29"); // 8 addresses

// Iterate all addresses
const all = Array.from(cidrv4Addresses(cidr));
assertEquals(all.map(stringifyAddressv4), [
  "10.0.0.0", "10.0.0.1", "10.0.0.2", "10.0.0.3",
  "10.0.0.4", "10.0.0.5", "10.0.0.6", "10.0.0.7",
]);

// Skip network address, take first 3 usable
const usable = Array.from(cidrv4Addresses(cidr, { offset: 1, count: 3 }));
assertEquals(usable.map(stringifyAddressv4), ["10.0.0.1", "10.0.0.2", "10.0.0.3"]);

Wire Bytes

Decode addresses straight out of a packet buffer

import { assertEquals } from "@std/assert";
import { addressv4FromBytes, addressv4ToBytes, stringifyAddressv4 } from "@hertzg/ip";

// deno-fmt-ignore
const packet = new Uint8Array([
  0x45, 0x00, 0x00, 0x54, 0x1c, 0x46, 0x40, 0x00,
  0x40, 0x06, 0x00, 0x00,
  10, 0, 0, 1,
  192, 168, 1, 1,
]);

// Read the source and destination fields in place
assertEquals(stringifyAddressv4(addressv4FromBytes(packet, 12)), "10.0.0.1");
assertEquals(stringifyAddressv4(addressv4FromBytes(packet, 16)), "192.168.1.1");

// Rewrite the destination in place; the return is only the bytes written
const written = addressv4ToBytes(addressv4FromBytes(packet, 12), packet, 16);
assertEquals(written, new Uint8Array([10, 0, 0, 1]));
assertEquals(stringifyAddressv4(addressv4FromBytes(packet, 16)), "10.0.0.1");

Reverse DNS

The names are relative -- no trailing dot. Append "." if a resolver requires an absolute name.

Build the name a PTR record lives at

import { assertEquals } from "@std/assert";
import { addressToArpa, parseAddress } from "@hertzg/ip";

assertEquals(addressToArpa(parseAddress("192.168.0.1").address), "1.0.168.192.in-addr.arpa");
assertEquals(
  addressToArpa(parseAddress("2001:db8::1").address),
  "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa",
);

API Reference

Universal (auto-detect IPv4/IPv6)

  • Address: An IP address of either version (number or bigint)
  • ParsedAddress: What parseAddress returns, the address plus an optional zone ID
  • ParseOptions: Options for the universal parsers, unmapToV4
  • parseAddress: Parse any IP address string, with an optional zone ID, to number (IPv4) or bigint (IPv6)
  • stringifyAddress: Convert an address, bare or parsed, to IP address string
  • Cidr: A CIDR block of either version, in either dialect (prefix length or mask)
  • ParsedCidr: What parseCidr returns, the block plus an optional zone ID
  • Mask: A network mask of either version (number or bigint)
  • PrefixLength: A prefix length of either version
  • parseCidr: Parse any CIDR notation string, in either dialect and with an optional zone ID
  • stringifyCidr: Convert a Cidr, parse result or address to CIDR notation string, in the dialect it stores
  • cidrSize: Get total number of addresses in a CIDR block
  • cidrFirstAddress: Get the first address of a CIDR block
  • cidrLastAddress: Get the last address of a CIDR block
  • cidrAddresses: Generate IP addresses in a CIDR block
  • cidrContains: Check if a CIDR block contains an address
  • cidrContainsCidr: Check if one CIDR fully contains another
  • cidrOverlaps: Check if two CIDRs share at least one address
  • cidrIntersect: Return the overlapping CIDR block, or null
  • cidrSubtract: Return CIDR blocks in A but not in B
  • cidrMerge: Merge CIDR blocks into the minimal covering set
  • compareAddress: Compare two IP addresses of either version for sorting
  • compareCidr: Compare two CIDR blocks of either version for sorting
  • isValidAddress: Check if a string is a valid plain IP address (IPv4 or IPv6)
  • isValidCidr: Check if a string is valid CIDR notation (IPv4 or IPv6)
  • addressVersion: Report which IP version an address string is written in, or undefined
  • cidrVersion: Report which IP version a CIDR string is written in, or undefined
  • IpVersion: An IP version number, 4 or 6
  • classifyAddress: Classify an IPv4 (number) or IPv6 (bigint) address
  • ClassifiedAddress: Discriminated union result with kind, value, and classification
  • ClassifiedAddressv4: Result type for IPv4 classification
  • ClassifiedAddressv6: Result type for IPv6 classification

Notation

  • splitNotation: Split address[%zoneId][/prefix] into its three slots without reading them
  • Notation: The three slots as slices
  • ZoneId: The zone ID after %, a string

IPv4

  • Addressv4: An IPv4 address as a 32-bit unsigned integer
  • ParsedAddressv4: What parseAddressv4 returns, the address plus an optional zone ID
  • parseAddressv4: Parse dotted decimal notation, with an optional zone ID, to number
  • stringifyAddressv4: Convert an IPv4 address, bare or parsed, to dotted decimal notation
  • compareAddressv4: Compare two IPv4 addresses for sorting
  • isValidAddressv4: Check if a string is a valid IPv4 address

IPv4 CIDR

  • Cidrv4: Type representing an IPv4 CIDR block, PrefixedCidrv4 or MaskedCidrv4
  • Maskv4: An IPv4 network mask as a 32-bit unsigned integer
  • PrefixLengthv4: An IPv4 prefix length, 0 to 32
  • ParsedCidrv4: What parseCidrv4 returns, the block plus an optional zone ID
  • parseCidrv4: Parse IPv4 CIDR notation, in either dialect and with an optional zone ID
  • stringifyCidrv4: Convert a Cidrv4, parse result or address to CIDR notation string, in the dialect it stores
  • cidrv4Mask: Get the network mask of a CIDR block or prefix length (0-32)
  • cidrv4PrefixLength: Get the prefix length of a CIDR block or network mask, as a number or notation string
  • cidrv4Contains: Check if IP is within CIDR block
  • cidrv4ContainsCidr: Check if one IPv4 CIDR fully contains another
  • cidrv4Overlaps: Check if two IPv4 CIDRs share at least one address
  • cidrv4Intersect: Return the overlapping IPv4 CIDR block, or null
  • cidrv4Subtract: Return IPv4 CIDR blocks in A but not in B
  • cidrv4Merge: Merge IPv4 CIDR blocks into the minimal covering set
  • cidrv4FirstAddress: Get first address in CIDR block
  • cidrv4LastAddress: Get last address in CIDR block
  • cidrv4NetworkAddress: Get the network address (first address) of a CIDR block
  • cidrv4BroadcastAddress: Get the directed broadcast address (last address) of a CIDR block
  • cidrv4FirstUsableAddress: Get first assignable address in CIDR block (RFC 3021 aware)
  • cidrv4LastUsableAddress: Get last assignable address in CIDR block (RFC 3021 aware)
  • cidrv4Size: Get total number of addresses in CIDR block
  • cidrv4UsableSize: Get number of assignable addresses in CIDR block
  • cidrv4Addresses: Generate IP addresses in CIDR block
  • cidrv4UsableAddresses: Generate every assignable address in CIDR block
  • compareCidrv4: Compare two IPv4 CIDR blocks for sorting
  • isValidCidrv4: Check if a string is valid IPv4 CIDR notation

IPv6

  • Addressv6: An IPv6 address as a 128-bit unsigned bigint
  • ParsedAddressv6: What parseAddressv6 returns, the address plus an optional zone ID
  • parseAddressv6: Parse colon-hexadecimal notation, with an optional zone ID, to bigint
  • stringifyAddressv6: Convert an IPv6 address, bare or parsed, to compressed colon-hexadecimal
  • stringifyAddressv6Expanded: Convert an IPv6 address, bare or parsed, to full uncompressed colon-hexadecimal
  • compareAddressv6: Compare two IPv6 addresses for sorting
  • isValidAddressv6: Check if a string is a valid IPv6 address

IPv6 CIDR

  • Cidrv6: Type representing an IPv6 CIDR block, PrefixedCidrv6 or MaskedCidrv6
  • Maskv6: An IPv6 network mask as a 128-bit unsigned bigint
  • PrefixLengthv6: An IPv6 prefix length, 0 to 128
  • ParsedCidrv6: What parseCidrv6 returns, the block plus an optional zone ID
  • parseCidrv6: Parse IPv6 CIDR notation, in either dialect and with an optional zone ID
  • stringifyCidrv6: Convert a Cidrv6, parse result or address to CIDR notation string, compressed, in the dialect it stores
  • stringifyCidrv6Expanded: The same with the address written in full
  • cidrv6Mask: Get the network mask of a CIDR block or prefix length (0-128)
  • cidrv6PrefixLength: Get the prefix length of a CIDR block or network mask, as a bigint or notation string
  • cidrv6Contains: Check if IP is within CIDR block
  • cidrv6ContainsCidr: Check if one IPv6 CIDR fully contains another
  • cidrv6Overlaps: Check if two IPv6 CIDRs share at least one address
  • cidrv6Intersect: Return the overlapping IPv6 CIDR block, or null
  • cidrv6Subtract: Return IPv6 CIDR blocks in A but not in B
  • cidrv6Merge: Merge IPv6 CIDR blocks into the minimal covering set
  • cidrv6FirstAddress: Get first address in CIDR block
  • cidrv6LastAddress: Get last address in CIDR block
  • cidrv6Size: Get total number of addresses in CIDR block
  • cidrv6Addresses: Generate IP addresses in CIDR block
  • compareCidrv6: Compare two IPv6 CIDR blocks for sorting
  • isValidCidrv6: Check if a string is valid IPv6 CIDR notation

IPv4 Classification

  • Classificationv4: Type for all IPv4 classification labels
  • classifyAddressv4: Classify an IPv4 address into its well-known range
  • isAddressv4Private: Check if address is private (RFC 1918)
  • isAddressv4Loopback: Check if address is loopback (127.0.0.0/8)
  • isAddressv4LinkLocal: Check if address is link-local (169.254.0.0/16)
  • isAddressv4Multicast: Check if address is multicast (224.0.0.0/4)
  • isAddressv4Reserved: Check if address is reserved (240.0.0.0/4)
  • isAddressv4Broadcast: Check if address is broadcast (255.255.255.255)
  • isAddressv4ThisNetwork: Check if address is "this network" (0.0.0.0/8)
  • isAddressv4CgNat: Check if address is Carrier-Grade NAT (100.64.0.0/10)
  • isAddressv4Benchmarking: Check if address is benchmarking (198.18.0.0/15)
  • isAddressv4Documentation: Check if address is documentation (RFC 5737)
  • isAddressv4Public: Check if address is publicly routable

IPv6 Classification

  • Classificationv6: Type for all IPv6 classification labels
  • classifyAddressv6: Classify an IPv6 address into its well-known range
  • isAddressv6Loopback: Check if address is loopback (::1)
  • isAddressv6Unspecified: Check if address is unspecified (::)
  • isAddressv6LinkLocal: Check if address is link-local (fe80::/10)
  • isAddressv6Multicast: Check if address is multicast (ff00::/8)
  • isAddressv6UniqueLocal: Check if address is unique local (fc00::/7)
  • isAddressv6GlobalUnicast: Check if address is global unicast (2000::/3)
  • isAddressv6Mapped: Check if address is IPv4-mapped (::ffff:0:0/96)
  • isAddressv6Translated: Check if address is IPv4-translated (64:ff9b::/96)
  • isAddressv6Documentation: Check if address is documentation (2001:db8::/32)
  • isAddressv6Teredo: Check if address is Teredo (2001::/32)
  • isAddressv6Benchmarking: Check if address is benchmarking (2001:2::/48)
  • isAddressv6Orchidv2: Check if address is ORCHIDv2 (2001:20::/28)

IPv4-Mapped IPv6 Conversion (addressv6, cidrv6)

  • mapFromAddressv4: Convert IPv4 number to IPv4-mapped IPv6 bigint
  • unmapToAddressv4: Extract IPv4 number from IPv4-mapped IPv6 bigint
  • mapFromCidrv4: Convert IPv4 CIDR to IPv4-mapped IPv6 CIDR
  • unmapToCidrv4: Convert IPv4-mapped IPv6 CIDR to IPv4 CIDR

Universal Wire Byte Conversion (bytes)

  • addressFromBytes: Read a 4- or 16-byte address, version from its length
  • addressToBytes: Write an address as its wire bytes, width from its type

IPv4 Wire Byte Conversion (bytesv4)

  • addressv4FromBytes: Read a 4-byte IPv4 address from a buffer
  • addressv4ToBytes: Write a 4-byte IPv4 address to a buffer

IPv6 Wire Byte Conversion (bytesv6)

  • addressv6FromBytes: Read a 16-byte IPv6 address from a buffer
  • addressv6ToBytes: Write a 16-byte IPv6 address to a buffer

Universal Reverse DNS Pointer Names (arpa)

  • addressToArpa: Build the pointer name of an address of either version

IPv4 Reverse DNS Pointer Names (arpav4)

  • addressv4ToArpa: Build the in-addr.arpa pointer name of an IPv4 address

IPv6 Reverse DNS Pointer Names (arpav6)

  • addressv6ToArpa: Build the ip6.arpa pointer name of an IPv6 address

Submodules

  • notation: The structural layer of notation via splitNotation
  • address: Universal IP parsing via parseAddress, stringifyAddress, compareAddress
  • cidr: Universal CIDR parsing via parseCidr, stringifyCidr, compareCidr
  • addressv4: IPv4 parsing, sorting, and validation
  • cidrv4: IPv4 CIDR utilities, sorting, and validation
  • addressv6: IPv6 parsing, sorting, validation, and IPv4-mapped conversion
  • cidrv6: IPv6 CIDR utilities, sorting, validation, and IPv4-mapped conversion
  • classify: Universal classifier via classifyAddress
  • classifyv4: IPv4 classification via classifyAddressv4, isAddressv4Private, etc.
  • classifyv6: IPv6 classification via classifyAddressv6, isAddressv6Loopback, etc.
  • validate: Universal validation via isValidAddress, isValidCidr
  • version: IP version detection via addressVersion, cidrVersion
  • bytes: Universal wire byte conversion via addressFromBytes, addressToBytes
  • bytesv4: IPv4 wire byte conversion via addressv4FromBytes, addressv4ToBytes
  • bytesv6: IPv6 wire byte conversion via addressv6FromBytes, addressv6ToBytes
  • arpa: Universal reverse DNS pointer names via addressToArpa
  • arpav4: IPv4 reverse DNS pointer names via addressv4ToArpa
  • arpav6: IPv6 reverse DNS pointer names via addressv6ToArpa

Functions

f
addressFromBytes(bytes: Uint8Array): Address

Reads an IPv4 or IPv6 address from a buffer, picking the version from its length.

f
addressToArpa(address: Address): string

Builds the reverse DNS pointer name of an IP address of either version.

f
addressToBytes(
address: Address,
into?: Uint8Array,
offset?: number
): Uint8Array

Writes an IPv4 or IPv6 address, either into a fresh buffer or into one you supply.

f
addressv4FromBytes(
bytes: Uint8Array,
offset?: number
): number

Reads a 4-byte IPv4 address from a buffer.

f
addressv4ToArpa(address: number): string

Builds the reverse DNS pointer name of an IPv4 address.

f
addressv4ToBytes(
address: number,
into?: Uint8Array,
offset?: number
): Uint8Array

Writes a 4-byte IPv4 address, either into a fresh buffer or into one you supply.

f
addressv6FromBytes(
bytes: Uint8Array,
offset?: number
): bigint

Reads a 16-byte IPv6 address from a buffer.

f
addressv6ToArpa(address: bigint): string

Builds the reverse DNS pointer name of an IPv6 address.

f
addressv6ToBytes(
address: bigint,
into?: Uint8Array,
offset?: number
): Uint8Array

Writes a 16-byte IPv6 address, either into a fresh buffer or into one you supply.

f
addressVersion(address: string): IpVersion | undefined

Reports which IP version a plain address string is written in.

f
cidrContains(
cidr: Cidr,
address: Address
): boolean

Checks if a CIDR block contains an address.

f
cidrContainsCidr(
outer: Cidr,
inner: Cidr
): boolean
1 overloads

Checks if one CIDR block fully contains another.

f
cidrFirstAddress(cidr: Cidr): Address
1 overloads

Returns the first address of a CIDR block.

f
cidrIntersect(
a: Cidr,
b: Cidr
): Cidr | null
1 overloads

Returns the intersection of two CIDR blocks.

f
cidrLastAddress(cidr: Cidr): Address
1 overloads

Returns the last address of a CIDR block.

f
cidrMerge(cidrs: readonly Cidr[]): Cidr[]
1 overloads

Merges CIDR blocks into the minimal covering set.

f
cidrOverlaps(
a: Cidr,
b: Cidr
): boolean
1 overloads

Checks if two CIDR blocks overlap (share at least one address).

f
cidrSize(cidr: Cidr): number | bigint
1 overloads

Returns the total number of addresses in a CIDR block.

f
cidrSubtract(
a: Cidr,
b: Cidr
): Cidr[]
1 overloads

Subtracts one CIDR block from another.

f
cidrv4BroadcastAddress(cidr: Cidrv4): number

Returns the directed broadcast address of a CIDR block.

f
cidrv4Contains(
cidr: Cidrv4,
address: number
): boolean

Checks if an IPv4 address is contained within a CIDR block.

f
cidrv4ContainsCidr(
outer: Cidrv4,
inner: Cidrv4
): boolean

Checks if one IPv4 CIDR block fully contains another.

f
cidrv4FirstAddress(cidr: Cidrv4): number

Returns the first address of a CIDR block (network address).

f
cidrv4FirstUsableAddress(cidr: Cidrv4): number

Returns the first assignable address of a CIDR block.

f
cidrv4Intersect(
a: Cidrv4,
b: Cidrv4
): Cidrv4 | null

Returns the intersection of two IPv4 CIDR blocks.

f
cidrv4LastAddress(cidr: Cidrv4): number

Returns the last address of a CIDR block (broadcast address for IPv4).

f
cidrv4LastUsableAddress(cidr: Cidrv4): number

Returns the last assignable address of a CIDR block.

f
cidrv4Mask(cidrOrPrefixLength: Cidrv4 | PrefixLengthv4): Maskv4
3 overloads

Creates a network mask from an IPv4 prefix length.

f
cidrv4Merge(cidrs: readonly Cidrv4[]): Cidrv4[]

Merges IPv4 CIDR blocks into the minimal covering set.

f
cidrv4NetworkAddress(cidr: Cidrv4): number

Returns the network address of a CIDR block.

f
cidrv4Overlaps(
a: Cidrv4,
b: Cidrv4
): boolean

Checks if two IPv4 CIDR blocks overlap (share at least one address).

f
cidrv4PrefixLength(cidrOrMask: Cidrv4 | Maskv4 | string): PrefixLengthv4
4 overloads

Recovers the prefix length from an IPv4 network mask given as a 32-bit unsigned integer.

f
cidrv4Size(cidrOrPrefixLength: Cidrv4 | PrefixLengthv4): number
3 overloads

Returns the total number of IP addresses in a CIDR block.

f
cidrv4Subtract(
a: Cidrv4,
b: Cidrv4
): Cidrv4[]

Subtracts one IPv4 CIDR block from another.

f
cidrv4UsableAddresses(cidr: Cidrv4): Generator<number>

Generates every assignable address in a CIDR block, in ascending order.

f
cidrv4UsableSize(cidrOrPrefixLength: Cidrv4 | PrefixLengthv4): number
3 overloads

Returns the number of assignable addresses in a CIDR block.

f
cidrv6Contains(
cidr: Cidrv6,
address: bigint
): boolean

Checks if an IPv6 address is contained within a CIDR block.

f
cidrv6ContainsCidr(
outer: Cidrv6,
inner: Cidrv6
): boolean

Checks if one IPv6 CIDR block fully contains another.

f
cidrv6FirstAddress(cidr: Cidrv6): bigint

Returns the first address of a CIDR block.

f
cidrv6Intersect(
a: Cidrv6,
b: Cidrv6
): Cidrv6 | null

Returns the intersection of two IPv6 CIDR blocks.

f
cidrv6LastAddress(cidr: Cidrv6): bigint

Returns the last address of a CIDR block.

f
cidrv6Mask(cidrOrPrefixLength: Cidrv6 | PrefixLengthv6): Maskv6
3 overloads

Creates a network mask from an IPv6 prefix length.

f
cidrv6Merge(cidrs: readonly Cidrv6[]): Cidrv6[]

Merges IPv6 CIDR blocks into the minimal covering set.

f
cidrv6Overlaps(
a: Cidrv6,
b: Cidrv6
): boolean

Checks if two IPv6 CIDR blocks overlap (share at least one address).

f
cidrv6PrefixLength(cidrOrMask: Cidrv6 | Maskv6 | string): PrefixLengthv6
4 overloads

Recovers the prefix length from an IPv6 network mask given as a bigint.

f
cidrv6Size(cidrOrPrefixLength: Cidrv6 | PrefixLengthv6): bigint
3 overloads

Returns the total number of IP addresses in a CIDR block.

f
cidrv6Subtract(
a: Cidrv6,
b: Cidrv6
): Cidrv6[]

Subtracts one IPv6 CIDR block from another.

f
cidrVersion(cidr: string): IpVersion | undefined

Reports which IP version a CIDR notation string is written in.

f
classifyAddress(address: Address | string): ClassifiedAddress
4 overloads

Classifies an IPv4 address into its well-known range.

f
classifyAddressv4(address: number): Classificationv4

Classifies an IPv4 address into its well-known range.

f
classifyAddressv6(address: bigint): Classificationv6

Classifies an IPv6 address into its well-known range.

f
compareAddress(
a: Address,
b: Address
): -1 | 0 | 1

Compares two IP addresses of either version for sorting.

f
compareAddressv4(
a: Addressv4,
b: Addressv4
): -1 | 0 | 1

Compares two IPv4 addresses for sorting, numerically ascending.

f
compareAddressv6(
a: Addressv6,
b: Addressv6
): -1 | 0 | 1

Compares two IPv6 addresses for sorting, numerically ascending.

f
compareCidr(
a: Cidr,
b: Cidr
): -1 | 0 | 1

Compares two CIDR blocks of either version for sorting.

f
compareCidrv4(
a: Cidrv4,
b: Cidrv4
): -1 | 0 | 1

Compares two IPv4 CIDR blocks for sorting.

f
compareCidrv6(
a: Cidrv6,
b: Cidrv6
): -1 | 0 | 1

Compares two IPv6 CIDR blocks for sorting.

f
isAddressv4Benchmarking(address: number): boolean

Checks if an IPv4 address is in the benchmarking range (RFC 2544).

f
isAddressv4Broadcast(address: number): boolean

Checks if an IPv4 address is the limited broadcast address.

f
isAddressv4CgNat(address: number): boolean

Checks if an IPv4 address is in the Carrier-Grade NAT range (RFC 6598).

f
isAddressv4Documentation(address: number): boolean

Checks if an IPv4 address is in a documentation range (RFC 5737).

f
isAddressv4LinkLocal(address: number): boolean

Checks if an IPv4 address is a link-local address (RFC 3927).

f
isAddressv4Loopback(address: number): boolean

Checks if an IPv4 address is a loopback address (RFC 1122).

f
isAddressv4Multicast(address: number): boolean

Checks if an IPv4 address is a multicast address (RFC 5771).

f
isAddressv4Private(address: number): boolean

Checks if an IPv4 address is in a private range (RFC 1918).

f
isAddressv4Public(address: number): boolean

Checks if an IPv4 address is a public (globally routable) address.

f
isAddressv4Reserved(address: number): boolean

Checks if an IPv4 address is in the reserved range (RFC 1112).

f
isAddressv4ThisNetwork(address: number): boolean

Checks if an IPv4 address is in the "this network" range (RFC 791).

f
isAddressv6Benchmarking(address: bigint): boolean

Checks if an IPv6 address is in the benchmarking range (RFC 5180).

f
isAddressv6Documentation(address: bigint): boolean

Checks if an IPv6 address is in the documentation range (RFC 3849).

f
isAddressv6GlobalUnicast(address: bigint): boolean

Checks if an IPv6 address is a global unicast address (RFC 4291).

f
isAddressv6LinkLocal(address: bigint): boolean

Checks if an IPv6 address is a link-local address (RFC 4291).

f
isAddressv6Loopback(address: bigint): boolean

Checks if an IPv6 address is the loopback address (RFC 4291).

f
isAddressv6Mapped(address: bigint): boolean

Checks if an IPv6 address is an IPv4-mapped address (RFC 4291).

f
isAddressv6Multicast(address: bigint): boolean

Checks if an IPv6 address is a multicast address (RFC 4291).

f
isAddressv6Orchidv2(address: bigint): boolean

Checks if an IPv6 address is an ORCHIDv2 address (RFC 7343).

f
isAddressv6Teredo(address: bigint): boolean

Checks if an IPv6 address is a Teredo address (RFC 4380).

f
isAddressv6Translated(address: bigint): boolean

Checks if an IPv6 address is an IPv4-translated address (RFC 6052).

f
isAddressv6UniqueLocal(address: bigint): boolean

Checks if an IPv6 address is a unique local address (RFC 4193).

f
isAddressv6Unspecified(address: bigint): boolean

Checks if an IPv6 address is the unspecified address (RFC 4291).

f
isCidrv4(cidr: Cidr): cidr is Cidrv4

Type guard that checks whether a Cidr is an IPv4 CIDR block.

f
isCidrv6(cidr: Cidr): cidr is Cidrv6

Type guard that checks whether a Cidr is an IPv6 CIDR block.

f
isValidAddress(address: string): boolean

Checks if a string is a valid plain IP address (IPv4 or IPv6).

f
isValidAddressv4(address: string): boolean

Checks if a string is a valid IPv4 address in dotted decimal notation.

f
isValidAddressv6(address: string): boolean

Checks if a string is a valid IPv6 address in colon-hexadecimal notation.

f
isValidCidr(cidr: string): boolean

Checks if a string is valid IPv4 or IPv6 CIDR notation.

f
isValidCidrv4(cidr: string): boolean

Checks if a string is valid IPv4 CIDR notation.

f
isValidCidrv6(cidr: string): boolean

Checks if a string is valid IPv6 CIDR notation.

f
mapFromAddressv4(address: Addressv4): Addressv6

Converts an IPv4 address to its IPv4-mapped IPv6 representation.

f
mapFromCidrv4(cidr: Cidrv4): Cidrv6
3 overloads

Converts an IPv4 CIDR block with a prefix length to its IPv4-mapped IPv6 CIDR representation.

f
parseAddress(
address: string,
options?: ParseOptions
): ParsedAddress

Parses an IPv4 or IPv6 address string, with an optional zone ID, to its numeric value.

f
parseAddressv4(address: string): ParsedAddressv4

Parses an IPv4 address in dotted decimal notation, with an optional zone ID, to its numeric value.

f
parseAddressv6(address: string): ParsedAddressv6

Parses an IPv6 address in colon-hexadecimal notation, with an optional zone ID, to its numeric value.

f
parseCidr(
cidr: string,
options?: ParseOptions
): ParsedCidr

Parses IPv4 or IPv6 CIDR notation, in either dialect and with an optional zone ID.

f
parseCidrv4(cidr: string): ParsedCidrv4

Parses IPv4 CIDR notation, in either dialect and with an optional zone ID, to a ParsedCidrv4.

f
parseCidrv6(cidr: string): ParsedCidrv6

Parses IPv6 CIDR notation, in either dialect and with an optional zone ID, to a ParsedCidrv6.

f
splitNotation(notation: string): Notation

Splits an IP notation string into its address, zone ID and prefix slots.

f
stringifyAddress(address: Address | ParsedAddress): string

Stringifies an IPv4 (number) or IPv6 (bigint) address, bare or parsed, to its standard notation.

f
stringifyAddressv4(address: Addressv4 | ParsedAddressv4): string

Stringifies an IPv4 address to dotted decimal notation.

f
stringifyAddressv6(address: Addressv6 | ParsedAddressv6): string

Stringifies an IPv6 address to compressed colon-hexadecimal notation.

f
stringifyAddressv6Expanded(address: Addressv6 | ParsedAddressv6): string

Stringifies an IPv6 address to full uncompressed colon-hexadecimal notation.

f
stringifyCidr(cidr: Address | ParsedAddress | ParsedCidr): string

Stringifies a CIDR block of either version, or an address, to CIDR notation.

f
stringifyCidrv4(cidr: Addressv4 | ParsedAddressv4 | ParsedCidrv4): string

Stringifies an IPv4 CIDR block, or an address, to CIDR notation.

f
stringifyCidrv6(cidr: Addressv6 | ParsedAddressv6 | ParsedCidrv6): string

Stringifies an IPv6 CIDR block, or an address, to CIDR notation with the address compressed.

f
stringifyCidrv6Expanded(cidr: Addressv6 | ParsedAddressv6 | ParsedCidrv6): string

Stringifies an IPv6 CIDR block, or an address, to CIDR notation with the address written in full uncompressed colon-hexadecimal.

f
unmapToAddressv4(address: Addressv6): Addressv4

Extracts the IPv4 address from an IPv4-mapped IPv6 address.

f
unmapToCidrv4(cidr: Cidrv6): Cidrv4
3 overloads

Converts an IPv4-mapped IPv6 CIDR block with a prefix length to its IPv4 CIDR representation.

Type Aliases

T
Address = Addressv4 | Addressv6

A plain IP address of either IP version.

T
Addressv4 = number

An IPv4 address as a 32-bit unsigned integer, 0 to 4294967295. The primitive type is what carries the version: a number is IPv4, a bigint is IPv6 (ADR 0001).

T
Addressv6 = bigint

An IPv6 address as a 128-bit unsigned bigint, 0n to 2n ** 128n - 1n. The primitive type is what carries the version: a bigint is IPv6, a number is IPv4 (ADR 0001).

T
Cidr = Cidrv4 | Cidrv6

A CIDR block of either IP version.

T
Cidrv4 = PrefixedCidrv4 | MaskedCidrv4

Represents an IPv4 CIDR block.

T
Cidrv6 = PrefixedCidrv6 | MaskedCidrv6

Represents an IPv6 CIDR block.

T
ClassifiedAddress = ClassifiedAddressv4 | ClassifiedAddressv6

Result of classifying an IP address with version information and parsed value.

T
IpVersion = 4 | 6

An IP version number: 4 for IPv4, 6 for IPv6.

T
Mask = Maskv4 | Maskv6

A network mask of either IP version: a number for IPv4, a bigint for IPv6, the same split as Address.

T
MaskedCidrv4 = { readonly address: Addressv4; readonly mask: Maskv4; readonly prefixLength?: never; }

An IPv4 CIDR block written with a network mask, as in 10.0.0.0/255.0.0.0.

T
MaskedCidrv6 = { readonly address: Addressv6; readonly mask: Maskv6; readonly prefixLength?: never; }

An IPv6 CIDR block written with a network mask, as in 2001:db8::/ffff:ffff::.

T
Maskv4 = number

An IPv4 network mask as a 32-bit unsigned integer, e.g. 0xFFFFFF00 for /24.

T
Maskv6 = bigint

An IPv6 network mask as a 128-bit unsigned bigint, e.g. 0xFFFFFFFFFFFFFFFF0000000000000000n for /64.

T
Notation = { readonly address: string; readonly zoneId?: ZoneId; readonly prefix?: string; }

The three slots of an IP notation string, as slices of it, before any of them is read. Absent slots are absent, not empty: splitNotation rejects an empty slot rather than returning "".

T
ParsedAddress = ParsedAddressv4 | ParsedAddressv6

What parseAddress returns and what stringifyAddress accepts: a ParsedAddressv4 or a ParsedAddressv6. Read .address for the bare Address and narrow with typeof; the zone never touches the value, and no operation in this package reads it.

T
ParsedAddressv4 = { readonly address: Addressv4; readonly zoneId?: ZoneId; }

What parseAddressv4 returns and what stringifyAddressv4 accepts: the address, plus the zone ID if the notation had one. Read .address for the bare Addressv4; the zone never touches the value, and no operation in this package reads it.

T
ParsedAddressv6 = { readonly address: Addressv6; readonly zoneId?: ZoneId; }

What parseAddressv6 returns and what stringifyAddressv6 accepts: the address, plus the zone ID if the notation had one. Read .address for the bare Addressv6; the zone never touches the value, and no operation in this package reads it.

T
ParsedCidr = ParsedCidrv4 | ParsedCidrv6

What parseCidr returns and what stringifyCidr accepts: a ParsedCidrv4 or a ParsedCidrv6, the block in the dialect it was written in plus an optional zone ID. Assignable to Cidr, so a parse result goes straight into every cidr* operation; none of them reads the zone.

T
ParsedCidrv4 = Cidrv4 & { readonly zoneId?: ZoneId; }

What parseCidrv4 returns and what stringifyCidrv4 accepts: a Cidrv4 in the dialect it was written in, plus the zone ID if the notation had one (fe80::%ether1/64 has one; RouterOS emits that form for connected routes). Assignable to Cidrv4, so a parse result goes straight into every cidrv4* operation; none of them reads the zone.

T
ParsedCidrv6 = Cidrv6 & { readonly zoneId?: ZoneId; }

What parseCidrv6 returns and what stringifyCidrv6 accepts: a Cidrv6 in the dialect it was written in, plus the zone ID if the notation had one (fe80::%ether1/64, the form RouterOS and netstat -rn emit for link-local routes). Assignable to Cidrv6, so a parse result goes straight into every cidrv6* operation; none of them reads the zone.

T
ParseOptions = { readonly unmapToV4?: boolean; }

Options for the universal parsers, parseAddress and parseCidr. The version-specific parsers take none: they return their own version and nothing else.

  • unmapToV4: boolean

    Whether an IPv4-mapped IPv6 address (::ffff:a.b.c.d) is returned as the IPv4 value it carries. Defaults to true (ADR 0004): dual-stack listeners report IPv4 clients in the mapped form, and almost every caller wants the IPv4 view. Set to false to keep the bigint.

T
PrefixedCidrv4 = { readonly address: Addressv4; readonly prefixLength: PrefixLengthv4; readonly mask?: never; }

An IPv4 CIDR block written with a prefix length, as in 10.0.0.0/8.

T
PrefixedCidrv6 = { readonly address: Addressv6; readonly prefixLength: PrefixLengthv6; readonly mask?: never; }

An IPv6 CIDR block written with a prefix length, as in 2001:db8::/32.

T
PrefixLength = number

A prefix length of either IP version. Being a bare number it cannot say which version it belongs to, which is why there is no universal mask-from-prefix-length function: 24 is /24 in both, and the masks differ.

T
PrefixLengthv4 = number

An IPv4 prefix length, the 24 in /24. The range is 0 to 32; it is documented rather than encoded in the type, so prefixLength + 1 stays a PrefixLengthv4 (ADR 0002).

T
PrefixLengthv6 = number

An IPv6 prefix length, the 64 in /64. The range is 0 to 128; it is documented rather than encoded in the type, so prefixLength + 1 stays a PrefixLengthv6 (ADR 0002).

T
ZoneId = string

The zone ID of an address, the interface tail after % in fe80::1%eth0 (RFC 4007 section 11). Carried verbatim by the Parsed* types; never percent-decoded, so %25eth0 is the zone 25eth0.

notation.ts

The structural layer of IP notation: splitting a string into its address, zone ID and prefix slots without reading any of them.

Examples

Splitting the three slots

import { assertEquals } from "@std/assert";
import { splitNotation } from "@hertzg/ip/notation";

assertEquals(splitNotation("fe80::%ether1/64"), {
  address: "fe80::",
  zoneId: "ether1",
  prefix: "64",
});
assertEquals(splitNotation("10.0.0.0/255.0.0.0"), {
  address: "10.0.0.0",
  prefix: "255.0.0.0",
});
assertEquals(splitNotation("192.168.1.1%ether1"), {
  address: "192.168.1.1",
  zoneId: "ether1",
});

Functions

f
splitNotation(notation: string): Notation

Splits an IP notation string into its address, zone ID and prefix slots.

Type Aliases

T
Notation = { readonly address: string; readonly zoneId?: ZoneId; readonly prefix?: string; }

The three slots of an IP notation string, as slices of it, before any of them is read. Absent slots are absent, not empty: splitNotation rejects an empty slot rather than returning "".

T
ZoneId = string

The zone ID of an address, the interface tail after % in fe80::1%eth0 (RFC 4007 section 11). Carried verbatim by the Parsed* types; never percent-decoded, so %25eth0 is the zone 25eth0.

address.ts

Universal IP address parsing and stringifying.

Examples

Parse and stringify any IP address

import { assertEquals } from "@std/assert";
import { parseAddress, stringifyAddress } from "@hertzg/ip/address";

// IPv4
const v4 = parseAddress("192.168.1.1");
assertEquals(v4, { address: 3232235777 });
assertEquals(stringifyAddress(v4), "192.168.1.1");

// IPv6
const v6 = parseAddress("2001:db8::1");
assertEquals(v6, { address: 42540766411282592856903984951653826561n });
assertEquals(stringifyAddress(v6), "2001:db8::1");

// A zone ID rides along
const linkLocal = parseAddress("fe80::1%eth0");
assertEquals(linkLocal, { address: 0xfe800000000000000000000000000001n, zoneId: "eth0" });
assertEquals(stringifyAddress(linkLocal), "fe80::1%eth0");

Functions

f
compareAddress(
a: Address,
b: Address
): -1 | 0 | 1

Compares two IP addresses of either version for sorting.

f
parseAddress(
address: string,
options?: ParseOptions
): ParsedAddress

Parses an IPv4 or IPv6 address string, with an optional zone ID, to its numeric value.

f
stringifyAddress(address: Address | ParsedAddress): string

Stringifies an IPv4 (number) or IPv6 (bigint) address, bare or parsed, to its standard notation.

Type Aliases

T
Address = Addressv4 | Addressv6

A plain IP address of either IP version.

T
Addressv4 = number

An IPv4 address as a 32-bit unsigned integer, 0 to 4294967295. The primitive type is what carries the version: a number is IPv4, a bigint is IPv6 (ADR 0001).

T
Addressv6 = bigint

An IPv6 address as a 128-bit unsigned bigint, 0n to 2n ** 128n - 1n. The primitive type is what carries the version: a bigint is IPv6, a number is IPv4 (ADR 0001).

T
ParsedAddress = ParsedAddressv4 | ParsedAddressv6

What parseAddress returns and what stringifyAddress accepts: a ParsedAddressv4 or a ParsedAddressv6. Read .address for the bare Address and narrow with typeof; the zone never touches the value, and no operation in this package reads it.

T
ParsedAddressv4 = { readonly address: Addressv4; readonly zoneId?: ZoneId; }

What parseAddressv4 returns and what stringifyAddressv4 accepts: the address, plus the zone ID if the notation had one. Read .address for the bare Addressv4; the zone never touches the value, and no operation in this package reads it.

T
ParsedAddressv6 = { readonly address: Addressv6; readonly zoneId?: ZoneId; }

What parseAddressv6 returns and what stringifyAddressv6 accepts: the address, plus the zone ID if the notation had one. Read .address for the bare Addressv6; the zone never touches the value, and no operation in this package reads it.

T
ParseOptions = { readonly unmapToV4?: boolean; }

Options for the universal parsers, parseAddress and parseCidr. The version-specific parsers take none: they return their own version and nothing else.

  • unmapToV4: boolean

    Whether an IPv4-mapped IPv6 address (::ffff:a.b.c.d) is returned as the IPv4 value it carries. Defaults to true (ADR 0004): dual-stack listeners report IPv4 clients in the mapped form, and almost every caller wants the IPv4 view. Set to false to keep the bigint.

T
ZoneId = string

The zone ID of an address, the interface tail after % in fe80::1%eth0 (RFC 4007 section 11). Carried verbatim by the Parsed* types; never percent-decoded, so %25eth0 is the zone 25eth0.

addressv4.ts

IPv4 address parsing and stringifying utilities.

Examples

Basic IPv4 operations

import { assertEquals } from "@std/assert";
import { parseAddressv4, stringifyAddressv4 } from "@hertzg/ip/addressv4";

const { address } = parseAddressv4("192.168.1.1");
assertEquals(address, 3232235777);

const next = address + 1;
assertEquals(stringifyAddressv4(next), "192.168.1.2");

Zone IDs are carried, not applied

import { assertEquals } from "@std/assert";
import { parseAddressv4, stringifyAddressv4 } from "@hertzg/ip/addressv4";

const gateway = parseAddressv4("10.155.101.1%ether1");
assertEquals(gateway, { address: 177956097, zoneId: "ether1" });
assertEquals(stringifyAddressv4(gateway), "10.155.101.1%ether1");
assertEquals(stringifyAddressv4(gateway.address), "10.155.101.1");

Bitwise operations on IPv4 addresses

Since IPv4 addresses are plain numbers, you can use standard JavaScript bitwise operators directly instead of library functions. Use >>> 0 to keep results as unsigned 32-bit integers.

import { assertEquals } from "@std/assert";
import { parseAddressv4, stringifyAddressv4 } from "@hertzg/ip/addressv4";
import { cidrv4Mask } from "@hertzg/ip/cidrv4";

const ip = parseAddressv4("192.168.1.100").address;
const mask = cidrv4Mask(24);

// Bitwise NOT (invert all bits)
const inverted = (~ip >>> 0);
assertEquals(stringifyAddressv4(inverted), "63.87.254.155");

// Bitwise AND (apply network mask to get network address)
const network = ((ip & mask) >>> 0);
assertEquals(stringifyAddressv4(network), "192.168.1.0");

// Bitwise OR (combine network with host bits for broadcast)
const broadcast = ((network | (~mask >>> 0)) >>> 0);
assertEquals(stringifyAddressv4(broadcast), "192.168.1.255");

// Direct comparison (no isEqual() needed)
assertEquals(parseAddressv4("10.0.0.1").address === parseAddressv4("10.0.0.1").address, true);
assertEquals(parseAddressv4("10.0.0.1").address === parseAddressv4("10.0.0.2").address, false);

Functions

f
compareAddressv4(
a: Addressv4,
b: Addressv4
): -1 | 0 | 1

Compares two IPv4 addresses for sorting, numerically ascending.

f
parseAddressv4(address: string): ParsedAddressv4

Parses an IPv4 address in dotted decimal notation, with an optional zone ID, to its numeric value.

f
stringifyAddressv4(address: Addressv4 | ParsedAddressv4): string

Stringifies an IPv4 address to dotted decimal notation.

Type Aliases

T
Addressv4 = number

An IPv4 address as a 32-bit unsigned integer, 0 to 4294967295. The primitive type is what carries the version: a number is IPv4, a bigint is IPv6 (ADR 0001).

T
ParsedAddressv4 = { readonly address: Addressv4; readonly zoneId?: ZoneId; }

What parseAddressv4 returns and what stringifyAddressv4 accepts: the address, plus the zone ID if the notation had one. Read .address for the bare Addressv4; the zone never touches the value, and no operation in this package reads it.

T
ZoneId = string

The zone ID of an address, the interface tail after % in fe80::1%eth0 (RFC 4007 section 11). Carried verbatim by the Parsed* types; never percent-decoded, so %25eth0 is the zone 25eth0.

cidr.ts

Universal CIDR notation parsing, stringifying, and validation.

Examples

Parse and stringify any CIDR block

import { assertEquals } from "@std/assert";
import { parseCidr, stringifyCidr } from "@hertzg/ip/cidr";

// IPv4
const v4 = parseCidr("192.168.1.0/24");
assertEquals(v4, { address: 3232235776, prefixLength: 24 });
assertEquals(stringifyCidr(v4), "192.168.1.0/24");

// IPv6, with a zone ID
const v6 = parseCidr("fe80::%ether1/64");
assertEquals(v6, { address: 0xfe80n << 112n, prefixLength: 64, zoneId: "ether1" });
assertEquals(stringifyCidr(v6), "fe80::%ether1/64");

// The mask dialect is kept as written
const masked = parseCidr("10.0.0.0/255.0.0.0");
assertEquals(masked, { address: 167772160, mask: 0xFF000000 });
assertEquals(stringifyCidr(masked), "10.0.0.0/255.0.0.0");

Functions

f
cidrContains(
cidr: Cidr,
address: Address
): boolean

Checks if a CIDR block contains an address.

f
cidrContainsCidr(
outer: Cidr,
inner: Cidr
): boolean
1 overloads

Checks if one CIDR block fully contains another.

f
cidrFirstAddress(cidr: Cidr): Address
1 overloads

Returns the first address of a CIDR block.

f
cidrIntersect(
a: Cidr,
b: Cidr
): Cidr | null
1 overloads

Returns the intersection of two CIDR blocks.

f
cidrLastAddress(cidr: Cidr): Address
1 overloads

Returns the last address of a CIDR block.

f
cidrMerge(cidrs: readonly Cidr[]): Cidr[]
1 overloads

Merges CIDR blocks into the minimal covering set.

f
cidrOverlaps(
a: Cidr,
b: Cidr
): boolean
1 overloads

Checks if two CIDR blocks overlap (share at least one address).

f
cidrSize(cidr: Cidr): number | bigint
1 overloads

Returns the total number of addresses in a CIDR block.

f
cidrSubtract(
a: Cidr,
b: Cidr
): Cidr[]
1 overloads

Subtracts one CIDR block from another.

f
compareCidr(
a: Cidr,
b: Cidr
): -1 | 0 | 1

Compares two CIDR blocks of either version for sorting.

f
isCidrv4(cidr: Cidr): cidr is Cidrv4

Type guard that checks whether a Cidr is an IPv4 CIDR block.

f
isCidrv6(cidr: Cidr): cidr is Cidrv6

Type guard that checks whether a Cidr is an IPv6 CIDR block.

f
parseCidr(
cidr: string,
options?: ParseOptions
): ParsedCidr

Parses IPv4 or IPv6 CIDR notation, in either dialect and with an optional zone ID.

f
stringifyCidr(cidr: Address | ParsedAddress | ParsedCidr): string

Stringifies a CIDR block of either version, or an address, to CIDR notation.

Type Aliases

T
Address = Addressv4 | Addressv6

A plain IP address of either IP version.

T
Addressv4 = number

An IPv4 address as a 32-bit unsigned integer, 0 to 4294967295. The primitive type is what carries the version: a number is IPv4, a bigint is IPv6 (ADR 0001).

T
Addressv6 = bigint

An IPv6 address as a 128-bit unsigned bigint, 0n to 2n ** 128n - 1n. The primitive type is what carries the version: a bigint is IPv6, a number is IPv4 (ADR 0001).

T
Cidr = Cidrv4 | Cidrv6

A CIDR block of either IP version.

T
Cidrv4 = PrefixedCidrv4 | MaskedCidrv4

Represents an IPv4 CIDR block.

T
Cidrv6 = PrefixedCidrv6 | MaskedCidrv6

Represents an IPv6 CIDR block.

T
Mask = Maskv4 | Maskv6

A network mask of either IP version: a number for IPv4, a bigint for IPv6, the same split as Address.

T
MaskedCidrv4 = { readonly address: Addressv4; readonly mask: Maskv4; readonly prefixLength?: never; }

An IPv4 CIDR block written with a network mask, as in 10.0.0.0/255.0.0.0.

T
MaskedCidrv6 = { readonly address: Addressv6; readonly mask: Maskv6; readonly prefixLength?: never; }

An IPv6 CIDR block written with a network mask, as in 2001:db8::/ffff:ffff::.

T
Maskv4 = number

An IPv4 network mask as a 32-bit unsigned integer, e.g. 0xFFFFFF00 for /24.

T
Maskv6 = bigint

An IPv6 network mask as a 128-bit unsigned bigint, e.g. 0xFFFFFFFFFFFFFFFF0000000000000000n for /64.

T
ParsedAddress = ParsedAddressv4 | ParsedAddressv6

What parseAddress returns and what stringifyAddress accepts: a ParsedAddressv4 or a ParsedAddressv6. Read .address for the bare Address and narrow with typeof; the zone never touches the value, and no operation in this package reads it.

T
ParsedAddressv4 = { readonly address: Addressv4; readonly zoneId?: ZoneId; }

What parseAddressv4 returns and what stringifyAddressv4 accepts: the address, plus the zone ID if the notation had one. Read .address for the bare Addressv4; the zone never touches the value, and no operation in this package reads it.

T
ParsedAddressv6 = { readonly address: Addressv6; readonly zoneId?: ZoneId; }

What parseAddressv6 returns and what stringifyAddressv6 accepts: the address, plus the zone ID if the notation had one. Read .address for the bare Addressv6; the zone never touches the value, and no operation in this package reads it.

T
ParsedCidr = ParsedCidrv4 | ParsedCidrv6

What parseCidr returns and what stringifyCidr accepts: a ParsedCidrv4 or a ParsedCidrv6, the block in the dialect it was written in plus an optional zone ID. Assignable to Cidr, so a parse result goes straight into every cidr* operation; none of them reads the zone.

T
ParsedCidrv4 = Cidrv4 & { readonly zoneId?: ZoneId; }

What parseCidrv4 returns and what stringifyCidrv4 accepts: a Cidrv4 in the dialect it was written in, plus the zone ID if the notation had one (fe80::%ether1/64 has one; RouterOS emits that form for connected routes). Assignable to Cidrv4, so a parse result goes straight into every cidrv4* operation; none of them reads the zone.

T
ParsedCidrv6 = Cidrv6 & { readonly zoneId?: ZoneId; }

What parseCidrv6 returns and what stringifyCidrv6 accepts: a Cidrv6 in the dialect it was written in, plus the zone ID if the notation had one (fe80::%ether1/64, the form RouterOS and netstat -rn emit for link-local routes). Assignable to Cidrv6, so a parse result goes straight into every cidrv6* operation; none of them reads the zone.

T
ParseOptions = { readonly unmapToV4?: boolean; }

Options for the universal parsers, parseAddress and parseCidr. The version-specific parsers take none: they return their own version and nothing else.

  • unmapToV4: boolean

    Whether an IPv4-mapped IPv6 address (::ffff:a.b.c.d) is returned as the IPv4 value it carries. Defaults to true (ADR 0004): dual-stack listeners report IPv4 clients in the mapped form, and almost every caller wants the IPv4 view. Set to false to keep the bigint.

T
PrefixedCidrv4 = { readonly address: Addressv4; readonly prefixLength: PrefixLengthv4; readonly mask?: never; }

An IPv4 CIDR block written with a prefix length, as in 10.0.0.0/8.

T
PrefixedCidrv6 = { readonly address: Addressv6; readonly prefixLength: PrefixLengthv6; readonly mask?: never; }

An IPv6 CIDR block written with a prefix length, as in 2001:db8::/32.

T
PrefixLength = number

A prefix length of either IP version. Being a bare number it cannot say which version it belongs to, which is why there is no universal mask-from-prefix-length function: 24 is /24 in both, and the masks differ.

T
PrefixLengthv4 = number

An IPv4 prefix length, the 24 in /24. The range is 0 to 32; it is documented rather than encoded in the type, so prefixLength + 1 stays a PrefixLengthv4 (ADR 0002).

T
PrefixLengthv6 = number

An IPv6 prefix length, the 64 in /64. The range is 0 to 128; it is documented rather than encoded in the type, so prefixLength + 1 stays a PrefixLengthv6 (ADR 0002).

T
ZoneId = string

The zone ID of an address, the interface tail after % in fe80::1%eth0 (RFC 4007 section 11). Carried verbatim by the Parsed* types; never percent-decoded, so %25eth0 is the zone 25eth0.

cidrv4.ts

IPv4 CIDR notation parsing and utilities.

Examples

Both dialects parse, and write back the way they came

import { assertEquals } from "@std/assert";
import { cidrv4Size, parseCidrv4, stringifyCidrv4 } from "@hertzg/ip/cidrv4";

const prefixed = parseCidrv4("10.0.0.0/8");
const masked = parseCidrv4("10.0.0.0/255.0.0.0");

assertEquals(cidrv4Size(prefixed), cidrv4Size(masked));
assertEquals(stringifyCidrv4(prefixed), "10.0.0.0/8");
assertEquals(stringifyCidrv4(masked), "10.0.0.0/255.0.0.0");

CIDR operations

import { assert, assertEquals } from "@std/assert";
import { cidrv4Contains, parseCidrv4 } from "@hertzg/ip/cidrv4";
import { parseAddressv4 } from "@hertzg/ip/addressv4";

const cidr = parseCidrv4("192.168.1.0/24");

assert(cidrv4Contains(cidr, parseAddressv4("192.168.1.1").address));
assertEquals(cidrv4Contains(cidr, parseAddressv4("192.168.2.1").address), false);

Handing out assignable addresses

import { assertEquals } from "@std/assert";
import {
  cidrv4UsableAddresses,
  cidrv4UsableSize,
  parseCidrv4,
} from "@hertzg/ip/cidrv4";
import { stringifyAddressv4 } from "@hertzg/ip/addressv4";

const pool = parseCidrv4("192.168.1.0/24");
const assigned = Array.from(cidrv4UsableAddresses(pool), stringifyAddressv4);

assertEquals(assigned.length, cidrv4UsableSize(pool));
assertEquals(assigned[0], "192.168.1.1");
assertEquals(assigned.at(-1), "192.168.1.254");

Functions

f
cidrv4BroadcastAddress(cidr: Cidrv4): number

Returns the directed broadcast address of a CIDR block.

f
cidrv4Contains(
cidr: Cidrv4,
address: number
): boolean

Checks if an IPv4 address is contained within a CIDR block.

f
cidrv4ContainsCidr(
outer: Cidrv4,
inner: Cidrv4
): boolean

Checks if one IPv4 CIDR block fully contains another.

f
cidrv4FirstAddress(cidr: Cidrv4): number

Returns the first address of a CIDR block (network address).

f
cidrv4FirstUsableAddress(cidr: Cidrv4): number

Returns the first assignable address of a CIDR block.

f
cidrv4Intersect(
a: Cidrv4,
b: Cidrv4
): Cidrv4 | null

Returns the intersection of two IPv4 CIDR blocks.

f
cidrv4LastAddress(cidr: Cidrv4): number

Returns the last address of a CIDR block (broadcast address for IPv4).

f
cidrv4LastUsableAddress(cidr: Cidrv4): number

Returns the last assignable address of a CIDR block.

f
cidrv4Mask(cidrOrPrefixLength: Cidrv4 | PrefixLengthv4): Maskv4
3 overloads

Creates a network mask from an IPv4 prefix length.

f
cidrv4Merge(cidrs: readonly Cidrv4[]): Cidrv4[]

Merges IPv4 CIDR blocks into the minimal covering set.

f
cidrv4NetworkAddress(cidr: Cidrv4): number

Returns the network address of a CIDR block.

f
cidrv4Overlaps(
a: Cidrv4,
b: Cidrv4
): boolean

Checks if two IPv4 CIDR blocks overlap (share at least one address).

f
cidrv4PrefixLength(cidrOrMask: Cidrv4 | Maskv4 | string): PrefixLengthv4
4 overloads

Recovers the prefix length from an IPv4 network mask given as a 32-bit unsigned integer.

f
cidrv4Size(cidrOrPrefixLength: Cidrv4 | PrefixLengthv4): number
3 overloads

Returns the total number of IP addresses in a CIDR block.

f
cidrv4Subtract(
a: Cidrv4,
b: Cidrv4
): Cidrv4[]

Subtracts one IPv4 CIDR block from another.

f
cidrv4UsableAddresses(cidr: Cidrv4): Generator<number>

Generates every assignable address in a CIDR block, in ascending order.

f
cidrv4UsableSize(cidrOrPrefixLength: Cidrv4 | PrefixLengthv4): number
3 overloads

Returns the number of assignable addresses in a CIDR block.

f
compareCidrv4(
a: Cidrv4,
b: Cidrv4
): -1 | 0 | 1

Compares two IPv4 CIDR blocks for sorting.

f
parseCidrv4(cidr: string): ParsedCidrv4

Parses IPv4 CIDR notation, in either dialect and with an optional zone ID, to a ParsedCidrv4.

f
stringifyCidrv4(cidr: Addressv4 | ParsedAddressv4 | ParsedCidrv4): string

Stringifies an IPv4 CIDR block, or an address, to CIDR notation.

Type Aliases

T
Addressv4 = number

An IPv4 address as a 32-bit unsigned integer, 0 to 4294967295. The primitive type is what carries the version: a number is IPv4, a bigint is IPv6 (ADR 0001).

T
Cidrv4 = PrefixedCidrv4 | MaskedCidrv4

Represents an IPv4 CIDR block.

T
MaskedCidrv4 = { readonly address: Addressv4; readonly mask: Maskv4; readonly prefixLength?: never; }

An IPv4 CIDR block written with a network mask, as in 10.0.0.0/255.0.0.0.

T
Maskv4 = number

An IPv4 network mask as a 32-bit unsigned integer, e.g. 0xFFFFFF00 for /24.

T
ParsedAddressv4 = { readonly address: Addressv4; readonly zoneId?: ZoneId; }

What parseAddressv4 returns and what stringifyAddressv4 accepts: the address, plus the zone ID if the notation had one. Read .address for the bare Addressv4; the zone never touches the value, and no operation in this package reads it.

T
ParsedCidrv4 = Cidrv4 & { readonly zoneId?: ZoneId; }

What parseCidrv4 returns and what stringifyCidrv4 accepts: a Cidrv4 in the dialect it was written in, plus the zone ID if the notation had one (fe80::%ether1/64 has one; RouterOS emits that form for connected routes). Assignable to Cidrv4, so a parse result goes straight into every cidrv4* operation; none of them reads the zone.

T
PrefixedCidrv4 = { readonly address: Addressv4; readonly prefixLength: PrefixLengthv4; readonly mask?: never; }

An IPv4 CIDR block written with a prefix length, as in 10.0.0.0/8.

T
PrefixLengthv4 = number

An IPv4 prefix length, the 24 in /24. The range is 0 to 32; it is documented rather than encoded in the type, so prefixLength + 1 stays a PrefixLengthv4 (ADR 0002).

T
ZoneId = string

The zone ID of an address, the interface tail after % in fe80::1%eth0 (RFC 4007 section 11). Carried verbatim by the Parsed* types; never percent-decoded, so %25eth0 is the zone 25eth0.

addressv6.ts

IPv6 address parsing and stringifying utilities.

Examples

Basic IPv6 operations

import { assertEquals } from "@std/assert";
import { parseAddressv6, stringifyAddressv6 } from "@hertzg/ip/addressv6";

const { address } = parseAddressv6("2001:db8::1");
assertEquals(address, 42540766411282592856903984951653826561n);

const next = address + 1n;
assertEquals(stringifyAddressv6(next), "2001:db8::2");

Zone IDs are carried, not applied

import { assertEquals } from "@std/assert";
import { parseAddressv6, stringifyAddressv6 } from "@hertzg/ip/addressv6";

const linkLocal = parseAddressv6("fe80::1%eth0");
assertEquals(linkLocal, { address: 0xfe800000000000000000000000000001n, zoneId: "eth0" });
assertEquals(stringifyAddressv6(linkLocal), "fe80::1%eth0");
assertEquals(stringifyAddressv6(linkLocal.address), "fe80::1");

Bitwise operations on IPv6 addresses

Since IPv6 addresses are plain bigints, you can use standard JavaScript bitwise operators directly. For NOT, mask the result with the maximum 128-bit value to stay within range.

import { assertEquals } from "@std/assert";
import { parseAddressv6, stringifyAddressv6 } from "@hertzg/ip/addressv6";

const ip = parseAddressv6("2001:db8::1").address;
const MAX_IPV6 = (1n << 128n) - 1n;

// Bitwise NOT (invert all bits, mask to 128 bits)
const inverted = ~ip & MAX_IPV6;
assertEquals(stringifyAddressv6(inverted), "dffe:f247:ffff:ffff:ffff:ffff:ffff:fffe");

// Bitwise AND (apply network mask to get network address)
const mask = (MAX_IPV6 << 96n) & MAX_IPV6; // /32 mask
const network = ip & mask;
assertEquals(stringifyAddressv6(network), "2001:db8::");

// Bitwise OR (set host bits)
const result = network | 0xFFn;
assertEquals(stringifyAddressv6(result), "2001:db8::ff");

// Direct comparison (no isEqual() needed)
assertEquals(parseAddressv6("::1").address === parseAddressv6("::1").address, true);
assertEquals(parseAddressv6("::1").address === parseAddressv6("::2").address, false);

Functions

f
compareAddressv6(
a: Addressv6,
b: Addressv6
): -1 | 0 | 1

Compares two IPv6 addresses for sorting, numerically ascending.

f
mapFromAddressv4(address: Addressv4): Addressv6

Converts an IPv4 address to its IPv4-mapped IPv6 representation.

f
parseAddressv6(address: string): ParsedAddressv6

Parses an IPv6 address in colon-hexadecimal notation, with an optional zone ID, to its numeric value.

f
stringifyAddressv6(address: Addressv6 | ParsedAddressv6): string

Stringifies an IPv6 address to compressed colon-hexadecimal notation.

f
stringifyAddressv6Expanded(address: Addressv6 | ParsedAddressv6): string

Stringifies an IPv6 address to full uncompressed colon-hexadecimal notation.

f
unmapToAddressv4(address: Addressv6): Addressv4

Extracts the IPv4 address from an IPv4-mapped IPv6 address.

Type Aliases

T
Addressv4 = number

An IPv4 address as a 32-bit unsigned integer, 0 to 4294967295. The primitive type is what carries the version: a number is IPv4, a bigint is IPv6 (ADR 0001).

T
Addressv6 = bigint

An IPv6 address as a 128-bit unsigned bigint, 0n to 2n ** 128n - 1n. The primitive type is what carries the version: a bigint is IPv6, a number is IPv4 (ADR 0001).

T
ParsedAddressv6 = { readonly address: Addressv6; readonly zoneId?: ZoneId; }

What parseAddressv6 returns and what stringifyAddressv6 accepts: the address, plus the zone ID if the notation had one. Read .address for the bare Addressv6; the zone never touches the value, and no operation in this package reads it.

T
ZoneId = string

The zone ID of an address, the interface tail after % in fe80::1%eth0 (RFC 4007 section 11). Carried verbatim by the Parsed* types; never percent-decoded, so %25eth0 is the zone 25eth0.

cidrv6.ts

IPv6 CIDR notation parsing and utilities.

Examples

Both dialects parse, a zone rides along, and each writes back as it came

import { assertEquals } from "@std/assert";
import { cidrv6Size, parseCidrv6, stringifyCidrv6 } from "@hertzg/ip/cidrv6";

const prefixed = parseCidrv6("fe80::%ether1/64");
const masked = parseCidrv6("fe80::/ffff:ffff:ffff:ffff::");

assertEquals(cidrv6Size(prefixed), cidrv6Size(masked));
assertEquals(stringifyCidrv6(prefixed), "fe80::%ether1/64");
assertEquals(stringifyCidrv6(masked), "fe80::/ffff:ffff:ffff:ffff::");

CIDR operations

import { assert, assertEquals } from "@std/assert";
import {
  cidrv6Contains,
  cidrv6FirstAddress,
  cidrv6LastAddress,
  parseCidrv6,
} from "@hertzg/ip/cidrv6";
import { parseAddressv6, stringifyAddressv6 } from "@hertzg/ip/addressv6";

const cidr = parseCidrv6("2001:db8:ffff:ffff:ffff:ffff::/120");
let currentIp = cidrv6FirstAddress(cidr) + 1n;

while (cidrv6Contains(cidr, currentIp)) {
  const assigned = stringifyAddressv6(currentIp);
  currentIp = currentIp + 1n;
  if (currentIp > cidrv6LastAddress(cidr)) break;
}

assert(cidrv6Contains(cidr, parseAddressv6("2001:db8:ffff:ffff:ffff:ffff::1").address));
assertEquals(cidrv6Contains(cidr, parseAddressv6("2001:db9::1").address), false);

Functions

f
cidrv6Contains(
cidr: Cidrv6,
address: bigint
): boolean

Checks if an IPv6 address is contained within a CIDR block.

f
cidrv6ContainsCidr(
outer: Cidrv6,
inner: Cidrv6
): boolean

Checks if one IPv6 CIDR block fully contains another.

f
cidrv6FirstAddress(cidr: Cidrv6): bigint

Returns the first address of a CIDR block.

f
cidrv6Intersect(
a: Cidrv6,
b: Cidrv6
): Cidrv6 | null

Returns the intersection of two IPv6 CIDR blocks.

f
cidrv6LastAddress(cidr: Cidrv6): bigint

Returns the last address of a CIDR block.

f
cidrv6Mask(cidrOrPrefixLength: Cidrv6 | PrefixLengthv6): Maskv6
3 overloads

Creates a network mask from an IPv6 prefix length.

f
cidrv6Merge(cidrs: readonly Cidrv6[]): Cidrv6[]

Merges IPv6 CIDR blocks into the minimal covering set.

f
cidrv6Overlaps(
a: Cidrv6,
b: Cidrv6
): boolean

Checks if two IPv6 CIDR blocks overlap (share at least one address).

f
cidrv6PrefixLength(cidrOrMask: Cidrv6 | Maskv6 | string): PrefixLengthv6
4 overloads

Recovers the prefix length from an IPv6 network mask given as a bigint.

f
cidrv6Size(cidrOrPrefixLength: Cidrv6 | PrefixLengthv6): bigint
3 overloads

Returns the total number of IP addresses in a CIDR block.

f
cidrv6Subtract(
a: Cidrv6,
b: Cidrv6
): Cidrv6[]

Subtracts one IPv6 CIDR block from another.

f
compareCidrv6(
a: Cidrv6,
b: Cidrv6
): -1 | 0 | 1

Compares two IPv6 CIDR blocks for sorting.

f
mapFromCidrv4(cidr: Cidrv4): Cidrv6
3 overloads

Converts an IPv4 CIDR block with a prefix length to its IPv4-mapped IPv6 CIDR representation.

f
parseCidrv6(cidr: string): ParsedCidrv6

Parses IPv6 CIDR notation, in either dialect and with an optional zone ID, to a ParsedCidrv6.

f
stringifyCidrv6(cidr: Addressv6 | ParsedAddressv6 | ParsedCidrv6): string

Stringifies an IPv6 CIDR block, or an address, to CIDR notation with the address compressed.

f
stringifyCidrv6Expanded(cidr: Addressv6 | ParsedAddressv6 | ParsedCidrv6): string

Stringifies an IPv6 CIDR block, or an address, to CIDR notation with the address written in full uncompressed colon-hexadecimal.

f
unmapToCidrv4(cidr: Cidrv6): Cidrv4
3 overloads

Converts an IPv4-mapped IPv6 CIDR block with a prefix length to its IPv4 CIDR representation.

Type Aliases

T
Addressv4 = number

An IPv4 address as a 32-bit unsigned integer, 0 to 4294967295. The primitive type is what carries the version: a number is IPv4, a bigint is IPv6 (ADR 0001).

T
Addressv6 = bigint

An IPv6 address as a 128-bit unsigned bigint, 0n to 2n ** 128n - 1n. The primitive type is what carries the version: a bigint is IPv6, a number is IPv4 (ADR 0001).

T
Cidrv4 = PrefixedCidrv4 | MaskedCidrv4

Represents an IPv4 CIDR block.

T
Cidrv6 = PrefixedCidrv6 | MaskedCidrv6

Represents an IPv6 CIDR block.

T
MaskedCidrv4 = { readonly address: Addressv4; readonly mask: Maskv4; readonly prefixLength?: never; }

An IPv4 CIDR block written with a network mask, as in 10.0.0.0/255.0.0.0.

T
MaskedCidrv6 = { readonly address: Addressv6; readonly mask: Maskv6; readonly prefixLength?: never; }

An IPv6 CIDR block written with a network mask, as in 2001:db8::/ffff:ffff::.

T
Maskv4 = number

An IPv4 network mask as a 32-bit unsigned integer, e.g. 0xFFFFFF00 for /24.

T
Maskv6 = bigint

An IPv6 network mask as a 128-bit unsigned bigint, e.g. 0xFFFFFFFFFFFFFFFF0000000000000000n for /64.

T
ParsedAddressv6 = { readonly address: Addressv6; readonly zoneId?: ZoneId; }

What parseAddressv6 returns and what stringifyAddressv6 accepts: the address, plus the zone ID if the notation had one. Read .address for the bare Addressv6; the zone never touches the value, and no operation in this package reads it.

T
ParsedCidrv6 = Cidrv6 & { readonly zoneId?: ZoneId; }

What parseCidrv6 returns and what stringifyCidrv6 accepts: a Cidrv6 in the dialect it was written in, plus the zone ID if the notation had one (fe80::%ether1/64, the form RouterOS and netstat -rn emit for link-local routes). Assignable to Cidrv6, so a parse result goes straight into every cidrv6* operation; none of them reads the zone.

T
PrefixedCidrv4 = { readonly address: Addressv4; readonly prefixLength: PrefixLengthv4; readonly mask?: never; }

An IPv4 CIDR block written with a prefix length, as in 10.0.0.0/8.

T
PrefixedCidrv6 = { readonly address: Addressv6; readonly prefixLength: PrefixLengthv6; readonly mask?: never; }

An IPv6 CIDR block written with a prefix length, as in 2001:db8::/32.

T
PrefixLengthv4 = number

An IPv4 prefix length, the 24 in /24. The range is 0 to 32; it is documented rather than encoded in the type, so prefixLength + 1 stays a PrefixLengthv4 (ADR 0002).

T
PrefixLengthv6 = number

An IPv6 prefix length, the 64 in /64. The range is 0 to 128; it is documented rather than encoded in the type, so prefixLength + 1 stays a PrefixLengthv6 (ADR 0002).

T
ZoneId = string

The zone ID of an address, the interface tail after % in fe80::1%eth0 (RFC 4007 section 11). Carried verbatim by the Parsed* types; never percent-decoded, so %25eth0 is the zone 25eth0.

classify.ts

Universal IP address classification.

Examples

Classify any IP address

import { assertEquals } from "@std/assert";
import { classifyAddress } from "@hertzg/ip/classify";
import { parseAddressv4 } from "@hertzg/ip/addressv4";
import { parseAddressv6 } from "@hertzg/ip/addressv6";

// IPv4 from parsed value
const v4 = classifyAddress(parseAddressv4("192.168.1.1").address);
assertEquals(v4.kind, "ipv4");
assertEquals(v4.value, 3232235777);
assertEquals(v4.classification, "private");

// IPv6 from parsed value
const v6 = classifyAddress(parseAddressv6("::1").address);
assertEquals(v6.kind, "ipv6");
assertEquals(v6.value, 1n);
assertEquals(v6.classification, "loopback");

// From string directly
const str4 = classifyAddress("127.0.0.1");
assertEquals(str4.kind, "ipv4");
assertEquals(str4.classification, "loopback");

const str6 = classifyAddress("2001:db8::1");
assertEquals(str6.kind, "ipv6");
assertEquals(str6.classification, "documentation");

Functions

f
classifyAddress(address: Address | string): ClassifiedAddress
4 overloads

Classifies an IPv4 address into its well-known range.

Type Aliases

T
Address = Addressv4 | Addressv6

A plain IP address of either IP version.

T
Addressv4 = number

An IPv4 address as a 32-bit unsigned integer, 0 to 4294967295. The primitive type is what carries the version: a number is IPv4, a bigint is IPv6 (ADR 0001).

T
Addressv6 = bigint

An IPv6 address as a 128-bit unsigned bigint, 0n to 2n ** 128n - 1n. The primitive type is what carries the version: a bigint is IPv6, a number is IPv4 (ADR 0001).

T
ClassifiedAddress = ClassifiedAddressv4 | ClassifiedAddressv6

Result of classifying an IP address with version information and parsed value.

classifyv4.ts

IPv4 address classification utilities.

Examples

Classify an IPv4 address

import { assertEquals } from "@std/assert";
import { classifyAddressv4 } from "@hertzg/ip/classifyv4";
import { parseAddressv4 } from "@hertzg/ip/addressv4";

assertEquals(classifyAddressv4(parseAddressv4("192.168.1.1").address), "private");
assertEquals(classifyAddressv4(parseAddressv4("8.8.8.8").address), "public");
assertEquals(classifyAddressv4(parseAddressv4("127.0.0.1").address), "loopback");
assertEquals(classifyAddressv4(parseAddressv4("224.0.0.1").address), "multicast");

Check specific ranges

import { assert, assertEquals } from "@std/assert";
import { isAddressv4Loopback, isAddressv4Private, isAddressv4Public } from "@hertzg/ip/classifyv4";
import { parseAddressv4 } from "@hertzg/ip/addressv4";

assert(isAddressv4Loopback(parseAddressv4("127.0.0.1").address));
assert(isAddressv4Private(parseAddressv4("10.0.0.1").address));
assertEquals(isAddressv4Public(parseAddressv4("10.0.0.1").address), false);
assert(isAddressv4Public(parseAddressv4("8.8.8.8").address));

Functions

f
classifyAddressv4(address: number): Classificationv4

Classifies an IPv4 address into its well-known range.

f
isAddressv4Benchmarking(address: number): boolean

Checks if an IPv4 address is in the benchmarking range (RFC 2544).

f
isAddressv4Broadcast(address: number): boolean

Checks if an IPv4 address is the limited broadcast address.

f
isAddressv4CgNat(address: number): boolean

Checks if an IPv4 address is in the Carrier-Grade NAT range (RFC 6598).

f
isAddressv4Documentation(address: number): boolean

Checks if an IPv4 address is in a documentation range (RFC 5737).

f
isAddressv4LinkLocal(address: number): boolean

Checks if an IPv4 address is a link-local address (RFC 3927).

f
isAddressv4Loopback(address: number): boolean

Checks if an IPv4 address is a loopback address (RFC 1122).

f
isAddressv4Multicast(address: number): boolean

Checks if an IPv4 address is a multicast address (RFC 5771).

f
isAddressv4Private(address: number): boolean

Checks if an IPv4 address is in a private range (RFC 1918).

f
isAddressv4Public(address: number): boolean

Checks if an IPv4 address is a public (globally routable) address.

f
isAddressv4Reserved(address: number): boolean

Checks if an IPv4 address is in the reserved range (RFC 1112).

f
isAddressv4ThisNetwork(address: number): boolean

Checks if an IPv4 address is in the "this network" range (RFC 791).

Type Aliases

classifyv6.ts

IPv6 address classification utilities.

Examples

Classify an IPv6 address

import { assertEquals } from "@std/assert";
import { classifyAddressv6 } from "@hertzg/ip/classifyv6";
import { parseAddressv6 } from "@hertzg/ip/addressv6";

assertEquals(classifyAddressv6(parseAddressv6("::1").address), "loopback");
assertEquals(classifyAddressv6(parseAddressv6("2001:db8::1").address), "documentation");
assertEquals(classifyAddressv6(parseAddressv6("fe80::1").address), "link-local");
assertEquals(classifyAddressv6(parseAddressv6("2607:f8b0:4004:800::200e").address), "global-unicast");

Check specific ranges

import { assert, assertEquals } from "@std/assert";
import { isAddressv6Loopback, isAddressv6UniqueLocal } from "@hertzg/ip/classifyv6";
import { parseAddressv6 } from "@hertzg/ip/addressv6";

assert(isAddressv6Loopback(parseAddressv6("::1").address));
assert(isAddressv6UniqueLocal(parseAddressv6("fd00::1").address));
assertEquals(isAddressv6Loopback(parseAddressv6("::2").address), false);

Functions

f
classifyAddressv6(address: bigint): Classificationv6

Classifies an IPv6 address into its well-known range.

f
isAddressv6Benchmarking(address: bigint): boolean

Checks if an IPv6 address is in the benchmarking range (RFC 5180).

f
isAddressv6Documentation(address: bigint): boolean

Checks if an IPv6 address is in the documentation range (RFC 3849).

f
isAddressv6GlobalUnicast(address: bigint): boolean

Checks if an IPv6 address is a global unicast address (RFC 4291).

f
isAddressv6LinkLocal(address: bigint): boolean

Checks if an IPv6 address is a link-local address (RFC 4291).

f
isAddressv6Loopback(address: bigint): boolean

Checks if an IPv6 address is the loopback address (RFC 4291).

f
isAddressv6Mapped(address: bigint): boolean

Checks if an IPv6 address is an IPv4-mapped address (RFC 4291).

f
isAddressv6Multicast(address: bigint): boolean

Checks if an IPv6 address is a multicast address (RFC 4291).

f
isAddressv6Orchidv2(address: bigint): boolean

Checks if an IPv6 address is an ORCHIDv2 address (RFC 7343).

f
isAddressv6Teredo(address: bigint): boolean

Checks if an IPv6 address is a Teredo address (RFC 4380).

f
isAddressv6Translated(address: bigint): boolean

Checks if an IPv6 address is an IPv4-translated address (RFC 6052).

f
isAddressv6UniqueLocal(address: bigint): boolean

Checks if an IPv6 address is a unique local address (RFC 4193).

f
isAddressv6Unspecified(address: bigint): boolean

Checks if an IPv6 address is the unspecified address (RFC 4291).

Type Aliases

validate.ts

Universal IP address and CIDR validation utilities.

Examples

Universal validation

import { assert, assertEquals } from "@std/assert";
import { isValidCidr, isValidAddress } from "@hertzg/ip/validate";

assert(isValidAddress("192.168.1.1"));
assert(isValidAddress("::1"));
assertEquals(isValidAddress("10.0.0.0/8"), false);
assertEquals(isValidAddress("garbage"), false);

assert(isValidCidr("10.0.0.0/8"));
assert(isValidCidr("2001:db8::/32"));
assertEquals(isValidCidr("192.168.1.1"), false);

Functions

f
isValidAddress(address: string): boolean

Checks if a string is a valid plain IP address (IPv4 or IPv6).

f
isValidCidr(cidr: string): boolean

Checks if a string is valid IPv4 or IPv6 CIDR notation.

version.ts

IP version detection for address and CIDR strings.

Examples

Dispatching on the IP version

import { assertEquals } from "@std/assert";
import { addressVersion } from "@hertzg/ip/version";
import { parseAddressv4 } from "@hertzg/ip/addressv4";
import { parseAddressv6 } from "@hertzg/ip/addressv6";

const describe = (input: string): string => {
  switch (addressVersion(input)) {
    case 4:
      return `v4:${parseAddressv4(input).address}`;
    case 6:
      return `v6:${parseAddressv6(input).address}`;
    default:
      return "not an address";
  }
};

assertEquals(describe("10.0.0.1"), "v4:167772161");
assertEquals(describe("::1"), "v6:1");
assertEquals(describe("nonsense"), "not an address");

Functions

f
addressVersion(address: string): IpVersion | undefined

Reports which IP version a plain address string is written in.

f
cidrVersion(cidr: string): IpVersion | undefined

Reports which IP version a CIDR notation string is written in.

Type Aliases

T
IpVersion = 4 | 6

An IP version number: 4 for IPv4, 6 for IPv6.

validatev4.ts

IPv4 address and CIDR validation utilities.

Examples

Example 1

import { assert, assertEquals } from "@std/assert";
import { isValidCidrv4, isValidAddressv4 } from "@hertzg/ip";

assert(isValidAddressv4("192.168.1.1"));
assertEquals(isValidAddressv4("::1"), false);

assert(isValidCidrv4("10.0.0.0/8"));
assertEquals(isValidCidrv4("10.0.0.0/33"), false);

Functions

f
isValidAddressv4(address: string): boolean

Checks if a string is a valid IPv4 address in dotted decimal notation.

f
isValidCidrv4(cidr: string): boolean

Checks if a string is valid IPv4 CIDR notation.

validatev6.ts

IPv6 address and CIDR validation utilities.

Examples

Example 1

import { assert, assertEquals } from "@std/assert";
import { isValidCidrv6, isValidAddressv6 } from "@hertzg/ip";

assert(isValidAddressv6("::1"));
assertEquals(isValidAddressv6("192.168.1.1"), false);

assert(isValidCidrv6("2001:db8::/32"));
assertEquals(isValidCidrv6("2001:db8::/129"), false);

Functions

f
isValidAddressv6(address: string): boolean

Checks if a string is a valid IPv6 address in colon-hexadecimal notation.

f
isValidCidrv6(cidr: string): boolean

Checks if a string is valid IPv6 CIDR notation.

bytes.ts

Universal conversion between IP addresses and their network-order wire bytes.

Examples

Read and write without knowing the version up front

import { assertEquals } from "@std/assert";
import { addressFromBytes, addressToBytes } from "@hertzg/ip/bytes";
import { stringifyAddress } from "@hertzg/ip/address";

const field = new Uint8Array([10, 0, 0, 1]);
assertEquals(stringifyAddress(addressFromBytes(field)), "10.0.0.1");

// The width comes back out unchanged
assertEquals(addressToBytes(addressFromBytes(field)), field);

Functions

f
addressFromBytes(bytes: Uint8Array): Address

Reads an IPv4 or IPv6 address from a buffer, picking the version from its length.

f
addressToBytes(
address: Address,
into?: Uint8Array,
offset?: number
): Uint8Array

Writes an IPv4 or IPv6 address, either into a fresh buffer or into one you supply.

bytesv4.ts

Conversion between IPv4 addresses and their network-order wire bytes.

Examples

Decode both addresses out of an IPv4 header

import { assertEquals } from "@std/assert";
import { addressv4FromBytes } from "@hertzg/ip/bytesv4";
import { stringifyAddressv4 } from "@hertzg/ip/addressv4";

// deno-fmt-ignore
const packet = new Uint8Array([
  0x45, 0x00, 0x00, 0x54, 0x1c, 0x46, 0x40, 0x00,
  0x40, 0x06, 0x00, 0x00,
  10, 0, 0, 1,
  192, 168, 1, 1,
]);

assertEquals(stringifyAddressv4(addressv4FromBytes(packet, 12)), "10.0.0.1");
assertEquals(stringifyAddressv4(addressv4FromBytes(packet, 16)), "192.168.1.1");

Assemble an IPv4 header in place

import { assertEquals } from "@std/assert";
import { addressv4ToBytes } from "@hertzg/ip/bytesv4";
import { parseAddressv4 } from "@hertzg/ip/addressv4";

const frame = new Uint8Array(20);
addressv4ToBytes(parseAddressv4("10.0.0.1").address, frame, 12);
addressv4ToBytes(parseAddressv4("192.168.1.1").address, frame, 16);

assertEquals(frame.slice(12), new Uint8Array([10, 0, 0, 1, 192, 168, 1, 1]));

Functions

f
addressv4FromBytes(
bytes: Uint8Array,
offset?: number
): number

Reads a 4-byte IPv4 address from a buffer.

f
addressv4ToBytes(
address: number,
into?: Uint8Array,
offset?: number
): Uint8Array

Writes a 4-byte IPv4 address, either into a fresh buffer or into one you supply.

bytesv6.ts

Conversion between IPv6 addresses and their network-order wire bytes.

Examples

Decode an address out of an IPv6 header

import { assertEquals } from "@std/assert";
import { addressv6FromBytes } from "@hertzg/ip/bytesv6";
import { stringifyAddressv6 } from "@hertzg/ip/addressv6";

// deno-fmt-ignore
const packet = new Uint8Array([
  0x60, 0x00, 0x00, 0x00, 0x00, 0x14, 0x06, 0x40,
  0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
]);

assertEquals(stringifyAddressv6(addressv6FromBytes(packet, 8)), "2001:db8::1");

Assemble an IPv6 header in place

import { assertEquals } from "@std/assert";
import { addressv6FromBytes, addressv6ToBytes } from "@hertzg/ip/bytesv6";
import { parseAddressv6 } from "@hertzg/ip/addressv6";

const frame = new Uint8Array(40);
addressv6ToBytes(parseAddressv6("2001:db8::1").address, frame, 8);
addressv6ToBytes(parseAddressv6("2001:db8::2").address, frame, 24);

assertEquals(addressv6FromBytes(frame, 24), parseAddressv6("2001:db8::2").address);

Functions

f
addressv6FromBytes(
bytes: Uint8Array,
offset?: number
): bigint

Reads a 16-byte IPv6 address from a buffer.

f
addressv6ToBytes(
address: bigint,
into?: Uint8Array,
offset?: number
): Uint8Array

Writes a 16-byte IPv6 address, either into a fresh buffer or into one you supply.

arpa.ts

Universal reverse DNS pointer names for IP addresses.

Examples

Look up the PTR record of an address

import { assertEquals } from "@std/assert";
import { addressToArpa } from "@hertzg/ip/arpa";
import { parseAddress } from "@hertzg/ip/address";

assertEquals(addressToArpa(parseAddress("8.8.8.8").address), "8.8.8.8.in-addr.arpa");
assertEquals(
  addressToArpa(parseAddress("2001:4860:4860::8888").address),
  "8.8.8.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.6.8.4.0.6.8.4.1.0.0.2.ip6.arpa",
);

Functions

f
addressToArpa(address: Address): string

Builds the reverse DNS pointer name of an IP address of either version.

arpav4.ts

IPv4 reverse DNS pointer names.

Examples

Build the name a PTR record lives at

import { assertEquals } from "@std/assert";
import { addressv4ToArpa } from "@hertzg/ip/arpav4";
import { parseAddressv4 } from "@hertzg/ip/addressv4";

assertEquals(addressv4ToArpa(parseAddressv4("192.168.0.1").address), "1.0.168.192.in-addr.arpa");

Functions

f
addressv4ToArpa(address: number): string

Builds the reverse DNS pointer name of an IPv4 address.

arpav6.ts

IPv6 reverse DNS pointer names.

Examples

Build the name a PTR record lives at

import { assertEquals } from "@std/assert";
import { addressv6ToArpa } from "@hertzg/ip/arpav6";
import { parseAddressv6 } from "@hertzg/ip/addressv6";

assertEquals(
  addressv6ToArpa(parseAddressv6("2001:db8::1").address),
  "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa",
);

Functions

f
addressv6ToArpa(address: bigint): string

Builds the reverse DNS pointer name of an IPv6 address.