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: https://github.com/nodejs/node/pull/57324
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Wiyeong Seo 2025-04-20 00:54:48 -05:00 committed by GitHub
parent 25842c5e35
commit 7102ea1559
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 81 additions and 55 deletions

View File

@ -8,6 +8,7 @@ const {
Error,
ErrorCaptureStackTrace,
FunctionPrototypeCall,
NumberParseInt,
ObjectDefineProperties,
ObjectDefineProperty,
ObjectFreeze,
@ -33,7 +34,9 @@ const {
SafeSet,
SafeWeakMap,
SafeWeakRef,
StringPrototypeIncludes,
StringPrototypeReplace,
StringPrototypeSlice,
StringPrototypeToLowerCase,
StringPrototypeToUpperCase,
Symbol,
@ -806,6 +809,59 @@ function setupCoverageHooks(dir) {
return coverageDirectory;
}
// Returns the number of ones in the binary representation of the decimal
// number.
function countBinaryOnes(n) {
// Count the number of bits set in parallel, which is faster than looping
n = n - ((n >>> 1) & 0x55555555);
n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
return ((n + (n >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
}
function getCIDR(address, netmask, family) {
let ones = 0;
let split = '.';
let range = 10;
let groupLength = 8;
let hasZeros = false;
let lastPos = 0;
if (family === 'IPv6') {
split = ':';
range = 16;
groupLength = 16;
}
for (let i = 0; i < netmask.length; i++) {
if (netmask[i] !== split) {
if (i + 1 < netmask.length) {
continue;
}
i++;
}
const part = StringPrototypeSlice(netmask, lastPos, i);
lastPos = i + 1;
if (part !== '') {
if (hasZeros) {
if (part !== '0') {
return null;
}
} else {
const binary = NumberParseInt(part, range);
const binaryOnes = countBinaryOnes(binary);
ones += binaryOnes;
if (binaryOnes !== groupLength) {
if (StringPrototypeIncludes(binary.toString(2), '01')) {
return null;
}
hasZeros = true;
}
}
}
}
return `${address}/${ones}`;
}
const handleTypes = ['TCP', 'TTY', 'UDP', 'FILE', 'PIPE', 'UNKNOWN'];
function guessHandleType(fd) {
@ -872,6 +928,7 @@ module.exports = {
filterDuplicateStrings,
filterOwnProperties,
getConstructorOf,
getCIDR,
getCWDURL,
getInternalGlobal,
getStructuredStack,

View File

@ -24,7 +24,6 @@
const {
ArrayPrototypePush,
Float64Array,
NumberParseInt,
ObjectDefineProperties,
StringPrototypeSlice,
SymbolToPrimitive,
@ -40,6 +39,7 @@ const {
},
hideStackFrames,
} = require('internal/errors');
const { getCIDR } = require('internal/util');
const { validateInt32 } = require('internal/validators');
const {
@ -202,60 +202,6 @@ function endianness() {
}
endianness[SymbolToPrimitive] = () => kEndianness;
// Returns the number of ones in the binary representation of the decimal
// number.
function countBinaryOnes(n) {
// Count the number of bits set in parallel, which is faster than looping
n = n - ((n >>> 1) & 0x55555555);
n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
return ((n + (n >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
}
function getCIDR(address, netmask, family) {
let ones = 0;
let split = '.';
let range = 10;
let groupLength = 8;
let hasZeros = false;
let lastPos = 0;
if (family === 'IPv6') {
split = ':';
range = 16;
groupLength = 16;
}
for (let i = 0; i < netmask.length; i++) {
if (netmask[i] !== split) {
if (i + 1 < netmask.length) {
continue;
}
i++;
}
const part = StringPrototypeSlice(netmask, lastPos, i);
lastPos = i + 1;
if (part !== '') {
if (hasZeros) {
if (part !== '0') {
return null;
}
} else {
const binary = NumberParseInt(part, range);
const binaryOnes = countBinaryOnes(binary);
ones += binaryOnes;
if (binaryOnes !== groupLength) {
if ((binary & 1) !== 0) {
return null;
}
hasZeros = true;
}
}
}
}
return `${address}/${ones}`;
}
/**
* @returns {Record<string, Array<{
* address: string,

View File

@ -0,0 +1,23 @@
// Flags: --expose-internals
'use strict';
require('../common');
// These are tests that verify that the subnetmask is used
// to create the correct CIDR address.
// Tests that it returns null if the subnetmask is not in the correct format.
// (ref: https://www.rfc-editor.org/rfc/rfc1878)
const assert = require('node:assert');
const { getCIDR } = require('internal/util');
assert.strictEqual(getCIDR('127.0.0.1', '255.0.0.0', 'IPv4'), '127.0.0.1/8');
assert.strictEqual(getCIDR('127.0.0.1', '255.255.0.0', 'IPv4'), '127.0.0.1/16');
// 242 = 11110010(2)
assert.strictEqual(getCIDR('127.0.0.1', '242.0.0.0', 'IPv4'), null);
assert.strictEqual(getCIDR('::1', 'ffff:ffff:ffff:ffff::', 'IPv6'), '::1/64');
assert.strictEqual(getCIDR('::1', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'IPv6'), '::1/128');
// ff00:ffff = 11111111 00000000 : 11111111 11111111(2)
assert.strictEqual(getCIDR('::1', 'ffff:ff00:ffff::', 'IPv6'), null);