199 lines
6 KiB
JavaScript
199 lines
6 KiB
JavaScript
const AF_INET = 2;
|
|
const AF_INET6 = 23;
|
|
|
|
function ord (string) {
|
|
var str = string + '';
|
|
var code = str.charCodeAt(0);
|
|
|
|
if (code >= 0xD800 && code <= 0xDBFF) {
|
|
// High surrogate (could change last hex to 0xDB7F to treat
|
|
// high private surrogates as single characters)
|
|
var hi = code;
|
|
if (str.length === 1) {
|
|
// This is just a high surrogate with no following low surrogate,
|
|
// so we return its value;
|
|
return code;
|
|
// we could also throw an error as it is not a complete character,
|
|
// but someone may want to know
|
|
}
|
|
var low = str.charCodeAt(1);
|
|
return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000
|
|
}
|
|
if (code >= 0xDC00 && code <= 0xDFFF) {
|
|
// Low surrogate
|
|
// This is just a low surrogate with no preceding high surrogate,
|
|
// so we return its value;
|
|
return code;
|
|
// we could also throw an error as it is not a complete character,
|
|
// but someone may want to know
|
|
}
|
|
|
|
return code
|
|
}
|
|
|
|
function inetNtop (a) {
|
|
var i = 0;
|
|
var m = '';
|
|
var c = [];
|
|
|
|
a += '';
|
|
if (a.length === 4) {
|
|
// IPv4
|
|
return [
|
|
a.charCodeAt(0),
|
|
a.charCodeAt(1),
|
|
a.charCodeAt(2),
|
|
a.charCodeAt(3)
|
|
].join('.');
|
|
} else if (a.length === 16) {
|
|
// IPv6
|
|
for (i = 0; i < 16; i++) {
|
|
c.push(((a.charCodeAt(i++) << 8) + a.charCodeAt(i)).toString(16));
|
|
}
|
|
return c.join(':')
|
|
.replace(/((^|:)0(?=:|$))+:?/g, function (t) {
|
|
m = (t.length > m.length) ? t : m;
|
|
return t;
|
|
})
|
|
.replace(m || ' ', '::');
|
|
} else {
|
|
// Invalid length
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function inetPton(a) {
|
|
var r;
|
|
var m;
|
|
var x;
|
|
var i;
|
|
var j;
|
|
var f = String.fromCharCode;
|
|
|
|
// IPv4
|
|
m = a.match(/^(?:\d{1,3}(?:\.|$)){4}/);
|
|
if (m) {
|
|
m = m[0].split('.');
|
|
m = f(m[0]) + f(m[1]) + f(m[2]) + f(m[3]);
|
|
// Return if 4 bytes, otherwise false.
|
|
return m.length === 4 ? m : false;
|
|
}
|
|
r = /^((?:[\da-f]{1,4}(?::|)){0,8})(::)?((?:[\da-f]{1,4}(?::|)){0,8})$/;
|
|
|
|
// IPv6
|
|
m = a.match(r);
|
|
if (m) {
|
|
// Translate each hexadecimal value.
|
|
for (j = 1; j < 4; j++) {
|
|
// Indice 2 is :: and if no length, continue.
|
|
if (j === 2 || m[j].length === 0) {
|
|
continue;
|
|
}
|
|
m[j] = m[j].split(':');
|
|
for (i = 0; i < m[j].length; i++) {
|
|
m[j][i] = parseInt(m[j][i], 16);
|
|
// Would be NaN if it was blank, return false.
|
|
if (isNaN(m[j][i])) {
|
|
// Invalid IP.
|
|
return false;
|
|
}
|
|
m[j][i] = f(m[j][i] >> 8) + f(m[j][i] & 0xFF);
|
|
}
|
|
m[j] = m[j].join('');
|
|
}
|
|
x = m[1].length + m[3].length;
|
|
if (x === 16) {
|
|
return m[1] + m[3];
|
|
} else if (x < 16 && m[2].length > 0) {
|
|
return m[1] + (new Array(16 - x + 1))
|
|
.join('\x00') + m[3];
|
|
}
|
|
}
|
|
|
|
// Invalid IP
|
|
return false
|
|
}
|
|
|
|
|
|
class IpAddress {
|
|
constructor(addressFamily, address) {
|
|
console.log('ip address: ', addressFamily, address);
|
|
this.addressFamily = addressFamily;
|
|
this.address = address;
|
|
}
|
|
|
|
str() {
|
|
return inetNtop(this.address);
|
|
}
|
|
}
|
|
|
|
class IpPrefix {
|
|
constructor(addressFamily, prefix) {
|
|
this.addressFamily = addressFamily;
|
|
if (this.addressFamily === AF_INET) {
|
|
this.addressLength = 32;
|
|
} else if (this.addressFamily == AF_INET6) {
|
|
this.addressLength = 128;
|
|
} else {
|
|
throw Error(`invalid address family: ${addressFamily}`);
|
|
}
|
|
|
|
const values = prefix.split('/');
|
|
console.log(`split prefix: ${values}`);
|
|
if (values.length > 2) {
|
|
throw Error(`invalid prefix: ${prefix}`);
|
|
}
|
|
|
|
if (values.length === 2) {
|
|
this.prefixLength = parseInt(values[1]);
|
|
} else {
|
|
this.prefixLength = this.addressLength;
|
|
}
|
|
|
|
this.prefix = inetPton(values[0]);
|
|
|
|
if (this.addressLength > this.prefixLength) {
|
|
const addressBits = this.addressLength - this.prefixLength;
|
|
let netMask = ((1 << this.prefixLength) - 1) << addressBits;
|
|
prefix = '';
|
|
//in xrange(-1, -(addrbits >> 3) - 2, -1)
|
|
const stopValue = -(addressBits) - 2;
|
|
for (let i = -1; i > stopValue; --i) {
|
|
prefix = String.fromCharCode(ord(this.prefix[i]) & (netMask & 0xff)) + prefix;
|
|
netMask >>= 8;
|
|
}
|
|
this.prefix = this.prefix.slice(0, stopValue) + prefix;
|
|
}
|
|
}
|
|
|
|
getAddress(id) {
|
|
if (id in [-1, 0, 1] && this.addressLength === this.prefixLength) {
|
|
return new IpAddress(this.addressFamily, this.prefix);
|
|
}
|
|
|
|
const value = (1 << (this.addressLength - this.prefixLength)) - 1;
|
|
console.log(`address length(${this.addressLength}) prefix length(${this.prefixLength}) value(${value})`);
|
|
if (id === 0 || id > value || (this.addressFamily === AF_INET && id === value)) {
|
|
throw Error(`invalid id for prefix ${this.prefix}: ${id}`);
|
|
}
|
|
|
|
let address = '';
|
|
let prefixEndpoint = -1;
|
|
console.log('stop condition: ', -(this.addressLength >> 3) - 1);
|
|
for (let i = -1; i > -(this.addressLength >> 3) - 1; --i) {
|
|
console.log('i: ', i);
|
|
prefixEndpoint = i;
|
|
address = String.fromCharCode(ord(this.prefix[i]) | (id & 0xff)) + address;
|
|
console.log('address: ', address);
|
|
id >>= 8;
|
|
console.log('id: ', id);
|
|
if (!id) {
|
|
break
|
|
}
|
|
}
|
|
address = this.prefix.slice(0, prefixEndpoint) + address;
|
|
return new IpAddress(this.addressFamily, address);
|
|
}
|
|
}
|
|
|
|
|