cidr-tools 11.3.0 → 11.3.1
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/dist/index.js +96 -78
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5,12 +5,15 @@ const bits = {
|
|
|
5
5
|
4: 32,
|
|
6
6
|
6: 128
|
|
7
7
|
};
|
|
8
|
-
|
|
8
|
+
const octetStrings = Array.from({ length: 256 }, (_, i) => String(i));
|
|
9
|
+
const prefixStrings = Array.from({ length: 129 }, (_, i) => `/${i}`);
|
|
10
|
+
const prefixNumStrings = Array.from({ length: 129 }, (_, i) => String(i));
|
|
11
|
+
function parseIPv4Fast(s, end) {
|
|
9
12
|
let num = 0;
|
|
10
13
|
let octet = 0;
|
|
11
14
|
let dots = 0;
|
|
12
15
|
let digits = 0;
|
|
13
|
-
for (let i = 0; i <
|
|
16
|
+
for (let i = 0; i < end; i++) {
|
|
14
17
|
const c = s.charCodeAt(i);
|
|
15
18
|
if (c === 46) {
|
|
16
19
|
if (digits === 0 || octet > 255) return -1;
|
|
@@ -27,7 +30,7 @@ function parseIPv4Fast(s) {
|
|
|
27
30
|
return (num << 8 | octet) >>> 0;
|
|
28
31
|
}
|
|
29
32
|
function formatIPv4Fast(n) {
|
|
30
|
-
return `${n >>> 24 & 255}.${n >>> 16 & 255}.${n >>> 8 & 255}.${n & 255}`;
|
|
33
|
+
return `${octetStrings[n >>> 24 & 255]}.${octetStrings[n >>> 16 & 255]}.${octetStrings[n >>> 8 & 255]}.${octetStrings[n & 255]}`;
|
|
31
34
|
}
|
|
32
35
|
function parsePrefixNum(str, slashIndex) {
|
|
33
36
|
if (slashIndex === -1) return -1;
|
|
@@ -40,19 +43,44 @@ function parsePrefixNum(str, slashIndex) {
|
|
|
40
43
|
}
|
|
41
44
|
return prefixNum;
|
|
42
45
|
}
|
|
43
|
-
|
|
46
|
+
let rangeV4Start = 0;
|
|
47
|
+
let rangeV4End = 0;
|
|
48
|
+
let rangeV4Prefix = 0;
|
|
49
|
+
let rangeSlashIndex = -1;
|
|
50
|
+
function parseIPv4Range(str) {
|
|
51
|
+
rangeSlashIndex = str.indexOf("/");
|
|
52
|
+
const v4num = parseIPv4Fast(str, rangeSlashIndex !== -1 ? rangeSlashIndex : str.length);
|
|
53
|
+
if (v4num === -1) return false;
|
|
54
|
+
rangeV4Prefix = rangeSlashIndex !== -1 ? parsePrefixNum(str, rangeSlashIndex) : 32;
|
|
55
|
+
const hostBits = 32 - rangeV4Prefix;
|
|
56
|
+
if (hostBits >= 32) {
|
|
57
|
+
rangeV4Start = 0;
|
|
58
|
+
rangeV4End = 4294967295;
|
|
59
|
+
} else {
|
|
60
|
+
const mask = hostBits > 0 ? (1 << hostBits >>> 0) - 1 : 0;
|
|
61
|
+
rangeV4Start = (v4num & ~mask) >>> 0;
|
|
62
|
+
rangeV4End = (v4num | mask) >>> 0;
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
function doNormalize(cidr, opts) {
|
|
67
|
+
if (parseIPv4Range(cidr)) {
|
|
68
|
+
const ip = formatIPv4Fast(rangeV4Start);
|
|
69
|
+
return rangeSlashIndex !== -1 ? ip + prefixStrings[rangeV4Prefix] : ip;
|
|
70
|
+
}
|
|
44
71
|
const { start, end, prefix, version, prefixPresent } = parseCidr(cidr);
|
|
45
72
|
if (version === 4) {
|
|
46
73
|
const ip = formatIPv4Fast(Number(start));
|
|
47
|
-
return start !== end || prefixPresent ?
|
|
74
|
+
return start !== end || prefixPresent ? ip + prefixStrings[Number(prefix)] : ip;
|
|
48
75
|
}
|
|
49
|
-
|
|
76
|
+
const { compress = true, hexify = false } = opts || {};
|
|
77
|
+
if (start !== end || prefixPresent) return normalizeIp(stringifyIp({
|
|
50
78
|
number: start,
|
|
51
79
|
version
|
|
52
80
|
}), {
|
|
53
81
|
compress,
|
|
54
82
|
hexify
|
|
55
|
-
})
|
|
83
|
+
}) + prefixStrings[Number(prefix)];
|
|
56
84
|
else return normalizeIp(cidr, {
|
|
57
85
|
compress,
|
|
58
86
|
hexify
|
|
@@ -66,49 +94,39 @@ function normalizeCidr(cidr, opts) {
|
|
|
66
94
|
/** Returns a `parsed` Object which is used internally by this module. It can be used to test whether the passed network is IPv4 or IPv6 or to work with the BigInts directly. */
|
|
67
95
|
function parseCidr(str) {
|
|
68
96
|
const slashIndex = str.indexOf("/");
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const ip = formatIPv4Fast(v4num);
|
|
86
|
-
const prefix = String(prefixNum);
|
|
87
|
-
const hostBits = 32 - prefixNum;
|
|
88
|
-
let startNum, endNum;
|
|
89
|
-
if (hostBits >= 32) {
|
|
90
|
-
startNum = 0;
|
|
91
|
-
endNum = 4294967295;
|
|
92
|
-
} else {
|
|
93
|
-
const mask = hostBits > 0 ? (1 << hostBits >>> 0) - 1 : 0;
|
|
94
|
-
startNum = (v4num & ~mask) >>> 0;
|
|
95
|
-
endNum = (v4num | mask) >>> 0;
|
|
96
|
-
}
|
|
97
|
-
return {
|
|
98
|
-
cidr: `${ip}/${prefix}`,
|
|
99
|
-
ip,
|
|
100
|
-
version: 4,
|
|
101
|
-
prefix,
|
|
102
|
-
prefixPresent,
|
|
103
|
-
start: BigInt(startNum),
|
|
104
|
-
end: BigInt(endNum)
|
|
105
|
-
};
|
|
97
|
+
const ipEnd = slashIndex !== -1 ? slashIndex : str.length;
|
|
98
|
+
const prefixPresent = slashIndex !== -1;
|
|
99
|
+
const v4num = parseIPv4Fast(str, ipEnd);
|
|
100
|
+
if (v4num !== -1) {
|
|
101
|
+
const prefixNum = prefixPresent ? parsePrefixNum(str, slashIndex) : 32;
|
|
102
|
+
const ip = formatIPv4Fast(v4num);
|
|
103
|
+
const prefix = prefixNumStrings[prefixNum];
|
|
104
|
+
const hostBits = 32 - prefixNum;
|
|
105
|
+
let startNum, endNum;
|
|
106
|
+
if (hostBits >= 32) {
|
|
107
|
+
startNum = 0;
|
|
108
|
+
endNum = 4294967295;
|
|
109
|
+
} else {
|
|
110
|
+
const mask = hostBits > 0 ? (1 << hostBits >>> 0) - 1 : 0;
|
|
111
|
+
startNum = (v4num & ~mask) >>> 0;
|
|
112
|
+
endNum = (v4num | mask) >>> 0;
|
|
106
113
|
}
|
|
114
|
+
return {
|
|
115
|
+
cidr: ip + prefixStrings[prefixNum],
|
|
116
|
+
ip,
|
|
117
|
+
version: 4,
|
|
118
|
+
prefix,
|
|
119
|
+
prefixPresent,
|
|
120
|
+
start: BigInt(startNum),
|
|
121
|
+
end: BigInt(endNum)
|
|
122
|
+
};
|
|
107
123
|
}
|
|
124
|
+
const ipPart = prefixPresent ? str.substring(0, slashIndex) : str;
|
|
125
|
+
let prefixNum = prefixPresent ? parsePrefixNum(str, slashIndex) : -1;
|
|
108
126
|
const { number, version, ipv4mapped, scopeid } = parseIp(ipPart);
|
|
109
127
|
if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
|
|
110
128
|
if (prefixNum === -1) prefixNum = bits[version];
|
|
111
|
-
const prefix =
|
|
129
|
+
const prefix = prefixNumStrings[prefixNum];
|
|
112
130
|
const ip = stringifyIp({
|
|
113
131
|
number,
|
|
114
132
|
version,
|
|
@@ -118,7 +136,7 @@ function parseCidr(str) {
|
|
|
118
136
|
const hostBits = bits[version] - prefixNum;
|
|
119
137
|
const mask = hostBits > 0 ? (1n << BigInt(hostBits)) - 1n : 0n;
|
|
120
138
|
return {
|
|
121
|
-
cidr:
|
|
139
|
+
cidr: ip + prefixStrings[prefixNum],
|
|
122
140
|
ip,
|
|
123
141
|
version,
|
|
124
142
|
prefix,
|
|
@@ -128,34 +146,14 @@ function parseCidr(str) {
|
|
|
128
146
|
};
|
|
129
147
|
}
|
|
130
148
|
function parseCidrLean(str) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
prefixNum = -1;
|
|
140
|
-
}
|
|
141
|
-
if (!ipPart.includes(":")) {
|
|
142
|
-
const v4num = parseIPv4Fast(ipPart);
|
|
143
|
-
if (v4num !== -1) {
|
|
144
|
-
if (prefixNum === -1) prefixNum = 32;
|
|
145
|
-
const hostBits = 32 - prefixNum;
|
|
146
|
-
if (hostBits >= 32) return {
|
|
147
|
-
start: 0,
|
|
148
|
-
end: 4294967295,
|
|
149
|
-
version: 4
|
|
150
|
-
};
|
|
151
|
-
const mask = hostBits > 0 ? (1 << hostBits >>> 0) - 1 : 0;
|
|
152
|
-
return {
|
|
153
|
-
start: (v4num & ~mask) >>> 0,
|
|
154
|
-
end: (v4num | mask) >>> 0,
|
|
155
|
-
version: 4
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
}
|
|
149
|
+
if (parseIPv4Range(str)) return {
|
|
150
|
+
start: rangeV4Start,
|
|
151
|
+
end: rangeV4End,
|
|
152
|
+
version: 4
|
|
153
|
+
};
|
|
154
|
+
const slashIndex = rangeSlashIndex;
|
|
155
|
+
const ipPart = slashIndex !== -1 ? str.substring(0, slashIndex) : str;
|
|
156
|
+
let prefixNum = slashIndex !== -1 ? parsePrefixNum(str, slashIndex) : -1;
|
|
159
157
|
const { number, version } = parseIp(ipPart);
|
|
160
158
|
if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
|
|
161
159
|
if (prefixNum === -1) prefixNum = bits[version];
|
|
@@ -334,7 +332,7 @@ function subparts6(pStart, pEnd, output) {
|
|
|
334
332
|
function formatPart4(part) {
|
|
335
333
|
const ip = formatIPv4Fast(part.start);
|
|
336
334
|
const size = part.end - part.start + 1;
|
|
337
|
-
return
|
|
335
|
+
return ip + prefixStrings[32 - (size <= 1 ? 0 : size >= 4294967296 ? 32 : 31 - Math.clz32(size))];
|
|
338
336
|
}
|
|
339
337
|
function formatPart6(part) {
|
|
340
338
|
const ip = stringifyIp({
|
|
@@ -342,7 +340,7 @@ function formatPart6(part) {
|
|
|
342
340
|
version: 6
|
|
343
341
|
});
|
|
344
342
|
const size = part.end - part.start + 1n;
|
|
345
|
-
return
|
|
343
|
+
return ip + prefixStrings[128 - (size <= 1n ? 0 : bigintBitLength(size) - 1)];
|
|
346
344
|
}
|
|
347
345
|
function mergeIntervalsRaw4(nets) {
|
|
348
346
|
if (nets.length === 0) return [];
|
|
@@ -518,6 +516,10 @@ function* expandCidr(nets) {
|
|
|
518
516
|
/** Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`. */
|
|
519
517
|
function overlapCidr(a, b) {
|
|
520
518
|
if (!Array.isArray(a) && !Array.isArray(b)) {
|
|
519
|
+
if (parseIPv4Range(a)) {
|
|
520
|
+
const startA = rangeV4Start, endA = rangeV4End;
|
|
521
|
+
if (parseIPv4Range(b)) return startA <= rangeV4End && rangeV4Start <= endA;
|
|
522
|
+
}
|
|
521
523
|
const pa = parseCidrLean(a);
|
|
522
524
|
const pb = parseCidrLean(b);
|
|
523
525
|
if (pa.version !== pb.version) return false;
|
|
@@ -537,7 +539,13 @@ function overlapCidr(a, b) {
|
|
|
537
539
|
if (n.version === 4) v4b.push(n);
|
|
538
540
|
else v6b.push(n);
|
|
539
541
|
}
|
|
540
|
-
if (v4a.length > 0 && v4b.length > 0) {
|
|
542
|
+
if (v4a.length > 0 && v4b.length > 0) if (v4b.length === 1) {
|
|
543
|
+
const bs = v4b[0].start, be = v4b[0].end;
|
|
544
|
+
for (const el of v4a) if (el.start <= be && bs <= el.end) return true;
|
|
545
|
+
} else if (v4a.length === 1) {
|
|
546
|
+
const as = v4a[0].start, ae = v4a[0].end;
|
|
547
|
+
for (const el of v4b) if (as <= el.end && el.start <= ae) return true;
|
|
548
|
+
} else {
|
|
541
549
|
v4a.sort((x, y) => x.start - y.start);
|
|
542
550
|
v4b.sort((x, y) => x.start - y.start);
|
|
543
551
|
let i = 0, j = 0;
|
|
@@ -547,7 +555,13 @@ function overlapCidr(a, b) {
|
|
|
547
555
|
else j++;
|
|
548
556
|
}
|
|
549
557
|
}
|
|
550
|
-
if (v6a.length > 0 && v6b.length > 0) {
|
|
558
|
+
if (v6a.length > 0 && v6b.length > 0) if (v6b.length === 1) {
|
|
559
|
+
const bs = v6b[0].start, be = v6b[0].end;
|
|
560
|
+
for (const el of v6a) if (el.start <= be && bs <= el.end) return true;
|
|
561
|
+
} else if (v6a.length === 1) {
|
|
562
|
+
const as = v6a[0].start, ae = v6a[0].end;
|
|
563
|
+
for (const el of v6b) if (as <= el.end && el.start <= ae) return true;
|
|
564
|
+
} else {
|
|
551
565
|
v6a.sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
|
|
552
566
|
v6b.sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
|
|
553
567
|
let i = 0, j = 0;
|
|
@@ -562,6 +576,10 @@ function overlapCidr(a, b) {
|
|
|
562
576
|
/** Returns a boolean that indicates whether `networksA` fully contain all `networksB`. */
|
|
563
577
|
function containsCidr(a, b) {
|
|
564
578
|
if (!Array.isArray(a) && !Array.isArray(b)) {
|
|
579
|
+
if (parseIPv4Range(a)) {
|
|
580
|
+
const startA = rangeV4Start, endA = rangeV4End;
|
|
581
|
+
if (parseIPv4Range(b)) return startA <= rangeV4Start && endA >= rangeV4End;
|
|
582
|
+
}
|
|
565
583
|
const pa = parseCidrLean(a);
|
|
566
584
|
const pb = parseCidrLean(b);
|
|
567
585
|
if (pa.version !== pb.version) return false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cidr-tools",
|
|
3
|
-
"version": "11.3.
|
|
3
|
+
"version": "11.3.1",
|
|
4
4
|
"author": "silverwind <me@silverwind.io>",
|
|
5
5
|
"description": "Tools to work with IPv4 and IPv6 CIDR",
|
|
6
6
|
"repository": "silverwind/cidr-tools",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"node": ">=18"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"ip-bigint": "^8.
|
|
20
|
+
"ip-bigint": "^8.3.1"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/node": "25.5.0",
|