124 lines
2.4 KiB
JavaScript
124 lines
2.4 KiB
JavaScript
|
|
/**
|
||
|
|
* IP Address Utilities
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { createRequire } from 'node:module';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if string is IPv4
|
||
|
|
*/
|
||
|
|
export function isIPv4(ip) {
|
||
|
|
if (typeof ip !== 'string') return false;
|
||
|
|
const parts = ip.split('.');
|
||
|
|
if (parts.length !== 4) return false;
|
||
|
|
|
||
|
|
return parts.every(part => {
|
||
|
|
const num = parseInt(part, 10);
|
||
|
|
return !isNaN(num) && num >= 0 && num <= 255 && part === String(num);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if string is IPv6
|
||
|
|
*/
|
||
|
|
export function isIPv6(ip) {
|
||
|
|
if (typeof ip !== 'string') return false;
|
||
|
|
// Simple check - contains colons and valid hex
|
||
|
|
return ip.includes(':') && /^[0-9a-fA-F:]+$/.test(ip);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if IP is in private range (RFC 1918)
|
||
|
|
*/
|
||
|
|
export function isPrivateIp(ip) {
|
||
|
|
if (!isIPv4(ip)) return false;
|
||
|
|
|
||
|
|
const parts = ip.split('.').map(Number);
|
||
|
|
const [a, b, c, d] = parts;
|
||
|
|
|
||
|
|
// 10.0.0.0/8
|
||
|
|
if (a === 10) return true;
|
||
|
|
|
||
|
|
// 172.16.0.0/12
|
||
|
|
if (a === 172 && b >= 16 && b <= 31) return true;
|
||
|
|
|
||
|
|
// 192.168.0.0/16
|
||
|
|
if (a === 192 && b === 168) return true;
|
||
|
|
|
||
|
|
// 127.0.0.0/8 (loopback)
|
||
|
|
if (a === 127) return true;
|
||
|
|
|
||
|
|
// 169.254.0.0/16 (link-local)
|
||
|
|
if (a === 169 && b === 254) return true;
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if IP is loopback
|
||
|
|
*/
|
||
|
|
export function isLoopbackIp(ip) {
|
||
|
|
if (!isIPv4(ip)) return false;
|
||
|
|
return ip.startsWith('127.');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Convert IP to integer for range comparison
|
||
|
|
*/
|
||
|
|
export function ipToInt(ip) {
|
||
|
|
return ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet, 10), 0) >>> 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Parse CIDR notation
|
||
|
|
*/
|
||
|
|
export function parseCidr(cidr) {
|
||
|
|
const [ip, prefix] = cidr.split('/');
|
||
|
|
const mask = -1 << (32 - parseInt(prefix, 10));
|
||
|
|
return { ip: ipToInt(ip), mask };
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if IP is in CIDR range
|
||
|
|
*/
|
||
|
|
export function isIpInCidr(ip, cidr) {
|
||
|
|
if (!isIPv4(ip)) return false;
|
||
|
|
|
||
|
|
try {
|
||
|
|
const ipInt = ipToInt(ip);
|
||
|
|
const { ip: networkInt, mask } = parseCidr(cidr);
|
||
|
|
|
||
|
|
return (ipInt & mask) === (networkInt & mask);
|
||
|
|
} catch (err) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if two IPs are equal
|
||
|
|
*/
|
||
|
|
export function ipEquals(ip1, ip2) {
|
||
|
|
return ip1 === ip2;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get IP version (4 or 6)
|
||
|
|
*/
|
||
|
|
export function getIpVersion(ip) {
|
||
|
|
if (isIPv4(ip)) return 4;
|
||
|
|
if (isIPv6(ip)) return 6;
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Normalize IP (remove leading zeros, etc.)
|
||
|
|
*/
|
||
|
|
export function normalizeIp(ip) {
|
||
|
|
if (!isIPv4(ip)) return ip;
|
||
|
|
|
||
|
|
return ip
|
||
|
|
.split('.')
|
||
|
|
.map(part => parseInt(part, 10).toString())
|
||
|
|
.join('.');
|
||
|
|
}
|