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';
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
const { headers } = req;
|
||||
const host = headers['x-forwarded-host']
|
||||
|
@ -19,6 +63,11 @@ export function getHostFromRequest(req, includeProto = true) {
|
|||
return `${proto}://${host}`;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get IP from request
|
||||
* @param req express req object
|
||||
* @return ip as string
|
||||
*/
|
||||
export function getIPFromRequest(req) {
|
||||
if (USE_XREALIP) {
|
||||
const ip = req.headers['x-real-ip'];
|
||||
|
@ -28,25 +77,126 @@ export function getIPFromRequest(req) {
|
|||
}
|
||||
|
||||
const { socket, connection } = req;
|
||||
|
||||
let conip = (connection ? connection.remoteAddress : socket.remoteAddress);
|
||||
conip = conip || '0.0.0.1';
|
||||
|
||||
if (!USE_XREALIP) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`Connection not going through reverse proxy! IP: ${conip}`, req.headers,
|
||||
);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (ip.includes(':')) {
|
||||
if (isIPv6(ip)) {
|
||||
// eslint-disable-next-line max-len
|
||||
const ipv6sub = `${ip.split(':').slice(0, 4).join(':')}:0000:0000:0000:0000`;
|
||||
return ipv6sub;
|
||||
}
|
||||
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