Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port from ip to ipaddr.js NPM module #1455

Merged
merged 1 commit into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion node_modules
Submodule node_modules updated 104 files
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"eslint-plugin-react-hooks": "4.6.0",
"gettext-parser": "8.0.0",
"htmlparser": "1.7.7",
"ip": "1.1.8",
"ipaddr.js": "2.1.0",
"jed": "1.1.1",
"sass": "1.71.0",
"sizzle": "2.3.10",
Expand Down
166 changes: 63 additions & 103 deletions src/components/networks/utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* This file is part of Cockpit.
*
* Copyright (C) 2019 Red Hat, Inc.
* Copyright (C) 2019 - 2024 Red Hat, Inc.
*
* Cockpit is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
Expand All @@ -17,32 +17,27 @@
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
*/

import * as ip from "ip";
import * as ipaddr from "ipaddr.js";

/**
* Validates correctness of ipv4 address
*
* @param {string} address
* @returns {boolean}
*/
export function validateIpv4(address) {
return ip.isV4Format(address);
}
export const validateIpv4 = address => ipaddr.IPv4.isValid(address);

/**
* Returns if the provided address is the network's broadcast address
*
* @param {string} address
* @returns {boolean}
*/
export function ipv4IsBroadcast(address, netMask) {
if (!validateNetmask(netMask))
export function ipv4IsBroadcast(address, netmask) {
const prefix = validateNetmask(netmask);
if (prefix === null)
return false;
const mask = netmaskConvert(netMask);
const subnet = ip.subnet(address, mask);

// user provided netmask
return address === subnet.broadcastAddress;
return address === ipaddr.IPv4.broadcastAddressFromCIDR(`${address}/${prefix}`).toString();
}

/**
Expand All @@ -51,47 +46,37 @@ export function ipv4IsBroadcast(address, netMask) {
* @param {string} address
* @returns {boolean}
*/
export function ipv4IsNetworkIdentifier(address, netMask) {
if (!validateNetmask(netMask))
export function ipv4IsNetworkIdentifier(address, netmask) {
const prefix = validateNetmask(netmask);
if (prefix === null)
return false;
const mask = netmaskConvert(netMask);
const subnet = ip.subnet(address, mask);

// user provided network identifier
return address === subnet.networkAddress;
return address === ipaddr.IPv4.networkAddressFromCIDR(`${address}/${prefix}`).toString();
jelly marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* validates correctness of ipv4 prefix length or mask
*
* @param {string} prefixOrNetmask
* @returns {boolean}
* @returns int prefix length, or null if invalid
jelly marked this conversation as resolved.
Show resolved Hide resolved
*/
export function validateNetmask(prefixOrNetmask) {
const netmaskParts = ["255", "254", "252", "248", "240", "224", "192", "128", "0"];
const parts = prefixOrNetmask.split('.');

// prefix length
if (parts.length === 1) {
if (!/^[0-9]+$/.test(parts[0].trim()))
return false;
const prefixLength = parseInt(parts[0], 10);
if (isNaN(prefixLength) || prefixLength < 1 || prefixLength > 31)
return false;

return true;
}

// netmask
if (!validateIpv4(prefixOrNetmask))
return false;

for (let i = 0; i < 4; i++) {
if (!(netmaskParts.includes(parts[i])))
return false;
if (/^[0-9]+$/.test(prefixOrNetmask)) {
// prefix
try {
const prefix = parseInt(prefixOrNetmask);
ipaddr.IPv4.subnetMaskFromPrefixLength(prefix);
return prefix;
} catch (_ex) {
return null;
}
} else {
// mask
try {
return ipaddr.IPv4.parse(prefixOrNetmask).prefixLengthFromSubnetMask();
} catch (_ex) {
return null;
Comment on lines +76 to +77
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 2 added lines are not executed by any test. Details

}
}

return true;
}

/**
Expand All @@ -101,28 +86,36 @@ export function validateNetmask(prefixOrNetmask) {
* @returns {string}
*/
export function netmaskConvert(prefixOrNetmask) {
const parts = prefixOrNetmask.split('.');
// single number → netmask
if (/^[0-9]+$/.test(prefixOrNetmask)) {
try {
return ipaddr.IPv4.subnetMaskFromPrefixLength(parseInt(prefixOrNetmask)).toString();
} catch (_ex) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This added line is not executed by any test. Details

// leave unchanged; UI will validate
}
}

if (parts.length === 4)
return prefixOrNetmask;
else if (parts.length === 1)
return ip.fromPrefixLen(prefixOrNetmask);
return prefixOrNetmask;
}

/**
* Checks whetever address @ip is in subnet defined by @network and @netmask
* Checks whetever address @address is in subnet defined by @network and @netmask
*
* @param {string} network
* @param {string} netmask
* @param {string} ip
* @param {string} address
* @returns {boolean}
*/
export function isIpv4InNetwork(network, netmask, ipaddr) {
if (!ip.isV4Format(network) || !validateNetmask(netmask) || !ip.isV4Format(ipaddr))
export function isIpv4InNetwork(network, netmask, address) {
if (!validateIpv4(network) || !validateIpv4(address))
return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This added line is not executed by any test. Details

const prefix = validateNetmask(netmask);
if (prefix === null)
return false;
const mask = netmaskConvert(netmask);

return ip.subnet(network, mask).contains(ipaddr);
const b_network = ipaddr.IPv4.broadcastAddressFromCIDR(`${network}/${prefix}`).toString();
const b_ipaddr = ipaddr.IPv4.broadcastAddressFromCIDR(`${address}/${prefix}`).toString();
return b_network === b_ipaddr;
}

/**
Expand All @@ -131,9 +124,7 @@ export function isIpv4InNetwork(network, netmask, ipaddr) {
* @param {string} address
* @returns {boolean}
*/
export function validateIpv6(address) {
return ip.isV6Format(address);
}
export const validateIpv6 = address => ipaddr.IPv6.isValid(address);

/**
* validates correctness of ipv6 prefix length
Expand All @@ -142,59 +133,28 @@ export function validateIpv6(address) {
* @returns {boolean}
*/
export function validateIpv6Prefix(prefix) {
if (!/^[0-9]+$/.test(prefix.trim()))
return false;
const prefixLength = parseInt(prefix, 10);
if (isNaN(prefixLength) || prefixLength < 0 || prefixLength > 128)
return false;

return true;
}

/**
* Converts ipv6 address to string containing it's binary representation
*
* @param {string} ip
* @returns {string}
*/
function ipv6ToBinStr(ip) {
const validGroupCount = 8;
/* Split address by `:`
* Then check if the array contains an empty string (happens at ::), and if so
* replace it with the appropriate number of 0 entries.
*/
const arrAddr = ip.split(":");
const arrAddrExpanded = arrAddr.reduce((accum, hexNum) => {
if (hexNum)
accum.push(hexNum);
else
for (let i = 0; i < (validGroupCount - arrAddr.length + 1); i++)
accum.push("0");
return accum;
}, []);

/* Convert the array of 8 hex entries into a 128 bits binary string */
return arrAddrExpanded.map(num => {
let bin = parseInt(num, 16).toString(2);
while (bin.length < 16)
bin = "0" + bin;
return bin;
}).join("");
if (/^[0-9]+$/.test(prefix.trim())) {
try {
ipaddr.IPv6.subnetMaskFromPrefixLength(prefix);
return true;
} catch (_ex) {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This added line is not executed by any test. Details

}
return false;
}

/**
* Checks whetever IPv6 address @ip is in subnet defined by @network and @prefix
* Checks whetever IPv6 @address is in subnet defined by @network and @prefix
*
* @param {string} network
* @param {string} prefix
* @param {string} ip
* @param {string} address
* @returns {boolean}
*/
export function isIpv6InNetwork(network, prefix, ip) {
network = ipv6ToBinStr(network);
network = network.substring(0, prefix);
ip = ipv6ToBinStr(ip);
ip = ip.substring(0, prefix);
export function isIpv6InNetwork(network, prefix, address) {
if (!validateIpv6(network) || !validateIpv6Prefix(prefix) || !validateIpv6(address))
return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This added line is not executed by any test. Details


return network == ip;
const b_network = ipaddr.IPv6.broadcastAddressFromCIDR(`${network}/${prefix}`).toString();
const b_ipaddr = ipaddr.IPv6.broadcastAddressFromCIDR(`${address}/${prefix}`).toString();
return b_network === b_ipaddr;
}