cidr-tools 11.0.12 → 11.1.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/dist/index.js +90 -71
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -50,37 +50,40 @@ function normalizeCidr(cidr, opts) {
|
|
|
50
50
|
}
|
|
51
51
|
/** 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. */
|
|
52
52
|
function parseCidr(str) {
|
|
53
|
-
const parsed = Object.create(null);
|
|
54
53
|
const slashIndex = str.indexOf("/");
|
|
55
54
|
let ipPart;
|
|
56
55
|
let prefix;
|
|
56
|
+
let prefixPresent;
|
|
57
57
|
if (slashIndex !== -1) {
|
|
58
58
|
ipPart = str.substring(0, slashIndex);
|
|
59
59
|
prefix = str.substring(slashIndex + 1);
|
|
60
60
|
if (!/^[0-9]+$/.test(prefix)) throw new Error(`Network is not a CIDR or IP: "${str}"`);
|
|
61
|
-
|
|
61
|
+
prefixPresent = true;
|
|
62
62
|
} else {
|
|
63
63
|
ipPart = str;
|
|
64
|
-
|
|
64
|
+
prefixPresent = false;
|
|
65
65
|
prefix = "";
|
|
66
66
|
}
|
|
67
67
|
const { number, version, ipv4mapped, scopeid } = parseIp(ipPart);
|
|
68
68
|
if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
|
|
69
|
-
if (!
|
|
70
|
-
|
|
71
|
-
parsed.ip = stringifyIp({
|
|
69
|
+
if (!prefixPresent) prefix = String(bits[version]);
|
|
70
|
+
const ip = stringifyIp({
|
|
72
71
|
number,
|
|
73
72
|
version,
|
|
74
73
|
ipv4mapped,
|
|
75
74
|
scopeid
|
|
76
75
|
});
|
|
77
|
-
parsed.cidr = `${parsed.ip}/${prefix}`;
|
|
78
|
-
parsed.prefix = prefix;
|
|
79
76
|
const hostBits = bits[version] - Number(prefix);
|
|
80
77
|
const mask = hostBits > 0 ? (1n << BigInt(hostBits)) - 1n : 0n;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
return {
|
|
79
|
+
cidr: `${ip}/${prefix}`,
|
|
80
|
+
ip,
|
|
81
|
+
version,
|
|
82
|
+
prefix,
|
|
83
|
+
prefixPresent,
|
|
84
|
+
start: number & ~mask,
|
|
85
|
+
end: number | mask
|
|
86
|
+
};
|
|
84
87
|
}
|
|
85
88
|
function parseCidrLean(str) {
|
|
86
89
|
const slashIndex = str.indexOf("/");
|
|
@@ -88,9 +91,13 @@ function parseCidrLean(str) {
|
|
|
88
91
|
let prefixNum;
|
|
89
92
|
if (slashIndex !== -1) {
|
|
90
93
|
ipPart = str.substring(0, slashIndex);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
if (slashIndex + 1 >= str.length) throw new Error(`Network is not a CIDR or IP: "${str}"`);
|
|
95
|
+
prefixNum = 0;
|
|
96
|
+
for (let i = slashIndex + 1; i < str.length; i++) {
|
|
97
|
+
const c = str.charCodeAt(i);
|
|
98
|
+
if (c < 48 || c > 57) throw new Error(`Network is not a CIDR or IP: "${str}"`);
|
|
99
|
+
prefixNum = prefixNum * 10 + (c - 48);
|
|
100
|
+
}
|
|
94
101
|
} else {
|
|
95
102
|
ipPart = str;
|
|
96
103
|
prefixNum = -1;
|
|
@@ -99,12 +106,16 @@ function parseCidrLean(str) {
|
|
|
99
106
|
const v4num = parseIPv4Fast(ipPart);
|
|
100
107
|
if (v4num !== -1) {
|
|
101
108
|
if (prefixNum === -1) prefixNum = 32;
|
|
102
|
-
const bigNum = BigInt(v4num);
|
|
103
109
|
const hostBits = 32 - prefixNum;
|
|
104
|
-
|
|
110
|
+
if (hostBits >= 32) return {
|
|
111
|
+
start: 0n,
|
|
112
|
+
end: 4294967295n,
|
|
113
|
+
version: 4
|
|
114
|
+
};
|
|
115
|
+
const mask = hostBits > 0 ? (1 << hostBits >>> 0) - 1 : 0;
|
|
105
116
|
return {
|
|
106
|
-
start:
|
|
107
|
-
end:
|
|
117
|
+
start: BigInt((v4num & ~mask) >>> 0),
|
|
118
|
+
end: BigInt((v4num | mask) >>> 0),
|
|
108
119
|
version: 4
|
|
109
120
|
};
|
|
110
121
|
}
|
|
@@ -124,64 +135,62 @@ function biggestPowerOfTwo(num) {
|
|
|
124
135
|
if (num === 0n) return 0n;
|
|
125
136
|
return 1n << BigInt(num.toString(2).length - 1);
|
|
126
137
|
}
|
|
127
|
-
function subparts(
|
|
128
|
-
if (
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
138
|
+
function subparts(pStart, pEnd, output) {
|
|
139
|
+
if (pEnd < pStart) return;
|
|
140
|
+
if (pEnd === pStart) {
|
|
141
|
+
output.push({
|
|
142
|
+
start: pStart,
|
|
143
|
+
end: pEnd
|
|
144
|
+
});
|
|
145
|
+
return;
|
|
133
146
|
}
|
|
134
|
-
if (
|
|
135
|
-
if (
|
|
136
|
-
start:
|
|
137
|
-
end:
|
|
147
|
+
if (pEnd - pStart === 1n) {
|
|
148
|
+
if (pEnd % 2n === 0n) output.push({
|
|
149
|
+
start: pStart,
|
|
150
|
+
end: pStart
|
|
138
151
|
}, {
|
|
139
|
-
start:
|
|
140
|
-
end:
|
|
152
|
+
start: pEnd,
|
|
153
|
+
end: pEnd
|
|
141
154
|
});
|
|
142
155
|
else output.push({
|
|
143
|
-
start:
|
|
144
|
-
end:
|
|
156
|
+
start: pStart,
|
|
157
|
+
end: pEnd
|
|
145
158
|
});
|
|
146
|
-
return
|
|
159
|
+
return;
|
|
147
160
|
}
|
|
148
|
-
const size = diff(
|
|
161
|
+
const size = diff(pEnd, pStart);
|
|
149
162
|
let biggest = biggestPowerOfTwo(size);
|
|
150
163
|
let start;
|
|
151
164
|
let end;
|
|
152
|
-
if (size === biggest &&
|
|
153
|
-
output.push(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
165
|
+
if (size === biggest && pStart % biggest === 0n) {
|
|
166
|
+
output.push({
|
|
167
|
+
start: pStart,
|
|
168
|
+
end: pEnd
|
|
169
|
+
});
|
|
170
|
+
return;
|
|
171
|
+
} else if (pStart % biggest === 0n) {
|
|
172
|
+
start = pStart;
|
|
157
173
|
end = start + biggest - 1n;
|
|
158
174
|
} else {
|
|
159
|
-
start =
|
|
160
|
-
if (start + biggest - 1n >
|
|
161
|
-
start = (
|
|
162
|
-
while (start <
|
|
175
|
+
start = pEnd / biggest * biggest;
|
|
176
|
+
if (start + biggest - 1n > pEnd) {
|
|
177
|
+
start = (pEnd / biggest - 1n) * biggest;
|
|
178
|
+
while (start < pStart) {
|
|
163
179
|
biggest /= 2n;
|
|
164
|
-
start = (
|
|
180
|
+
start = (pEnd / biggest - 1n) * biggest;
|
|
165
181
|
}
|
|
166
182
|
end = start + biggest - 1n;
|
|
167
183
|
} else {
|
|
168
|
-
start =
|
|
184
|
+
start = pEnd / biggest * biggest;
|
|
169
185
|
end = start + biggest - 1n;
|
|
170
186
|
}
|
|
171
187
|
}
|
|
172
|
-
if (start !==
|
|
173
|
-
start: part.start,
|
|
174
|
-
end: start - 1n
|
|
175
|
-
}, output);
|
|
188
|
+
if (start !== pStart) subparts(pStart, start - 1n, output);
|
|
176
189
|
output.push({
|
|
177
190
|
start,
|
|
178
191
|
end
|
|
179
192
|
});
|
|
180
|
-
if (end !==
|
|
181
|
-
start: end + 1n,
|
|
182
|
-
end: part.end
|
|
183
|
-
}, output);
|
|
184
|
-
return output;
|
|
193
|
+
if (end !== pEnd) subparts(end + 1n, pEnd, output);
|
|
185
194
|
}
|
|
186
195
|
function diff(a, b) {
|
|
187
196
|
return a + 1n - b;
|
|
@@ -202,12 +211,12 @@ function formatPart(part, version) {
|
|
|
202
211
|
}
|
|
203
212
|
function mergeIntervalsRaw(nets) {
|
|
204
213
|
if (nets.length === 0) return [];
|
|
205
|
-
|
|
214
|
+
nets.sort((a, b) => a.start > b.start ? 1 : a.start < b.start ? -1 : a.end > b.end ? 1 : a.end < b.end ? -1 : 0);
|
|
206
215
|
const merged = [];
|
|
207
|
-
let curStart =
|
|
208
|
-
let curEnd =
|
|
209
|
-
for (let i = 1; i <
|
|
210
|
-
const { start, end } =
|
|
216
|
+
let curStart = nets[0].start;
|
|
217
|
+
let curEnd = nets[0].end;
|
|
218
|
+
for (let i = 1; i < nets.length; i++) {
|
|
219
|
+
const { start, end } = nets[i];
|
|
211
220
|
if (start <= curEnd + 1n) {
|
|
212
221
|
if (end > curEnd) curEnd = end;
|
|
213
222
|
} else {
|
|
@@ -227,7 +236,7 @@ function mergeIntervalsRaw(nets) {
|
|
|
227
236
|
}
|
|
228
237
|
function mergeIntervals(nets) {
|
|
229
238
|
const merged = [];
|
|
230
|
-
for (const part of mergeIntervalsRaw(nets)) subparts(part, merged);
|
|
239
|
+
for (const part of mergeIntervalsRaw(nets)) subparts(part.start, part.end, merged);
|
|
231
240
|
return merged;
|
|
232
241
|
}
|
|
233
242
|
function subtractSorted(bases, excls) {
|
|
@@ -284,21 +293,31 @@ function excludeCidr(base, excl) {
|
|
|
284
293
|
const result = [];
|
|
285
294
|
for (const v of [4, 6]) {
|
|
286
295
|
const remaining = subtractSorted(mergeIntervalsRaw(baseByVersion[v]), mergeIntervalsRaw(exclByVersion[v]));
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
296
|
+
const aligned = [];
|
|
297
|
+
for (const part of remaining) subparts(part.start, part.end, aligned);
|
|
298
|
+
for (const p of aligned) result.push(formatPart(p, v));
|
|
291
299
|
}
|
|
292
300
|
return result;
|
|
293
301
|
}
|
|
294
302
|
function* expandCidr(nets) {
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
303
|
+
const parsed = (Array.isArray(nets) ? nets : [nets]).map(parseCidrLean);
|
|
304
|
+
const byVersion = {
|
|
305
|
+
4: [],
|
|
306
|
+
6: []
|
|
307
|
+
};
|
|
308
|
+
for (const n of parsed) byVersion[n.version].push(n);
|
|
309
|
+
for (const v of [4, 6]) {
|
|
310
|
+
if (byVersion[v].length === 0) continue;
|
|
311
|
+
const intervals = mergeIntervalsRaw(byVersion[v]);
|
|
312
|
+
if (v === 4) for (const part of intervals) {
|
|
313
|
+
const startNum = Number(part.start);
|
|
314
|
+
const endNum = Number(part.end);
|
|
315
|
+
for (let n = startNum; n <= endNum; n++) yield formatIPv4Fast(n);
|
|
316
|
+
}
|
|
317
|
+
else for (const part of intervals) for (let num = part.start; num <= part.end; num++) yield stringifyIp({
|
|
318
|
+
number: num,
|
|
319
|
+
version: 6
|
|
320
|
+
});
|
|
302
321
|
}
|
|
303
322
|
}
|
|
304
323
|
/** Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cidr-tools",
|
|
3
|
-
"version": "11.0
|
|
3
|
+
"version": "11.1.0",
|
|
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.2.
|
|
20
|
+
"ip-bigint": "^8.2.9"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/node": "25.3.3",
|