Skip to content

Commit 7102ea1

Browse files
authored
os: fix netmask format check condition in getCIDR function
Modified to check the format of the netmask instead of just checking that each part of the netmask is even PR-URL: #57324 Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 25842c5 commit 7102ea1

File tree

3 files changed

+81
-55
lines changed

3 files changed

+81
-55
lines changed

lib/internal/util.js

+57
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const {
88
Error,
99
ErrorCaptureStackTrace,
1010
FunctionPrototypeCall,
11+
NumberParseInt,
1112
ObjectDefineProperties,
1213
ObjectDefineProperty,
1314
ObjectFreeze,
@@ -33,7 +34,9 @@ const {
3334
SafeSet,
3435
SafeWeakMap,
3536
SafeWeakRef,
37+
StringPrototypeIncludes,
3638
StringPrototypeReplace,
39+
StringPrototypeSlice,
3740
StringPrototypeToLowerCase,
3841
StringPrototypeToUpperCase,
3942
Symbol,
@@ -806,6 +809,59 @@ function setupCoverageHooks(dir) {
806809
return coverageDirectory;
807810
}
808811

812+
// Returns the number of ones in the binary representation of the decimal
813+
// number.
814+
function countBinaryOnes(n) {
815+
// Count the number of bits set in parallel, which is faster than looping
816+
n = n - ((n >>> 1) & 0x55555555);
817+
n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
818+
return ((n + (n >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
819+
}
820+
821+
function getCIDR(address, netmask, family) {
822+
let ones = 0;
823+
let split = '.';
824+
let range = 10;
825+
let groupLength = 8;
826+
let hasZeros = false;
827+
let lastPos = 0;
828+
829+
if (family === 'IPv6') {
830+
split = ':';
831+
range = 16;
832+
groupLength = 16;
833+
}
834+
835+
for (let i = 0; i < netmask.length; i++) {
836+
if (netmask[i] !== split) {
837+
if (i + 1 < netmask.length) {
838+
continue;
839+
}
840+
i++;
841+
}
842+
const part = StringPrototypeSlice(netmask, lastPos, i);
843+
lastPos = i + 1;
844+
if (part !== '') {
845+
if (hasZeros) {
846+
if (part !== '0') {
847+
return null;
848+
}
849+
} else {
850+
const binary = NumberParseInt(part, range);
851+
const binaryOnes = countBinaryOnes(binary);
852+
ones += binaryOnes;
853+
if (binaryOnes !== groupLength) {
854+
if (StringPrototypeIncludes(binary.toString(2), '01')) {
855+
return null;
856+
}
857+
hasZeros = true;
858+
}
859+
}
860+
}
861+
}
862+
863+
return `${address}/${ones}`;
864+
}
809865

810866
const handleTypes = ['TCP', 'TTY', 'UDP', 'FILE', 'PIPE', 'UNKNOWN'];
811867
function guessHandleType(fd) {
@@ -872,6 +928,7 @@ module.exports = {
872928
filterDuplicateStrings,
873929
filterOwnProperties,
874930
getConstructorOf,
931+
getCIDR,
875932
getCWDURL,
876933
getInternalGlobal,
877934
getStructuredStack,

lib/os.js

+1-55
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
const {
2525
ArrayPrototypePush,
2626
Float64Array,
27-
NumberParseInt,
2827
ObjectDefineProperties,
2928
StringPrototypeSlice,
3029
SymbolToPrimitive,
@@ -40,6 +39,7 @@ const {
4039
},
4140
hideStackFrames,
4241
} = require('internal/errors');
42+
const { getCIDR } = require('internal/util');
4343
const { validateInt32 } = require('internal/validators');
4444

4545
const {
@@ -202,60 +202,6 @@ function endianness() {
202202
}
203203
endianness[SymbolToPrimitive] = () => kEndianness;
204204

205-
// Returns the number of ones in the binary representation of the decimal
206-
// number.
207-
function countBinaryOnes(n) {
208-
// Count the number of bits set in parallel, which is faster than looping
209-
n = n - ((n >>> 1) & 0x55555555);
210-
n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
211-
return ((n + (n >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
212-
}
213-
214-
function getCIDR(address, netmask, family) {
215-
let ones = 0;
216-
let split = '.';
217-
let range = 10;
218-
let groupLength = 8;
219-
let hasZeros = false;
220-
let lastPos = 0;
221-
222-
if (family === 'IPv6') {
223-
split = ':';
224-
range = 16;
225-
groupLength = 16;
226-
}
227-
228-
for (let i = 0; i < netmask.length; i++) {
229-
if (netmask[i] !== split) {
230-
if (i + 1 < netmask.length) {
231-
continue;
232-
}
233-
i++;
234-
}
235-
const part = StringPrototypeSlice(netmask, lastPos, i);
236-
lastPos = i + 1;
237-
if (part !== '') {
238-
if (hasZeros) {
239-
if (part !== '0') {
240-
return null;
241-
}
242-
} else {
243-
const binary = NumberParseInt(part, range);
244-
const binaryOnes = countBinaryOnes(binary);
245-
ones += binaryOnes;
246-
if (binaryOnes !== groupLength) {
247-
if ((binary & 1) !== 0) {
248-
return null;
249-
}
250-
hasZeros = true;
251-
}
252-
}
253-
}
254-
}
255-
256-
return `${address}/${ones}`;
257-
}
258-
259205
/**
260206
* @returns {Record<string, Array<{
261207
* address: string,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Flags: --expose-internals
2+
'use strict';
3+
require('../common');
4+
5+
// These are tests that verify that the subnetmask is used
6+
// to create the correct CIDR address.
7+
// Tests that it returns null if the subnetmask is not in the correct format.
8+
// (ref: https://www.rfc-editor.org/rfc/rfc1878)
9+
10+
const assert = require('node:assert');
11+
const { getCIDR } = require('internal/util');
12+
13+
assert.strictEqual(getCIDR('127.0.0.1', '255.0.0.0', 'IPv4'), '127.0.0.1/8');
14+
assert.strictEqual(getCIDR('127.0.0.1', '255.255.0.0', 'IPv4'), '127.0.0.1/16');
15+
16+
// 242 = 11110010(2)
17+
assert.strictEqual(getCIDR('127.0.0.1', '242.0.0.0', 'IPv4'), null);
18+
19+
assert.strictEqual(getCIDR('::1', 'ffff:ffff:ffff:ffff::', 'IPv6'), '::1/64');
20+
assert.strictEqual(getCIDR('::1', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'IPv6'), '::1/128');
21+
22+
// ff00:ffff = 11111111 00000000 : 11111111 11111111(2)
23+
assert.strictEqual(getCIDR('::1', 'ffff:ff00:ffff::', 'IPv6'), null);

0 commit comments

Comments
 (0)