forked from ppfun/pixelplanet
add whois util
This commit is contained in:
parent
b355aa60d4
commit
4dee1e24e4
158
src/utils/ip.js
158
src/utils/ip.js
|
@ -5,7 +5,51 @@
|
||||||
|
|
||||||
import { USE_XREALIP } from '../core/config';
|
import { USE_XREALIP } from '../core/config';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse ip4 string to 32bit integer
|
||||||
|
* @param ipString ip string
|
||||||
|
* @return ipNum numerical ip
|
||||||
|
*/
|
||||||
|
function ip4ToNum(ipString) {
|
||||||
|
if (!ipString) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const ipArr = ipString
|
||||||
|
.trim()
|
||||||
|
.split('.')
|
||||||
|
.map((numString) => parseInt(numString, 10));
|
||||||
|
if (ipArr.length !== 4 || ipArr.some(
|
||||||
|
(num) => Number.isNaN(num) || num > 255 || num < 0,
|
||||||
|
)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const ipNum = (ipArr[0] << 24)
|
||||||
|
+ (ipArr[1] << 16)
|
||||||
|
+ (ipArr[2] << 8)
|
||||||
|
+ ipArr[3];
|
||||||
|
return ipNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse ip4 number to string representation
|
||||||
|
* @param ipNum numerical ip (32bit integer)
|
||||||
|
* @return ipString string representation of ip (xxx.xxx.xxx.xxx)
|
||||||
|
*/
|
||||||
|
function ip4NumToStr(ipNum) {
|
||||||
|
return [
|
||||||
|
ipNum >>> 24,
|
||||||
|
ipNum >>> 16 & 0xFF,
|
||||||
|
ipNum >>> 8 & 0xFF,
|
||||||
|
ipNum & 0xFF,
|
||||||
|
].join('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get hostname from request
|
||||||
|
* @param req express req object
|
||||||
|
* @param includeProto if we include protocol (https, http)
|
||||||
|
* @return host (like pixelplanet.fun)
|
||||||
|
*/
|
||||||
export function getHostFromRequest(req, includeProto = true) {
|
export function getHostFromRequest(req, includeProto = true) {
|
||||||
const { headers } = req;
|
const { headers } = req;
|
||||||
const host = headers['x-forwarded-host']
|
const host = headers['x-forwarded-host']
|
||||||
|
@ -19,6 +63,11 @@ export function getHostFromRequest(req, includeProto = true) {
|
||||||
return `${proto}://${host}`;
|
return `${proto}://${host}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get IP from request
|
||||||
|
* @param req express req object
|
||||||
|
* @return ip as string
|
||||||
|
*/
|
||||||
export function getIPFromRequest(req) {
|
export function getIPFromRequest(req) {
|
||||||
if (USE_XREALIP) {
|
if (USE_XREALIP) {
|
||||||
const ip = req.headers['x-real-ip'];
|
const ip = req.headers['x-real-ip'];
|
||||||
|
@ -28,25 +77,126 @@ export function getIPFromRequest(req) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { socket, connection } = req;
|
const { socket, connection } = req;
|
||||||
|
|
||||||
let conip = (connection ? connection.remoteAddress : socket.remoteAddress);
|
let conip = (connection ? connection.remoteAddress : socket.remoteAddress);
|
||||||
conip = conip || '0.0.0.1';
|
conip = conip || '0.0.0.1';
|
||||||
|
|
||||||
if (!USE_XREALIP) {
|
if (!USE_XREALIP) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.warn(
|
console.warn(
|
||||||
`Connection not going through reverse proxy! IP: ${conip}`, req.headers,
|
`Connection not going through reverse proxy! IP: ${conip}`, req.headers,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return conip;
|
return conip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if IP is v6 or v4
|
||||||
|
* @param ip ip as string
|
||||||
|
* @return true if ipv6, false otherwise
|
||||||
|
*/
|
||||||
|
export function isIPv6(ip) {
|
||||||
|
return ip.includes(':');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set last digits of IPv6 to zero,
|
||||||
|
* needed because IPv6 assignes subnets to customers, we don't want to
|
||||||
|
* mess with individual ips
|
||||||
|
* @param ip ip as string (v4 or v6)
|
||||||
|
* @return ip as string, and if v6, the last digits set to 0
|
||||||
|
*/
|
||||||
export function getIPv6Subnet(ip) {
|
export function getIPv6Subnet(ip) {
|
||||||
if (ip.includes(':')) {
|
if (isIPv6(ip)) {
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
const ipv6sub = `${ip.split(':').slice(0, 4).join(':')}:0000:0000:0000:0000`;
|
const ipv6sub = `${ip.split(':').slice(0, 4).join(':')}:0000:0000:0000:0000`;
|
||||||
return ipv6sub;
|
return ipv6sub;
|
||||||
}
|
}
|
||||||
return ip;
|
return ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get numerical start and end of range
|
||||||
|
* @param range sring of range in the format 'xxx.xxx.xxx.xxx - xxx.xxx.xxx.xxx'
|
||||||
|
* @return [start, end] with numerical IPs (32bit integer)
|
||||||
|
*/
|
||||||
|
function ip4RangeStrToRangeNum(range) {
|
||||||
|
const [start, end] = range.split('-')
|
||||||
|
.map(ip4ToNum);
|
||||||
|
if (!start || !end || start > end) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return [start, end];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get Array of CIDRs for an numerical IPv4 range
|
||||||
|
* @param [start, end] with numerical IPs (32bit integer)
|
||||||
|
* @return Array of CIDR strings
|
||||||
|
*/
|
||||||
|
function ip4RangeNumToCIDR([start, end]) {
|
||||||
|
let maskNum = 32;
|
||||||
|
let mask = 0xFFFFFFFF;
|
||||||
|
const diff = start ^ end;
|
||||||
|
while (diff & mask) {
|
||||||
|
mask <<= 1;
|
||||||
|
maskNum -= 1;
|
||||||
|
}
|
||||||
|
if ((start & (~mask)) || (~(end | mask))) {
|
||||||
|
const divider = start | (~mask >> 1);
|
||||||
|
return ip4RangeNumToCIDR([start, divider]).concat(
|
||||||
|
ip4RangeNumToCIDR([divider + 1, end]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return [`${ip4NumToStr(start)}/${maskNum}`];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get Array of CIDRs for an IPv4 range
|
||||||
|
* @param range sring of range in the format 'xxx.xxx.xxx.xxx - xxx.xxx.xxx.xxx'
|
||||||
|
* @return Array of CIDR strings
|
||||||
|
*/
|
||||||
|
export function ip4RangeToCIDR(range) {
|
||||||
|
const rangeNum = ip4RangeStrToRangeNum(range);
|
||||||
|
if (!rangeNum) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ip4RangeNumToCIDR(rangeNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get specific CIDR in numeric range that includes numeric ip
|
||||||
|
* @param ip numerical ip (32bit integer)
|
||||||
|
* @param [start, end] with numerical IPs (32bit integer)
|
||||||
|
* @return CIDR string
|
||||||
|
*/
|
||||||
|
function ip4NumInRangeNumToCIDR(ip, [start, end]) {
|
||||||
|
let maskNum = 32;
|
||||||
|
let mask = 0xFFFFFFFF;
|
||||||
|
const diff = start ^ end;
|
||||||
|
while (diff & mask) {
|
||||||
|
mask <<= 1;
|
||||||
|
maskNum -= 1;
|
||||||
|
}
|
||||||
|
if ((start & (~mask)) || (~(end | mask))) {
|
||||||
|
const divider = start | (~mask >> 1);
|
||||||
|
if (ip <= divider) {
|
||||||
|
return ip4NumInRangeNumToCIDR(ip, [start, divider]);
|
||||||
|
}
|
||||||
|
return ip4NumInRangeNumToCIDR(ip, [divider + 1, end]);
|
||||||
|
}
|
||||||
|
return `${ip4NumToStr(start)}/${maskNum}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get specific CIDR in range that includes ip
|
||||||
|
* @param ip ip string ('xxx.xxx.xxx.xxx')
|
||||||
|
* @param range string ('xxx.xxx.xxx.xxx - xxx.xxx.xxx.xxx')
|
||||||
|
* @return CIDR string
|
||||||
|
*/
|
||||||
|
export function ip4InRangeToCIDR(ip, range) {
|
||||||
|
const rangeNum = ip4RangeStrToRangeNum(range);
|
||||||
|
const ipNum = ip4ToNum(ip);
|
||||||
|
if (!ipNum || !rangeNum || rangeNum[0] > ip || rangeNum[1] < ip) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ip4NumInRangeNumToCIDR(ipNum, rangeNum);
|
||||||
|
}
|
||||||
|
|
58
src/utils/whois.js
Normal file
58
src/utils/whois.js
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* get information from ip
|
||||||
|
*/
|
||||||
|
|
||||||
|
import whoiser from 'whoiser';
|
||||||
|
|
||||||
|
import { isIPv6, ip4InRangeToCIDR } from './ip';
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get CIDR of ip from whois return
|
||||||
|
* @param ip ip string
|
||||||
|
* @param whois whois return
|
||||||
|
* @return cidr string
|
||||||
|
*/
|
||||||
|
function cIDRofWhois(ip, whoisData) {
|
||||||
|
if (isIPv6(ip)) {
|
||||||
|
return whoisData.inet6num || 'N/A';
|
||||||
|
}
|
||||||
|
return ip4InRangeToCIDR(ip, whoisData.range) || 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get organisation from whois return
|
||||||
|
* @param whois whois return
|
||||||
|
* @return organisation string
|
||||||
|
*/
|
||||||
|
function orgFromWhois(whoisData) {
|
||||||
|
return (whoisData.organisation && whoisData.organisation['org-name'])
|
||||||
|
|| (whoisData['Contact Master']
|
||||||
|
&& whoisData['Contact Master'].address.split('\n')[0])
|
||||||
|
|| whoisData.netname
|
||||||
|
|| 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* parse whois return
|
||||||
|
* @param ip ip string
|
||||||
|
* @param whois whois return
|
||||||
|
* @return object with whois data
|
||||||
|
*/
|
||||||
|
function parseWhois(ip, whoisData) {
|
||||||
|
return {
|
||||||
|
ip,
|
||||||
|
country: whoisData.country || 'N/A',
|
||||||
|
cidr: cIDRofWhois(ip, whoisData),
|
||||||
|
org: orgFromWhois(whoisData),
|
||||||
|
descr: whoisData.descr || 'N/A',
|
||||||
|
asn: whoisData.asn || 'N/A',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function whois(ip) {
|
||||||
|
const whoisData = await whoiser.ip(ip);
|
||||||
|
return parseWhois(ip, whoisData);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default whois;
|
Loading…
Reference in New Issue
Block a user