ip-utilities 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +238 -0
- package/dist/cidr.d.ts +2 -0
- package/dist/cidr.d.ts.map +1 -0
- package/dist/cidr.js +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +89 -0
- package/dist/internal/cidr.d.ts +112 -0
- package/dist/internal/cidr.d.ts.map +1 -0
- package/dist/internal/cidr.js +297 -0
- package/dist/internal/ipv4-ranges.d.ts +26 -0
- package/dist/internal/ipv4-ranges.d.ts.map +1 -0
- package/dist/internal/ipv4-ranges.js +73 -0
- package/dist/internal/ipv4.d.ts +44 -0
- package/dist/internal/ipv4.d.ts.map +1 -0
- package/dist/internal/ipv4.js +183 -0
- package/dist/internal/ipv6-ranges.d.ts +47 -0
- package/dist/internal/ipv6-ranges.d.ts.map +1 -0
- package/dist/internal/ipv6-ranges.js +159 -0
- package/dist/internal/ipv6.d.ts +67 -0
- package/dist/internal/ipv6.d.ts.map +1 -0
- package/dist/internal/ipv6.js +385 -0
- package/dist/internal/regex.d.ts +37 -0
- package/dist/internal/regex.d.ts.map +1 -0
- package/dist/internal/regex.js +118 -0
- package/dist/internal/sort.d.ts +22 -0
- package/dist/internal/sort.d.ts.map +1 -0
- package/dist/internal/sort.js +207 -0
- package/dist/ipv4.d.ts +2 -0
- package/dist/ipv4.d.ts.map +1 -0
- package/dist/ipv4.js +1 -0
- package/dist/ipv6.d.ts +2 -0
- package/dist/ipv6.d.ts.map +1 -0
- package/dist/ipv6.js +1 -0
- package/dist/ranges.d.ts +3 -0
- package/dist/ranges.d.ts.map +1 -0
- package/dist/ranges.js +2 -0
- package/dist/regex.d.ts +3 -0
- package/dist/regex.d.ts.map +1 -0
- package/dist/regex.js +1 -0
- package/dist/sort.d.ts +2 -0
- package/dist/sort.d.ts.map +1 -0
- package/dist/sort.js +1 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
const _V6_W0 = [0x00000000, 0x00000000, 0x0064FF9B, 0x00000000, 0x00000000, 0x01000000, 0x20010004, 0x2620004F, 0x20010002, 0x20010DB8, 0x20010003, 0x20010000, 0x20010010, 0x20010020, 0x20010030, 0x20010000, 0x20020000, 0xFE800000, 0xFF000000, 0xFC000000];
|
|
2
|
+
const _V6_W1 = [0, 0, 0, 0, 0, 0, 0x01120000, 0x80000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
3
|
+
const _V6_W2 = [0, 0, 0, 0x0000FFFF, 0xFFFF0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
4
|
+
const _V6_W3 = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
5
|
+
const _V6_PREFIX = [128, 128, 96, 96, 96, 64, 48, 48, 48, 32, 32, 32, 28, 28, 28, 23, 16, 10, 8, 7];
|
|
6
|
+
const _V6_NAMES = [
|
|
7
|
+
'loopback', 'unspecified', 'rfc6052', 'ipv4Mapped', 'rfc6145',
|
|
8
|
+
'discard', 'as112v6', 'as112v6', 'benchmarking', 'reserved',
|
|
9
|
+
'amt', 'teredo', 'deprecated', 'orchid2', 'droneRemoteIdProtocolEntityTags',
|
|
10
|
+
'reserved', '6to4', 'linkLocal', 'multicast', 'uniqueLocal',
|
|
11
|
+
];
|
|
12
|
+
// Pre-masked range tables: masks and expected values for each of the 4 uint32 words
|
|
13
|
+
const V6_COUNT = _V6_W0.length;
|
|
14
|
+
const V6R_MW0 = new Uint32Array(V6_COUNT);
|
|
15
|
+
const V6R_MW1 = new Uint32Array(V6_COUNT);
|
|
16
|
+
const V6R_MW2 = new Uint32Array(V6_COUNT);
|
|
17
|
+
const V6R_MW3 = new Uint32Array(V6_COUNT);
|
|
18
|
+
const V6R_KM0 = new Uint32Array(V6_COUNT);
|
|
19
|
+
const V6R_KM1 = new Uint32Array(V6_COUNT);
|
|
20
|
+
const V6R_KM2 = new Uint32Array(V6_COUNT);
|
|
21
|
+
const V6R_KM3 = new Uint32Array(V6_COUNT);
|
|
22
|
+
const V6R_NAMES = new Array(V6_COUNT);
|
|
23
|
+
for (let i = 0; i < V6_COUNT; i++) {
|
|
24
|
+
const p = _V6_PREFIX[i];
|
|
25
|
+
const m0 = prefixMaskV6(p, 0);
|
|
26
|
+
const m1 = prefixMaskV6(p, 1);
|
|
27
|
+
const m2 = prefixMaskV6(p, 2);
|
|
28
|
+
const m3 = prefixMaskV6(p, 3);
|
|
29
|
+
V6R_MW0[i] = (_V6_W0[i] & m0) >>> 0;
|
|
30
|
+
V6R_MW1[i] = (_V6_W1[i] & m1) >>> 0;
|
|
31
|
+
V6R_MW2[i] = (_V6_W2[i] & m2) >>> 0;
|
|
32
|
+
V6R_MW3[i] = (_V6_W3[i] & m3) >>> 0;
|
|
33
|
+
V6R_KM0[i] = m0;
|
|
34
|
+
V6R_KM1[i] = m1;
|
|
35
|
+
V6R_KM2[i] = m2;
|
|
36
|
+
V6R_KM3[i] = m3;
|
|
37
|
+
V6R_NAMES[i] = _V6_NAMES[i];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Tests whether an IPv6 address matches a CIDR prefix using raw uint32 words.
|
|
41
|
+
* @param aw0 - Address word 0 (bits 0-31)
|
|
42
|
+
* @param aw1 - Address word 1 (bits 32-63)
|
|
43
|
+
* @param aw2 - Address word 2 (bits 64-95)
|
|
44
|
+
* @param aw3 - Address word 3 (bits 96-127)
|
|
45
|
+
* @param nw0 - Network word 0
|
|
46
|
+
* @param nw1 - Network word 1
|
|
47
|
+
* @param nw2 - Network word 2
|
|
48
|
+
* @param nw3 - Network word 3
|
|
49
|
+
* @param prefix - Prefix length (0-128)
|
|
50
|
+
* @returns True if the address is within the network prefix
|
|
51
|
+
*/
|
|
52
|
+
export function matchesCidr(aw0, aw1, aw2, aw3, nw0, nw1, nw2, nw3, prefix) {
|
|
53
|
+
if (prefix === 0)
|
|
54
|
+
return true;
|
|
55
|
+
if (prefix <= 32) {
|
|
56
|
+
const mask = (0xFFFFFFFF << (32 - prefix)) >>> 0;
|
|
57
|
+
return (aw0 & mask) === (nw0 & mask);
|
|
58
|
+
}
|
|
59
|
+
if (aw0 !== nw0)
|
|
60
|
+
return false;
|
|
61
|
+
if (prefix <= 64) {
|
|
62
|
+
if (prefix === 32)
|
|
63
|
+
return true;
|
|
64
|
+
const mask = (0xFFFFFFFF << (64 - prefix)) >>> 0;
|
|
65
|
+
return (aw1 & mask) === (nw1 & mask);
|
|
66
|
+
}
|
|
67
|
+
if (aw1 !== nw1)
|
|
68
|
+
return false;
|
|
69
|
+
if (prefix <= 96) {
|
|
70
|
+
if (prefix === 64)
|
|
71
|
+
return true;
|
|
72
|
+
const mask = (0xFFFFFFFF << (96 - prefix)) >>> 0;
|
|
73
|
+
return (aw2 & mask) === (nw2 & mask);
|
|
74
|
+
}
|
|
75
|
+
if (aw2 !== nw2)
|
|
76
|
+
return false;
|
|
77
|
+
if (prefix === 128)
|
|
78
|
+
return aw3 === nw3;
|
|
79
|
+
const mask = (0xFFFFFFFF << (128 - prefix)) >>> 0;
|
|
80
|
+
return (aw3 & mask) === (nw3 & mask);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Computes the bitmask for a specific 32-bit word of an IPv6 prefix.
|
|
84
|
+
* @param prefix - Prefix length (0-128)
|
|
85
|
+
* @param wordOffset - Which 32-bit word (0-3)
|
|
86
|
+
* @returns The uint32 mask for that word
|
|
87
|
+
*/
|
|
88
|
+
export function prefixMaskV6(prefix, wordOffset) {
|
|
89
|
+
const lo = wordOffset * 32;
|
|
90
|
+
const hi = lo + 32;
|
|
91
|
+
if (prefix >= hi)
|
|
92
|
+
return 0xFFFFFFFF;
|
|
93
|
+
if (prefix <= lo)
|
|
94
|
+
return 0;
|
|
95
|
+
return (0xFFFFFFFF << (hi - prefix)) >>> 0;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Classifies an IPv6 address into its IANA-assigned range.
|
|
99
|
+
* @param addr - The IPv6 address
|
|
100
|
+
* @returns The range name (e.g. "uniqueLocal", "loopback", "unicast")
|
|
101
|
+
*/
|
|
102
|
+
export function ipv6Range(addr) {
|
|
103
|
+
const w0 = addr.w0;
|
|
104
|
+
const top3 = w0 >>> 29;
|
|
105
|
+
if (top3 === 1) {
|
|
106
|
+
const top16 = w0 >>> 16;
|
|
107
|
+
if (top16 !== 0x2001 && top16 !== 0x2002 && top16 !== 0x2620)
|
|
108
|
+
return 'unicast';
|
|
109
|
+
}
|
|
110
|
+
else if ((top3 - 2) >>> 0 <= 3) {
|
|
111
|
+
return 'unicast';
|
|
112
|
+
}
|
|
113
|
+
const w1 = addr.w1, w2 = addr.w2, w3 = addr.w3;
|
|
114
|
+
for (let i = 0; i < V6_COUNT; i++) {
|
|
115
|
+
if (((w0 & V6R_KM0[i]) >>> 0) !== V6R_MW0[i])
|
|
116
|
+
continue;
|
|
117
|
+
if (((w1 & V6R_KM1[i]) >>> 0) !== V6R_MW1[i])
|
|
118
|
+
continue;
|
|
119
|
+
if (((w2 & V6R_KM2[i]) >>> 0) !== V6R_MW2[i])
|
|
120
|
+
continue;
|
|
121
|
+
if (((w3 & V6R_KM3[i]) >>> 0) !== V6R_MW3[i])
|
|
122
|
+
continue;
|
|
123
|
+
return V6R_NAMES[i];
|
|
124
|
+
}
|
|
125
|
+
return 'unicast';
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Checks whether an IPv6 address is private (unique-local, multicast, link-local, or loopback).
|
|
129
|
+
* @param addr - The IPv6 address
|
|
130
|
+
* @returns True if the address is non-globally-routable
|
|
131
|
+
*/
|
|
132
|
+
export function isPrivateIPv6(addr) {
|
|
133
|
+
const w0 = addr.w0;
|
|
134
|
+
if ((w0 >>> 25) === 0x7E)
|
|
135
|
+
return true;
|
|
136
|
+
if ((w0 >>> 24) === 0xFF)
|
|
137
|
+
return true;
|
|
138
|
+
if ((w0 >>> 22) === 0x3FA)
|
|
139
|
+
return true;
|
|
140
|
+
if (w0 === 0 && addr.w1 === 0 && addr.w2 === 0 && addr.w3 === 1)
|
|
141
|
+
return true;
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Checks whether an IPv6 address is the loopback address (::1).
|
|
146
|
+
* @param addr - The IPv6 address
|
|
147
|
+
* @returns True if the address is ::1
|
|
148
|
+
*/
|
|
149
|
+
export function isLoopbackIPv6(addr) {
|
|
150
|
+
return addr.w0 === 0 && addr.w1 === 0 && addr.w2 === 0 && addr.w3 === 1;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Checks whether an IPv6 address is link-local (fe80::/10).
|
|
154
|
+
* @param addr - The IPv6 address
|
|
155
|
+
* @returns True if the address is in the link-local range
|
|
156
|
+
*/
|
|
157
|
+
export function isLinkLocalIPv6(addr) {
|
|
158
|
+
return (addr.w0 >>> 22) === 0x3FA;
|
|
159
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { IPv6Addr } from '../types.ts';
|
|
2
|
+
export declare const ipv6RawBuf: Uint32Array<ArrayBuffer>;
|
|
3
|
+
/**
|
|
4
|
+
* Retrieves the zone ID (e.g. "eth0") associated with a parsed IPv6 address.
|
|
5
|
+
* @param addr - The IPv6 address
|
|
6
|
+
* @returns The zone ID string, or empty string if none
|
|
7
|
+
*/
|
|
8
|
+
export declare function ipv6GetZoneId(addr: IPv6Addr): string;
|
|
9
|
+
/**
|
|
10
|
+
* Parses an IPv6 string into raw uint32 words written to {@link ipv6RawBuf}.
|
|
11
|
+
* @param s - Source string
|
|
12
|
+
* @param end - End index (exclusive) within the string
|
|
13
|
+
* @returns True if valid, with results in ipv6RawBuf[0..3]
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseIPv6Raw(s: string, end: number): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Parses an IPv6 string into an IPv6Addr. Supports :: compression,
|
|
18
|
+
* embedded IPv4 suffixes, and zone IDs (e.g. "fe80::1%eth0").
|
|
19
|
+
* @param s - The IPv6 string
|
|
20
|
+
* @param end - Optional end index for parsing a substring
|
|
21
|
+
* @returns The parsed address, or null if invalid
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseIPv6(s: string, end?: number): IPv6Addr | null;
|
|
24
|
+
/**
|
|
25
|
+
* Checks whether a string is a valid IPv6 address.
|
|
26
|
+
* @param s - The string to validate
|
|
27
|
+
* @returns True if the string is a valid IPv6 address
|
|
28
|
+
*/
|
|
29
|
+
export declare function isIPv6(s: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Checks whether an IPv6 address is an IPv4-mapped address (::ffff:0:0/96).
|
|
32
|
+
* @param addr - The IPv6 address
|
|
33
|
+
* @returns True if the address is IPv4-mapped
|
|
34
|
+
*/
|
|
35
|
+
export declare function isIPv4Mapped(addr: IPv6Addr): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Expands an IPv6 address into eight 16-bit words.
|
|
38
|
+
* @param addr - The IPv6 address
|
|
39
|
+
* @returns Tuple of eight 16-bit group values
|
|
40
|
+
*/
|
|
41
|
+
export declare function ipv6ToWords(addr: IPv6Addr): [number, number, number, number, number, number, number, number];
|
|
42
|
+
/**
|
|
43
|
+
* Converts an IPv6 address to zero-padded colon notation (e.g. "0000:0000:...:0001").
|
|
44
|
+
* @param addr - The IPv6 address
|
|
45
|
+
* @returns The zero-padded string representation
|
|
46
|
+
*/
|
|
47
|
+
export declare function ipv6ToFixedString(addr: IPv6Addr): string;
|
|
48
|
+
/**
|
|
49
|
+
* Converts an IPv6 address to uncompressed colon notation without zero-padding (e.g. "0:0:...:1").
|
|
50
|
+
* @param addr - The IPv6 address
|
|
51
|
+
* @returns The uncompressed string representation
|
|
52
|
+
*/
|
|
53
|
+
export declare function ipv6ToNormalizedString(addr: IPv6Addr): string;
|
|
54
|
+
/**
|
|
55
|
+
* Converts an IPv6 address to its shortest RFC 5952 representation
|
|
56
|
+
* with :: compression for the longest zero-group run.
|
|
57
|
+
* @param addr - The IPv6 address
|
|
58
|
+
* @returns The compressed string (e.g. "::1", "fe80::1%eth0")
|
|
59
|
+
*/
|
|
60
|
+
export declare function ipv6ToString(addr: IPv6Addr): string;
|
|
61
|
+
/**
|
|
62
|
+
* Extracts the embedded IPv4 address from the lower 32 bits of an IPv6 address.
|
|
63
|
+
* @param addr - The IPv6 address (typically an IPv4-mapped or IPv4-compatible address)
|
|
64
|
+
* @returns The packed uint32 IPv4 address
|
|
65
|
+
*/
|
|
66
|
+
export declare function ipv6ToIPv4(addr: IPv6Addr): number;
|
|
67
|
+
//# sourceMappingURL=ipv6.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ipv6.d.ts","sourceRoot":"","sources":["../../src/internal/ipv6.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAoC3C,eAAO,MAAM,UAAU,0BAAqB,CAAA;AAgC5C;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAEpD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAuG5D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAElE;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAO5G;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAExD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAE7D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CA4CnD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAEjD"}
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { parseIPv4Raw } from "./ipv4.js";
|
|
2
|
+
// --- Constants & lookup tables ---
|
|
3
|
+
// Hex digit lookup: maps charCode to nibble value (0-15), or 0xFF for non-hex
|
|
4
|
+
const HEX = new Uint8Array(256);
|
|
5
|
+
HEX.fill(0xFF);
|
|
6
|
+
for (let i = 0; i < 10; i++)
|
|
7
|
+
HEX[0x30 + i] = i;
|
|
8
|
+
for (let i = 0; i < 6; i++) {
|
|
9
|
+
HEX[0x41 + i] = 10 + i;
|
|
10
|
+
HEX[0x61 + i] = 10 + i;
|
|
11
|
+
}
|
|
12
|
+
// Hex string tables for all 16-bit values, used by toString functions
|
|
13
|
+
const HEX4_CACHE = new Array(65536);
|
|
14
|
+
for (let i = 0; i < 65536; i++)
|
|
15
|
+
HEX4_CACHE[i] = i.toString(16);
|
|
16
|
+
const HEX4_COLON = new Array(65536);
|
|
17
|
+
for (let i = 0; i < 65536; i++)
|
|
18
|
+
HEX4_COLON[i] = HEX4_CACHE[i] + ':';
|
|
19
|
+
const HEX4PAD_CACHE = new Array(65536);
|
|
20
|
+
for (let i = 0; i < 65536; i++)
|
|
21
|
+
HEX4PAD_CACHE[i] = i.toString(16).padStart(4, '0');
|
|
22
|
+
const HEX4PAD_COLON = new Array(65536);
|
|
23
|
+
for (let i = 0; i < 65536; i++)
|
|
24
|
+
HEX4PAD_COLON[i] = HEX4PAD_CACHE[i] + ':';
|
|
25
|
+
// --- Mutable state ---
|
|
26
|
+
// Zone IDs stored separately to keep IPv6Addr object property count minimal
|
|
27
|
+
const _zoneIds = new WeakMap();
|
|
28
|
+
// Scratch buffer for group accumulation during parsing
|
|
29
|
+
const _parts = new Uint16Array(8);
|
|
30
|
+
// Scratch buffer for toString compression path
|
|
31
|
+
const _toStrParts = new Uint16Array(8);
|
|
32
|
+
// Shared parse result buffer written by parseIPv6Raw: [w0, w1, w2, w3]
|
|
33
|
+
export const ipv6RawBuf = new Uint32Array(4);
|
|
34
|
+
let _rawZS = -1;
|
|
35
|
+
let _rawEnd = 0;
|
|
36
|
+
// --- Non-exported helpers ---
|
|
37
|
+
function _parseIPv6(s, end) {
|
|
38
|
+
if (!parseIPv6Raw(s, end))
|
|
39
|
+
return null;
|
|
40
|
+
const addr = {
|
|
41
|
+
w0: ipv6RawBuf[0],
|
|
42
|
+
w1: ipv6RawBuf[1],
|
|
43
|
+
w2: ipv6RawBuf[2],
|
|
44
|
+
w3: ipv6RawBuf[3],
|
|
45
|
+
kind: 6,
|
|
46
|
+
};
|
|
47
|
+
if (_rawZS !== -1)
|
|
48
|
+
_zoneIds.set(addr, s.slice(_rawZS, _rawEnd));
|
|
49
|
+
return addr;
|
|
50
|
+
}
|
|
51
|
+
function _ipv6FullStr(addr, colonLut, lastLut) {
|
|
52
|
+
const w0 = addr.w0, w1 = addr.w1, w2 = addr.w2, w3 = addr.w3;
|
|
53
|
+
let s = colonLut[(w0 >>> 16) & 0xFFFF] + colonLut[w0 & 0xFFFF] +
|
|
54
|
+
colonLut[(w1 >>> 16) & 0xFFFF] + colonLut[w1 & 0xFFFF] +
|
|
55
|
+
colonLut[(w2 >>> 16) & 0xFFFF] + colonLut[w2 & 0xFFFF] +
|
|
56
|
+
colonLut[(w3 >>> 16) & 0xFFFF] + lastLut[w3 & 0xFFFF];
|
|
57
|
+
const z = _zoneIds.get(addr);
|
|
58
|
+
if (z)
|
|
59
|
+
s += '%' + z;
|
|
60
|
+
return s;
|
|
61
|
+
}
|
|
62
|
+
// --- Exports ---
|
|
63
|
+
/**
|
|
64
|
+
* Retrieves the zone ID (e.g. "eth0") associated with a parsed IPv6 address.
|
|
65
|
+
* @param addr - The IPv6 address
|
|
66
|
+
* @returns The zone ID string, or empty string if none
|
|
67
|
+
*/
|
|
68
|
+
export function ipv6GetZoneId(addr) {
|
|
69
|
+
return _zoneIds.get(addr) ?? '';
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Parses an IPv6 string into raw uint32 words written to {@link ipv6RawBuf}.
|
|
73
|
+
* @param s - Source string
|
|
74
|
+
* @param end - End index (exclusive) within the string
|
|
75
|
+
* @returns True if valid, with results in ipv6RawBuf[0..3]
|
|
76
|
+
*/
|
|
77
|
+
export function parseIPv6Raw(s, end) {
|
|
78
|
+
if (end < 2 || end > 70)
|
|
79
|
+
return false;
|
|
80
|
+
let zoneStart = -1;
|
|
81
|
+
let bodyEnd = end;
|
|
82
|
+
for (let i = end - 1; i >= 0; i--) {
|
|
83
|
+
const ch = s.charCodeAt(i);
|
|
84
|
+
if (ch === 37) {
|
|
85
|
+
if (i === end - 1)
|
|
86
|
+
return false;
|
|
87
|
+
zoneStart = i + 1;
|
|
88
|
+
bodyEnd = i;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
if (ch === 58)
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
const c0 = s.charCodeAt(0);
|
|
95
|
+
if (c0 === 58) {
|
|
96
|
+
if (bodyEnd < 2 || s.charCodeAt(1) !== 58)
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const parts = _parts;
|
|
100
|
+
let pIdx = 0;
|
|
101
|
+
let dcPart = -1;
|
|
102
|
+
let pos = 0;
|
|
103
|
+
if (c0 === 58) {
|
|
104
|
+
pos = 2;
|
|
105
|
+
dcPart = 0;
|
|
106
|
+
}
|
|
107
|
+
let gVal = 0;
|
|
108
|
+
let gLen = 0;
|
|
109
|
+
while (pos < bodyEnd) {
|
|
110
|
+
const ch = s.charCodeAt(pos);
|
|
111
|
+
if (ch === 58) {
|
|
112
|
+
if (gLen > 0) {
|
|
113
|
+
if (pIdx >= 8)
|
|
114
|
+
return false;
|
|
115
|
+
parts[pIdx++] = gVal;
|
|
116
|
+
gVal = 0;
|
|
117
|
+
gLen = 0;
|
|
118
|
+
if (pos + 1 < bodyEnd && s.charCodeAt(pos + 1) === 58) {
|
|
119
|
+
if (dcPart !== -1)
|
|
120
|
+
return false;
|
|
121
|
+
dcPart = pIdx;
|
|
122
|
+
pos += 2;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
pos++;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (ch === 46) {
|
|
133
|
+
const v4Start = pos - gLen;
|
|
134
|
+
const packed = parseIPv4Raw(s, v4Start, bodyEnd);
|
|
135
|
+
if (packed === -1)
|
|
136
|
+
return false;
|
|
137
|
+
if (pIdx >= 7)
|
|
138
|
+
return false;
|
|
139
|
+
parts[pIdx++] = (packed >>> 16) & 0xFFFF;
|
|
140
|
+
parts[pIdx++] = packed & 0xFFFF;
|
|
141
|
+
gVal = 0;
|
|
142
|
+
gLen = 0;
|
|
143
|
+
pos = bodyEnd;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (ch >= 128)
|
|
147
|
+
return false;
|
|
148
|
+
const nibble = HEX[ch];
|
|
149
|
+
if (nibble === 0xFF)
|
|
150
|
+
return false;
|
|
151
|
+
gVal = (gVal << 4) | nibble;
|
|
152
|
+
if (++gLen > 4)
|
|
153
|
+
return false;
|
|
154
|
+
pos++;
|
|
155
|
+
}
|
|
156
|
+
if (gLen > 0) {
|
|
157
|
+
if (pIdx >= 8)
|
|
158
|
+
return false;
|
|
159
|
+
parts[pIdx++] = gVal;
|
|
160
|
+
}
|
|
161
|
+
else if (bodyEnd > 0 && s.charCodeAt(bodyEnd - 1) === 58 && !(bodyEnd >= 2 && s.charCodeAt(bodyEnd - 2) === 58)) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
if (dcPart !== -1) {
|
|
165
|
+
if (pIdx > 7)
|
|
166
|
+
return false;
|
|
167
|
+
const zeros = 8 - pIdx;
|
|
168
|
+
if (zeros <= 0)
|
|
169
|
+
return false;
|
|
170
|
+
for (let j = pIdx - 1; j >= dcPart; j--)
|
|
171
|
+
parts[j + zeros] = parts[j];
|
|
172
|
+
for (let j = dcPart; j < dcPart + zeros; j++)
|
|
173
|
+
parts[j] = 0;
|
|
174
|
+
pIdx = 8;
|
|
175
|
+
}
|
|
176
|
+
if (pIdx !== 8)
|
|
177
|
+
return false;
|
|
178
|
+
ipv6RawBuf[0] = ((parts[0] << 16) | parts[1]) >>> 0;
|
|
179
|
+
ipv6RawBuf[1] = ((parts[2] << 16) | parts[3]) >>> 0;
|
|
180
|
+
ipv6RawBuf[2] = ((parts[4] << 16) | parts[5]) >>> 0;
|
|
181
|
+
ipv6RawBuf[3] = ((parts[6] << 16) | parts[7]) >>> 0;
|
|
182
|
+
_rawZS = zoneStart;
|
|
183
|
+
_rawEnd = end;
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Parses an IPv6 string into an IPv6Addr. Supports :: compression,
|
|
188
|
+
* embedded IPv4 suffixes, and zone IDs (e.g. "fe80::1%eth0").
|
|
189
|
+
* @param s - The IPv6 string
|
|
190
|
+
* @param end - Optional end index for parsing a substring
|
|
191
|
+
* @returns The parsed address, or null if invalid
|
|
192
|
+
*/
|
|
193
|
+
export function parseIPv6(s, end) {
|
|
194
|
+
return _parseIPv6(s, end !== undefined ? end : s.length);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Checks whether a string is a valid IPv6 address.
|
|
198
|
+
* @param s - The string to validate
|
|
199
|
+
* @returns True if the string is a valid IPv6 address
|
|
200
|
+
*/
|
|
201
|
+
export function isIPv6(s) {
|
|
202
|
+
return parseIPv6Raw(s, s.length);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Checks whether an IPv6 address is an IPv4-mapped address (::ffff:0:0/96).
|
|
206
|
+
* @param addr - The IPv6 address
|
|
207
|
+
* @returns True if the address is IPv4-mapped
|
|
208
|
+
*/
|
|
209
|
+
export function isIPv4Mapped(addr) {
|
|
210
|
+
return addr.w0 === 0 && addr.w1 === 0 && addr.w2 === 0x0000FFFF;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Expands an IPv6 address into eight 16-bit words.
|
|
214
|
+
* @param addr - The IPv6 address
|
|
215
|
+
* @returns Tuple of eight 16-bit group values
|
|
216
|
+
*/
|
|
217
|
+
export function ipv6ToWords(addr) {
|
|
218
|
+
return [
|
|
219
|
+
(addr.w0 >>> 16) & 0xFFFF, addr.w0 & 0xFFFF,
|
|
220
|
+
(addr.w1 >>> 16) & 0xFFFF, addr.w1 & 0xFFFF,
|
|
221
|
+
(addr.w2 >>> 16) & 0xFFFF, addr.w2 & 0xFFFF,
|
|
222
|
+
(addr.w3 >>> 16) & 0xFFFF, addr.w3 & 0xFFFF,
|
|
223
|
+
];
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Converts an IPv6 address to zero-padded colon notation (e.g. "0000:0000:...:0001").
|
|
227
|
+
* @param addr - The IPv6 address
|
|
228
|
+
* @returns The zero-padded string representation
|
|
229
|
+
*/
|
|
230
|
+
export function ipv6ToFixedString(addr) {
|
|
231
|
+
return _ipv6FullStr(addr, HEX4PAD_COLON, HEX4PAD_CACHE);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Converts an IPv6 address to uncompressed colon notation without zero-padding (e.g. "0:0:...:1").
|
|
235
|
+
* @param addr - The IPv6 address
|
|
236
|
+
* @returns The uncompressed string representation
|
|
237
|
+
*/
|
|
238
|
+
export function ipv6ToNormalizedString(addr) {
|
|
239
|
+
return _ipv6FullStr(addr, HEX4_COLON, HEX4_CACHE);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Converts an IPv6 address to its shortest RFC 5952 representation
|
|
243
|
+
* with :: compression for the longest zero-group run.
|
|
244
|
+
* @param addr - The IPv6 address
|
|
245
|
+
* @returns The compressed string (e.g. "::1", "fe80::1%eth0")
|
|
246
|
+
*/
|
|
247
|
+
export function ipv6ToString(addr) {
|
|
248
|
+
const w0 = addr.w0, w1 = addr.w1, w2 = addr.w2, w3 = addr.w3;
|
|
249
|
+
const p0 = (w0 >>> 16) & 0xFFFF, p1 = w0 & 0xFFFF;
|
|
250
|
+
const p2 = (w1 >>> 16) & 0xFFFF, p3 = w1 & 0xFFFF;
|
|
251
|
+
const p4 = (w2 >>> 16) & 0xFFFF, p5 = w2 & 0xFFFF;
|
|
252
|
+
const p6 = (w3 >>> 16) & 0xFFFF, p7 = w3 & 0xFFFF;
|
|
253
|
+
// Find longest run of consecutive zero groups (minimum length 2)
|
|
254
|
+
let bestStart = -1, bestLen = 0, curStart = -1, curLen = 0;
|
|
255
|
+
if (p0 === 0) {
|
|
256
|
+
curStart = 0;
|
|
257
|
+
curLen = 1;
|
|
258
|
+
}
|
|
259
|
+
if (p1 === 0) {
|
|
260
|
+
if (curStart === -1)
|
|
261
|
+
curStart = 1;
|
|
262
|
+
curLen++;
|
|
263
|
+
if (curLen > bestLen && curLen >= 2) {
|
|
264
|
+
bestStart = curStart;
|
|
265
|
+
bestLen = curLen;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
curStart = -1;
|
|
270
|
+
curLen = 0;
|
|
271
|
+
}
|
|
272
|
+
if (p2 === 0) {
|
|
273
|
+
if (curStart === -1)
|
|
274
|
+
curStart = 2;
|
|
275
|
+
curLen++;
|
|
276
|
+
if (curLen > bestLen && curLen >= 2) {
|
|
277
|
+
bestStart = curStart;
|
|
278
|
+
bestLen = curLen;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
curStart = -1;
|
|
283
|
+
curLen = 0;
|
|
284
|
+
}
|
|
285
|
+
if (p3 === 0) {
|
|
286
|
+
if (curStart === -1)
|
|
287
|
+
curStart = 3;
|
|
288
|
+
curLen++;
|
|
289
|
+
if (curLen > bestLen && curLen >= 2) {
|
|
290
|
+
bestStart = curStart;
|
|
291
|
+
bestLen = curLen;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
curStart = -1;
|
|
296
|
+
curLen = 0;
|
|
297
|
+
}
|
|
298
|
+
if (p4 === 0) {
|
|
299
|
+
if (curStart === -1)
|
|
300
|
+
curStart = 4;
|
|
301
|
+
curLen++;
|
|
302
|
+
if (curLen > bestLen && curLen >= 2) {
|
|
303
|
+
bestStart = curStart;
|
|
304
|
+
bestLen = curLen;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
curStart = -1;
|
|
309
|
+
curLen = 0;
|
|
310
|
+
}
|
|
311
|
+
if (p5 === 0) {
|
|
312
|
+
if (curStart === -1)
|
|
313
|
+
curStart = 5;
|
|
314
|
+
curLen++;
|
|
315
|
+
if (curLen > bestLen && curLen >= 2) {
|
|
316
|
+
bestStart = curStart;
|
|
317
|
+
bestLen = curLen;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
curStart = -1;
|
|
322
|
+
curLen = 0;
|
|
323
|
+
}
|
|
324
|
+
if (p6 === 0) {
|
|
325
|
+
if (curStart === -1)
|
|
326
|
+
curStart = 6;
|
|
327
|
+
curLen++;
|
|
328
|
+
if (curLen > bestLen && curLen >= 2) {
|
|
329
|
+
bestStart = curStart;
|
|
330
|
+
bestLen = curLen;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
curStart = -1;
|
|
335
|
+
curLen = 0;
|
|
336
|
+
}
|
|
337
|
+
if (p7 === 0) {
|
|
338
|
+
if (curStart === -1)
|
|
339
|
+
curStart = 7;
|
|
340
|
+
curLen++;
|
|
341
|
+
if (curLen > bestLen && curLen >= 2) {
|
|
342
|
+
bestStart = curStart;
|
|
343
|
+
bestLen = curLen;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (bestLen === 0)
|
|
347
|
+
return _ipv6FullStr(addr, HEX4_COLON, HEX4_CACHE);
|
|
348
|
+
const z = _zoneIds.get(addr);
|
|
349
|
+
_toStrParts[0] = p0;
|
|
350
|
+
_toStrParts[1] = p1;
|
|
351
|
+
_toStrParts[2] = p2;
|
|
352
|
+
_toStrParts[3] = p3;
|
|
353
|
+
_toStrParts[4] = p4;
|
|
354
|
+
_toStrParts[5] = p5;
|
|
355
|
+
_toStrParts[6] = p6;
|
|
356
|
+
_toStrParts[7] = p7;
|
|
357
|
+
let s = '';
|
|
358
|
+
if (bestStart === 0) {
|
|
359
|
+
s = '::';
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
for (let i = 0; i < bestStart - 1; i++) {
|
|
363
|
+
s += HEX4_COLON[_toStrParts[i]];
|
|
364
|
+
}
|
|
365
|
+
s += HEX4_CACHE[_toStrParts[bestStart - 1]];
|
|
366
|
+
s += '::';
|
|
367
|
+
}
|
|
368
|
+
const afterIdx = bestStart + bestLen;
|
|
369
|
+
for (let i = afterIdx; i < 8; i++) {
|
|
370
|
+
if (i > afterIdx)
|
|
371
|
+
s += ':';
|
|
372
|
+
s += HEX4_CACHE[_toStrParts[i]];
|
|
373
|
+
}
|
|
374
|
+
if (z)
|
|
375
|
+
s += '%' + z;
|
|
376
|
+
return s;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Extracts the embedded IPv4 address from the lower 32 bits of an IPv6 address.
|
|
380
|
+
* @param addr - The IPv6 address (typically an IPv4-mapped or IPv4-compatible address)
|
|
381
|
+
* @returns The packed uint32 IPv4 address
|
|
382
|
+
*/
|
|
383
|
+
export function ipv6ToIPv4(addr) {
|
|
384
|
+
return addr.w3;
|
|
385
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface Options {
|
|
2
|
+
readonly exact?: boolean;
|
|
3
|
+
readonly includeBoundaries?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export interface ScanResult {
|
|
6
|
+
readonly value: string;
|
|
7
|
+
readonly index: number;
|
|
8
|
+
}
|
|
9
|
+
export interface IpRegex {
|
|
10
|
+
(options?: Options): RegExp;
|
|
11
|
+
v4(options?: Options): RegExp;
|
|
12
|
+
v6(options?: Options): RegExp;
|
|
13
|
+
scanAll(input: string, options?: Options): ScanResult[];
|
|
14
|
+
scanAllV4(input: string, options?: Options): ScanResult[];
|
|
15
|
+
scanAllV6(input: string, options?: Options): ScanResult[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Tests whether a string is a valid IPv4 address using regex.
|
|
19
|
+
* @param s - The string to test
|
|
20
|
+
* @returns True if the string matches IPv4 format
|
|
21
|
+
*/
|
|
22
|
+
export declare function testV4(s: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Tests whether a string is a valid IPv6 address using regex.
|
|
25
|
+
* @param s - The string to test
|
|
26
|
+
* @returns True if the string matches IPv6 format
|
|
27
|
+
*/
|
|
28
|
+
export declare function testV6(s: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Tests whether a string is a valid IPv4 or IPv6 address using regex.
|
|
31
|
+
* @param s - The string to test
|
|
32
|
+
* @returns True if the string matches either format
|
|
33
|
+
*/
|
|
34
|
+
export declare function testV46(s: string): boolean;
|
|
35
|
+
declare const ipRegex: IpRegex;
|
|
36
|
+
export default ipRegex;
|
|
37
|
+
//# sourceMappingURL=regex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regex.d.ts","sourceRoot":"","sources":["../../src/internal/regex.ts"],"names":[],"mappings":"AA6CA,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;CACrC;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,OAAO;IACtB,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IAC3B,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IAC7B,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IAC7B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,UAAU,EAAE,CAAA;IACvD,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,UAAU,EAAE,CAAA;IACzD,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,UAAU,EAAE,CAAA;CAC1D;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAE1C;AAwCD,QAAA,MAAM,OAAO,EAKP,OAAO,CAAA;AAoBb,eAAe,OAAO,CAAA"}
|