lakutata
Version:
An IoC-based universal application framework.
373 lines (368 loc) • 14.2 kB
JavaScript
class IPv4Range {
#t;
#s;
constructor(t, s) {
if (+t < 0 || +t > 4294967295 || +s < 0 || +s > 4294967295) {
throw new Error("Invalid start or end IPv4 address");
}
this.#t = t;
this.#s = s;
}
static fromLong(t, s) {
if (typeof t !== "number" || typeof s !== "number") throw new Error("Invalid start or end IPv4 address");
if (+s < +t) throw new Error("Invalid range value, end must be greater than or equal to start");
return new IPv4Range(t, s);
}
static fromString(t, s) {
const i = IPv4.ip2long(t);
const n = IPv4.ip2long(s);
if (typeof i !== "number" || typeof n !== "number") throw new Error("Invalid start or end IPv4 address");
if (n < i) throw new Error("Invalid range value, end must be greater than or equal to start");
return new IPv4Range(i, n);
}
ip2long() {
return [ this.#t, this.#s ];
}
long2ip() {
return [ IPv4.long2ip(this.#t), IPv4.long2ip(this.#s) ];
}
ipCount() {
return this.#s - this.#t + 1;
}
contains(t) {
const s = IPv4.ip2long(t);
if (typeof s !== "number") return false;
return s >= this.#t && s <= this.#s;
}
}
class IP {
static contains(t, s) {
if (typeof t !== "string" || typeof s !== "string") return false;
return IPv4.contains(t, s) || IPv6.contains(t, s);
}
static ip2long(t) {
if (!this.isValidIP(t)) return false;
return IPv4.ip2long(t) || IPv6.ip2long(t);
}
static isCIDR(t) {
if (typeof t !== "string") return false;
return IPv4.isCIDR(t) || IPv6.isCIDR(t);
}
static isConflict(t) {
if (!Array.isArray(t) || t.length === 0) return false;
return IPv4.isConflict(t) || IPv6.isConflict(t);
}
static isValidIP(t) {
if (typeof t !== "string") return false;
return IPv4.isValidIP(t) || IPv6.isValidIP(t);
}
static long2ip(t) {
if (typeof t !== "number" && typeof t !== "bigint") return false;
return IPv4.long2ip(t) || IPv6.long2ip(t);
}
}
class IPv4 {
static ip2long(t) {
if (!this.isValidIP(t)) return false;
let s = 0;
const i = t.split(".");
for (const t of i) s = (s << 8) + +t;
return s >>> 0;
}
static long2ip(t) {
if (typeof t !== "number" || isNaN(t)) return false;
if (t >= 0 && t <= 4294967295) {
const s = [];
for (let i = 3; i >= 0; i--) s.push(t >>> i * 8 & 255);
return s.join(".");
} else {
return false;
}
}
static {
this.ipRange = IPv4Range;
}
static isCIDR(t) {
if (typeof t !== "string") return false;
const s = this.parseCIDR(t);
return typeof s === "object" ? true : false;
}
static isEqual(t, s) {
if (typeof t === "number" && (t < 0 || t > 4294967295)) return false;
if (typeof s === "number" && (s < 0 || s > 4294967295)) return false;
if (typeof t === "string") t = this.ip2long(t);
if (typeof s === "string") s = this.ip2long(s);
if (typeof t !== "number" || typeof s !== "number") return false;
return t === s;
}
static contains(t, s) {
const i = this.parseCIDR(t);
if (typeof i !== "object" || !this.isValidIP(s)) return false;
const {cidrMask: n, firstHost: e, lastHost: r, networkAddress: o, broadcastAddress: a} = i;
if (n >= 31) {
return this.ipRange.fromString(e, r).contains(s);
} else {
return this.ipRange.fromString(o, a).contains(s);
}
}
static isPrivate(t) {
if (!this.isValidIP(t)) return false;
const s = [ {
start: "10.0.0.0",
end: "10.255.255.255"
}, {
start: "127.0.0.0",
end: "127.255.255.255"
}, {
start: "172.16.0.0",
end: "172.31.255.255"
}, {
start: "169.254.0.0",
end: "169.254.255.255"
}, {
start: "192.168.0.0",
end: "192.168.255.255"
} ];
for (const i of s) {
if (this.ipRange.fromString(i.start, i.end).contains(t)) {
return true;
}
}
return false;
}
static isValidIP(t, s = {
strict: false
}) {
if (s.strict) {
const s = /^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])(\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)){3}$/;
return s.test(t);
} else {
const s = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/;
return s.test(t);
}
}
static parseCIDR(t) {
if (typeof t !== "string") return false;
const [s, i] = t.split("/");
if (s === undefined || i === undefined || i === "") return false;
if (!this.isValidIP(s) || !this.isValidMask(+i)) return false;
const n = 32 - +i;
const e = this.ip2long(s);
const r = Number(1n << BigInt(n));
const o = +i ? e >> n << n >>> 0 : 0;
const a = (o | r - 1) >>> 0;
const f = {
ipCount: r,
cidrMask: +i,
usableCount: +i < 31 ? r - 2 : r,
subnetMask: this.toSubnetMask(+i),
networkAddress: +i < 31 ? this.long2ip(o) : "",
broadcastAddress: +i < 31 ? this.long2ip(a) : "",
firstHost: this.long2ip(o + (+i < 31 ? 1 : 0)),
lastHost: this.long2ip(a - (+i < 31 ? 1 : 0))
};
return f;
}
static isConflict(t) {
if (!Array.isArray(t) || t.length === 0) return false;
const s = [];
for (const i of t) {
const t = this.parseCIDR(i);
if (typeof t === "object") s.push({
cidr: i,
networkAddress: t.networkAddress || t.firstHost
});
}
for (let t = 0; t < s.length; t++) {
for (let i = t + 1; i < s.length; i++) {
const n = this.contains(s[i].cidr, s[t].networkAddress);
const e = this.contains(s[t].cidr, s[i].networkAddress);
if (n || e) return true;
}
}
return false;
}
static parseSubnet(t, s) {
if (!this.isValidIP(t) || !this.isValidMask(s)) return false;
const i = this.toMaskLength(s);
const n = this.parseCIDR(`${t}/${i}`);
return n;
}
static isValidMask(t) {
if (typeof t === "number" && !isNaN(t)) {
if (t < 0 || t > 32) return false;
return true;
} else if (typeof t === "string") {
const s = this.ip2long(t);
if (typeof s !== "number") return false;
return /^1*0*$/.test(s.toString(2).padStart(32, "0"));
} else {
return false;
}
}
static isSameSubnet(t, s, i) {
if (!this.isValidIP(t) || !this.isValidIP(s) || !this.isValidMask(i)) return false;
const n = this.ip2long(t);
const e = this.ip2long(s);
if (typeof i === "number") i = this.toSubnetMask(i);
const r = this.ip2long(i);
return (n & r) === (e & r);
}
static toBinHex(t) {
if (!this.isValidIP(t)) return false;
const s = this.ip2long(t);
return {
decimal: s,
hex: `0x${s.toString(16).padStart(8, "0")}`,
binary: s.toString(2).padStart(32, "0")
};
}
static toIPv6Format(t) {
if (!this.isValidIP(t)) return false;
const s = this.ip2long(t);
const i = this.long2ip(s);
return {
mapped: `::ffff:${i}`,
comperssed: IPv6.compressedForm(`::ffff:${i}`),
expanded: IPv6.expandedForm(`::ffff:${i}`)
};
}
static toSubnetMask(t) {
if (typeof t !== "number" || isNaN(t) || !this.isValidMask(t)) return false;
const s = 4294967295 << 32 - t;
return t ? this.long2ip(s >>> 0) : "0.0.0.0";
}
static toMaskLength(t) {
if (typeof t !== "string") return false;
if (!this.isValidMask(t)) return false;
const s = this.ip2long(t);
const i = s === 0 ? 0 : s.toString(2).replaceAll("0", "").length;
return i;
}
static toInverseMask(t) {
if (!this.isValidMask(t)) return false;
if (typeof t === "number") t = this.toSubnetMask(t);
const s = this.ip2long(t);
const i = ~s >>> 0;
return this.long2ip(i);
}
}
class IPv6 {
static compressedForm(t) {
if (!this.isValidIP(t)) return false;
if (this.ip2long(t) === 0n) return "::";
t = this.expandedForm(t);
const s = t.split(":");
const i = s.map((t => {
const s = parseInt(t, 16);
return s ? s.toString(16) : "X";
})).join(":");
const n = [ /(X:X:X:X:X:X:X)/, /(X:X:X:X:X:X)/, /(X:X:X:X:X)/, /(X:X:X:X)/, /(X:X:X)/, /(X:X)/ ];
for (let t = 0; t < n.length; t++) {
if (i.match(n[t])) return i.replace(n[t], ":").replace(":::", "::").replaceAll("X", "0");
}
return i.replaceAll("X", "0");
}
static contains(t, s) {
const i = this.parseCIDR(t);
if (typeof i !== "object" || !this.isValidIP(s)) return false;
const {lastHost: n, firstHost: e} = i;
const r = this.ip2long(s);
const o = this.ip2long(n);
const a = this.ip2long(e);
return r >= a && r <= o;
}
static expandedForm(t) {
if (!this.isValidIP(t)) return false;
if (t === "::") return "0000:".repeat(8).slice(0, -1);
const s = t.split(":");
for (let t = 0; t < s.length; t++) {
if (s[t] === "" && s[t + 1] === "") s.splice(t, 1);
}
const i = s[s.length - 1];
if (IPv4.isValidIP(i)) {
const t = IPv4.toBinHex(i).hex.slice(2);
s.pop() && s.push(t.slice(0, 4), t.slice(4));
}
return s.map((t => t ? t.padStart(4, "0") : "0000:".repeat(9 - s.length).slice(0, -1))).join(":");
}
static ip2long(t) {
if (!this.isValidIP(t)) return false;
const s = [];
t = this.expandedForm(t);
const i = t.split(":");
for (let t = 0; t < i.length; t++) {
const n = parseInt(i[t], 16);
s.push(n.toString(2).padStart(16, "0"));
}
return BigInt(`0b${s.join("")}`);
}
static isCIDR(t) {
if (typeof t !== "string") return false;
const s = this.parseCIDR(t);
return typeof s === "object" ? true : false;
}
static isConflict(t) {
if (!Array.isArray(t) || t.length === 0) return false;
const s = [];
for (const i of t) {
const t = this.parseCIDR(i);
if (typeof t === "object") s.push({
cidr: i,
firstHost: t.firstHost
});
}
for (let t = 0; t < s.length; t++) {
for (let i = t + 1; i < s.length; i++) {
const n = this.contains(s[i].cidr, s[t].firstHost);
const e = this.contains(s[t].cidr, s[i].firstHost);
if (n || e) return true;
}
}
return false;
}
static isEqual(t, s) {
if (typeof t === "bigint" && (t < 0n || t > 340282366920938463463374607431768211455n)) return false;
if (typeof s === "bigint" && (s < 0 || s > 340282366920938463463374607431768211455n)) return false;
if (typeof t === "string") t = this.ip2long(t);
if (typeof s === "string") s = this.ip2long(s);
if (typeof t !== "bigint" || typeof s !== "bigint") return false;
return t === s;
}
static isValidIP(t) {
const s = /^[\s]*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}))|:)))(%.+)?[\s]*$/;
return s.test(t);
}
static long2ip(t) {
if (typeof t !== "bigint") return false;
if (t >= 0n && t <= 340282366920938463463374607431768211455n) {
const s = [];
const i = t.toString(16).padStart(32, "0");
for (let t = 0; t < 8; t++) s.push(i.slice(t * 4, (t + 1) * 4));
return this.compressedForm(s.join(":"));
} else {
return false;
}
}
static parseCIDR(t) {
if (typeof t !== "string") return false;
const [s, i] = t.split("/");
if (s === undefined || i === undefined || i === "") return false;
const n = +i;
if (!this.isValidIP(s) || isNaN(n) || n < 0 || n > 128) return false;
const e = BigInt(128 - n);
const r = this.ip2long(s);
const o = BigInt(1n << e);
const a = r >> e << e;
const f = this.long2ip(a);
const l = this.long2ip(a | o - 1n);
const c = {
ipCount: o,
firstHost: f,
lastHost: l,
prefixLength: n
};
return c;
}
}
export { IP, IPv4, IPv6 };