@portable/ip
Functions
Ip.canParse
Returns whether text is an IP address that parse accepts. Anything other than a string
returns false.
function canParse(
text: unknown,
options: canParse.Options = {},
): boolean;| Option | Type | Default | Description |
|---|---|---|---|
version | 4 | 6 | undefined, which accepts both | Accept only this IP version. |
import * as Ip from '@portable/ip';
Ip.canParse('2001:db8::1'); // true
Ip.canParse('2001:db8::1', { version: 4 }); // false
Ip.canParse('010.0.0.1'); // falseIp.compare
Orders two addresses: IPv4 before IPv6, then by bytes, then by zone. Returns a negative number,
zero or a positive number, so it fits Array.prototype.sort. An IPv4 address and its
IPv4-mapped IPv6 form count as different addresses. Pass both through unmap to treat them as
one.
function compare(a: Address | string, b: Address | string): number;import * as Ip from '@portable/ip';
['10.0.0.2', '::1', '10.0.0.1'].sort(Ip.compare); // ['10.0.0.1', '10.0.0.2', '::1']
Ip.compare('2001:db8::1', '2001:DB8:0::1') === 0; // trueIp.format
Formats an address as text. IPv6 follows RFC 5952: lowercase hex without leading zeros, and
:: for the longest run of two or more zero groups (the first one on a tie). IPv4-mapped
addresses end in dotted IPv4, as RFC 5952 section 5 recommends.
function format(address: Address | string): string;import * as Ip from '@portable/ip';
Ip.format('2001:0DB8:0:0:0:0:0:1'); // '2001:db8::1'
Ip.format('::ffff:c000:201'); // '::ffff:192.0.2.1'Ip.fromBytes
Builds an address from its bytes in network order: 4 bytes for IPv4, 16 for IPv6. The bytes are copied.
function fromBytes(bytes: Uint8Array): Address;import * as Ip from '@portable/ip';
Ip.format(Ip.fromBytes(new Uint8Array([127, 0, 0, 1]))); // '127.0.0.1'Ip.parse
Parses an IPv4 or IPv6 address.
IPv4 must be four decimal numbers from 0 to 255 without leading zeros. parse throws on octal
(0177.0.0.1), hex (0x7f.0.0.1), integer (2130706433) and short (127.1) forms, because
programs disagree on what they mean. IPv6 follows RFC 4291: up to four hex digits per group, one
::, an optional dotted IPv4 address in the last 32 bits, and an optional %zone.
function parse(text: string): Address;import * as Ip from '@portable/ip';
Ip.parse('192.0.2.1'); // { version: 4, bytes: Uint8Array [192, 0, 2, 1] }
Ip.parse('fe80::1%eth0'); // { version: 6, bytes: Uint8Array [254, 128, ..., 1], zone: 'eth0' }Ip.reverseName
Returns the DNS name for a reverse (PTR) lookup of the address: under in-addr.arpa for IPv4 and
ip6.arpa for IPv6, one label per byte or per hex digit, last first. An IPv4-mapped address
gets an ip6.arpa name, as in Node's dns.reverse. unmap it first to get the IPv4 name.
function reverseName(address: Address | string): string;import * as Ip from '@portable/ip';
Ip.reverseName('192.0.2.1'); // '1.2.0.192.in-addr.arpa'
Ip.reverseName('2001:db8::1'); // '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'Ip.toIpv6
Returns the IPv4-mapped IPv6 form of an IPv4 address (::ffff:192.0.2.1), and an IPv6 address
unchanged.
function toIpv6(address: Address | string): Address;import * as Ip from '@portable/ip';
Ip.format(Ip.toIpv6('192.0.2.1')); // '::ffff:192.0.2.1'Ip.unmap
Returns the IPv4 address inside an IPv4-mapped IPv6 address (::ffff:192.0.2.1), and any other
address unchanged. Call it before checking an address that may come from a dual-stack socket,
which reports IPv4 clients in mapped form.
function unmap(address: Address | string): Address;import * as Ip from '@portable/ip';
Ip.format(Ip.unmap('::ffff:127.0.0.1')); // '127.0.0.1'
Ip.format(Ip.unmap('2001:db8::1')); // '2001:db8::1'Ip.contains
Returns whether the CIDR block contains the address. An IPv4 address and its IPv4-mapped IPv6
form (::ffff:192.0.2.1) count as the same address, as in Node's BlockList, so 127.0.0.0/8
contains ::ffff:127.0.0.1. Zones are ignored. Strings are parsed with parseCidr and parse,
so parse a block once when you check many addresses against it.
function contains(
cidr: Cidr | string,
address: Address | string,
): boolean;import * as Ip from '@portable/ip';
Ip.contains('10.0.0.0/8', '10.1.2.3'); // true
Ip.contains('127.0.0.0/8', '::ffff:127.0.0.1'); // true
const internal = Ip.parseCidr('fc00::/7');
Ip.contains(internal, 'fd12:3456::1'); // trueIp.formatCidr
Formats a CIDR block as address/prefix, with the address formatted by format.
function formatCidr(cidr: Cidr): string;import * as Ip from '@portable/ip';
Ip.formatCidr({ address: Ip.parse('2001:db8::'), prefix: 32 }); // '2001:db8::/32'Ip.parseCidr
Parses CIDR notation, such as 10.0.0.0/8 or 2001:db8::/32. The address follows the rules of
parse, without a zone, and its host bits are cleared, so 192.168.1.134/26 gives the network
192.168.1.128/26. The prefix is a decimal number without leading zeros, up to 32 for IPv4 and
128 for IPv6.
function parseCidr(text: string): Cidr;import * as Ip from '@portable/ip';
const cidr = Ip.parseCidr('192.168.1.134/26');
Ip.formatCidr(cidr); // '192.168.1.128/26'
cidr.prefix; // 26Ip.range
Returns the special-purpose block the address is in, or 'unicast' for an ordinary public
address. To reject internal destinations before a server-side request (SSRF), allow only
'unicast'. An IPv4-mapped address returns 'ipv4Mapped', so pass it through unmap first to
classify the IPv4 address inside.
function range(address: Address | string): Range;import * as Ip from '@portable/ip';
Ip.range('10.1.2.3'); // 'private'
Ip.range('fe80::1'); // 'linkLocal'
Ip.range(Ip.unmap('::ffff:127.0.0.1')); // 'loopback'
Ip.range('8.8.8.8'); // 'unicast'Errors
Ip.InvalidInputError
Thrown for text that isn't an IP address or CIDR block in the strict notation parse and
parseCidr accept, and by fromBytes for a length other than 4 or 16.
class InvalidInputError extends Errors.InvalidInputError {
readonly kind: string;
}import * as Ip from '@portable/ip';
try {
Ip.parse('0x7f.1');
} catch (error) {
if (error instanceof Ip.InvalidInputError) {
console.log('not an IP address');
}
}Types
Ip.Address
An IPv4 or IPv6 address. bytes holds the address in network order: 4 bytes for IPv4 and 16
for IPv6. zone is the IPv6 zone index after %, as in fe80::1%eth0.
type Address = {
version: ;
bytes: Uint8Array;
} | {
version: ;
bytes: Uint8Array;
zone?: string | undefined;
};Ip.Cidr
A CIDR block: the network address and the number of leading bits that every address in it shares.
type Cidr = {
address: Address;
prefix: number;
};Ip.Range
The special-purpose block an address belongs to, named after its entry in the IANA IPv4 and IPv6
Special-Purpose Address Registries. 'multicast', 'deprecatedSiteLocal' and 'ipv4Translated'
come from RFC 5771, RFC 3879 and RFC 2765. 'unicast' is every other IPv4 address and every
other IPv6 address in 2000::/3. 'reserved' is the rest of the IPv6 space and 240.0.0.0/4.
type Range = | | | | | | | | | | | | | | | | | | | | | | | | | | | | ;