From 931ffcc296b141e05f93c90e4933cc49121d9d3c Mon Sep 17 00:00:00 2001 From: HF Date: Wed, 21 Sep 2022 18:52:24 +0200 Subject: [PATCH] move sql out of core/Rank finish own whois library --- src/core/Ranks.js | 45 +---- src/core/draw.js | 189 +---------------- src/core/isAllowed.js | 47 +++-- src/data/sql/RegUser.js | 40 +++- src/routes/api/me.js | 14 -- src/socket/SocketServer.js | 9 +- src/utils/ProxyCheck.js | 4 +- src/utils/whois.js | 402 +++++++++++++++++-------------------- 8 files changed, 273 insertions(+), 477 deletions(-) diff --git a/src/core/Ranks.js b/src/core/Ranks.js index d05b438..3f2c679 100644 --- a/src/core/Ranks.js +++ b/src/core/Ranks.js @@ -2,8 +2,7 @@ * timers and cron for account related actions */ -import Sequelize from 'sequelize'; -import RegUser from '../data/sql/RegUser'; +import { populateRanking } from '../data/sql/RegUser'; import { getRanks, resetDailyRanks, @@ -42,44 +41,6 @@ class Ranks { DailyCron.hook(this.resetDailyRanking); } - /* - * take array of {useId: score} and resolve - * user informations - */ - static async populateRanking(rawRanks) { - if (!rawRanks.length) { - return rawRanks; - } - const uids = rawRanks.map((r) => r.id); - const userData = await RegUser.findAll({ - attributes: [ - 'id', - 'name', - [ - Sequelize.fn( - 'DATEDIFF', - Sequelize.literal('CURRENT_TIMESTAMP'), - Sequelize.col('createdAt'), - ), - 'age', - ], - ], - where: { - id: uids, - }, - raw: true, - }); - for (let i = 0; i < userData.length; i += 1) { - const { id, name, age } = userData[i]; - const dat = rawRanks.find((r) => r.id === id); - if (dat) { - dat.name = name; - dat.age = age; - } - } - return rawRanks; - } - static async updateRanking() { /* * only main shard updates and sends it to others @@ -87,13 +48,13 @@ class Ranks { if (!socketEvents.amIImportant()) { return; } - const ranking = await Ranks.populateRanking( + const ranking = await populateRanking( await getRanks( false, 1, 100, )); - const dailyRanking = await Ranks.populateRanking( + const dailyRanking = await populateRanking( await getRanks( true, 1, diff --git a/src/core/draw.js b/src/core/draw.js index b7ff29a..2fdf334 100644 --- a/src/core/draw.js +++ b/src/core/draw.js @@ -6,13 +6,10 @@ import { getPixelFromChunkOffset, } from './utils'; import logger, { pixelLogger } from './logger'; -import RedisCanvas from '../data/redis/RedisCanvas'; import allowPlace from '../data/redis/cooldown'; import socketEvents from '../socket/socketEvents'; -import { - setPixelByOffset, - setPixelByCoords, -} from './setPixel'; +import { setPixelByOffset } from './setPixel'; +import isIPAllowed from './isAllowed'; import rankings from './Ranks'; import canvases from './canvases'; @@ -58,7 +55,7 @@ setInterval(() => { * Offset is the offset of the pixel within the chunk * @return Promise */ -export async function drawByOffsets( +export default async function drawByOffsets( user, canvasId, i, @@ -70,7 +67,6 @@ export async function drawByOffsets( let retCode = 0; let pxlCnt = 0; let rankedPxlCnt = 0; - let needProxycheck = 0; const { ipSub: ip } = user; try { @@ -189,6 +185,7 @@ export async function drawByOffsets( } } + let needProxycheck; [retCode, pxlCnt, wait, coolDown, needProxycheck] = await allowPlace( ip, user.id, @@ -202,6 +199,10 @@ export async function drawByOffsets( pxlOffsets, ); + if (needProxycheck) { + await isIPAllowed(ip, true); + } + for (let u = 0; u < pxlCnt; u += 1) { const [offset, color] = pixels[u]; setPixelByOffset(canvasId, color, i, j, offset); @@ -235,179 +236,5 @@ export async function drawByOffsets( pxlCnt, rankedPxlCnt, retCode, - needProxycheck, - }; -} - - -/** - * - * Old version of draw that returns explicit error messages - * used for http json api/pixel, used with coordinates - * Is not used anywhere currently, but we keep it around. - * @param user - * @param canvasId - * @param x - * @param y - * @param color - * @returns {Promise.} - */ -export async function drawByCoords( - user, - canvasId, - color, - x, - y, - z = null, -) { - const canvas = canvases[canvasId]; - - if (!canvas) { - return { - error: 'This canvas does not exist', - success: false, - }; - } - - const canvasMaxXY = canvas.size / 2; - const canvasMinXY = -canvasMaxXY; - if (x < canvasMinXY || x >= canvasMaxXY) { - return { - error: 'x Coordinate not within canvas', - success: false, - }; - } - - const clrIgnore = canvas.cli || 0; - - if (canvas.v) { - if (z < canvasMinXY || z >= canvasMaxXY) { - return { - error: 'z Coordinate not within canvas', - success: false, - }; - } - if (y >= THREE_CANVAS_HEIGHT) { - return { - error: 'You reached build limit. Can\'t place higher than 128 blocks.', - success: false, - }; - } - if (y < 0) { - return { - error: 'Can\'t place on y < 0', - success: false, - }; - } - if (z === null) { - return { - error: 'This is a 3D canvas. z is required.', - success: false, - }; - } - } else { - if (y < canvasMinXY || y >= canvasMaxXY) { - return { - error: 'y Coordinate not within canvas', - success: false, - }; - } - if (color < clrIgnore) { - return { - error: 'Invalid color selected', - success: false, - }; - } - if (z !== null) { - if (!canvas.v) { - return { - error: 'This is not a 3D canvas', - success: false, - }; - } - } - } - - if (color < 0 || color >= canvas.colors.length) { - return { - error: 'Invalid color selected', - success: false, - }; - } - - if (canvas.req !== -1) { - if (user.id === null) { - return { - errorTitle: 'Not Logged In', - error: 'You need to be logged in to use this canvas.', - success: false, - }; - } - // if the canvas has a requirement of totalPixels that the user - // has to have set - const totalPixels = await user.getTotalPixels(); - if (totalPixels < canvas.req) { - return { - errorTitle: 'Not Yet :(', - // eslint-disable-next-line max-len - error: `You need to set ${canvas.req} pixels on another canvas first, before you can use this one.`, - success: false, - }; - } - } - - const isAdmin = (user.userlvl === 1); - const setColor = await RedisCanvas.getPixel(canvasId, canvas.size, x, y, z); - - /* - * bitwise operation to get rid of protection - */ - let coolDown = ((setColor & 0x3F) >= clrIgnore && canvas.pcd) - ? canvas.pcd : canvas.bcd; - if (isAdmin) { - coolDown = 0.0; - } else { - coolDown *= coolDownFactor; - } - - const now = Date.now(); - let wait = await user.getWait(canvasId); - if (!wait) wait = now; - wait += coolDown; - const waitLeft = wait - now; - if (waitLeft > canvas.cds) { - return { - success: false, - waitSeconds: (waitLeft - coolDown) / 1000, - coolDownSeconds: (canvas.cds - waitLeft) / 1000, - }; - } - - if (setColor & 0x80 - || (canvas.v - && x >= 96 && x <= 128 && z >= 35 && z <= 100 - && !isAdmin) - ) { - logger.info(`${user.ip} tried to set on protected pixel (${x}, ${y})`); - return { - errorTitle: 'Pixel Protection', - error: 'This pixel is protected', - success: false, - waitSeconds: (waitLeft - coolDown) / 1000, - }; - } - - setPixelByCoords(canvasId, color, x, y, z); - - user.setWait(waitLeft, canvasId); - /* hardcode to not count pixels in antarctica */ - // eslint-disable-next-line eqeqeq - if (canvas.ranked && (canvasId != 0 || y < 14450)) { - user.incrementPixelcount(); - } - return { - success: true, - waitSeconds: waitLeft / 1000, - coolDownSeconds: coolDown / 1000, }; } diff --git a/src/core/isAllowed.js b/src/core/isAllowed.js index 8e6c787..bf79c4d 100644 --- a/src/core/isAllowed.js +++ b/src/core/isAllowed.js @@ -44,13 +44,13 @@ async function saveIPInfo(ip, whoisRet, allowed, info) { } /* - * execute proxycheck without caring about cache - * @param f function for checking if proxy - * @param ip IP to check - * @return true if proxy or blacklisted, false if not or whitelisted + * execute proxycheck and blacklist whitelist check + * @param f proxycheck function + * @param ip full ip + * @param ipKey + * @return [ allowed, status, pcheck ] */ -async function withoutCache(f, ip) { - const ipKey = getIPv6Subnet(ip); +async function checkPCAndLists(f, ip, ipKey) { let allowed = true; let status = -2; let pcheck = null; @@ -70,16 +70,33 @@ async function withoutCache(f, ip) { allowed = res.allowed; pcheck = res.pcheck; } - } finally { - let whoisRet = null; - try { - whoisRet = await whois(ip); - } catch (err) { - logger.error(`Error whois for ${ip}: ${err.message}`); - } - await cacheAllowed(ipKey, status); - await saveIPInfo(ipKey, whoisRet || {}, status, pcheck); + } catch (err) { + logger.error(`Error checkAllowed for ${ip}: ${err.message}`); } + return [allowed, status, pcheck]; +} + +/* + * execute proxycheck and whois and save result into cache + * @param f function for checking if proxy + * @param ip IP to check + * @return checkifAllowed return + */ +async function withoutCache(f, ip) { + const ipKey = getIPv6Subnet(ip); + + const [ + [allowed, status, pcheck], + whoisRet, + ] = await Promise.all([ + checkPCAndLists(f, ip, ipKey), + whois(ip), + ]); + + await Promise.all([ + cacheAllowed(ipKey, status), + saveIPInfo(ipKey, whoisRet, status, pcheck), + ]); return { allowed, diff --git a/src/data/sql/RegUser.js b/src/data/sql/RegUser.js index ba79508..a016c1c 100644 --- a/src/data/sql/RegUser.js +++ b/src/data/sql/RegUser.js @@ -5,7 +5,7 @@ * */ -import { DataTypes, QueryTypes } from 'sequelize'; +import Sequelize, { DataTypes, QueryTypes } from 'sequelize'; import sequelize from './sequelize'; import { generateHash } from '../../utils/hash'; @@ -181,4 +181,42 @@ export async function getNamesToIds(ids) { return idToNameMap; } +/* + * take array of {id: useId, ...} object and resolve + * user informations + */ +export async function populateRanking(rawRanks) { + if (!rawRanks.length) { + return rawRanks; + } + const uids = rawRanks.map((r) => r.id); + const userData = await RegUser.findAll({ + attributes: [ + 'id', + 'name', + [ + Sequelize.fn( + 'DATEDIFF', + Sequelize.literal('CURRENT_TIMESTAMP'), + Sequelize.col('createdAt'), + ), + 'age', + ], + ], + where: { + id: uids, + }, + raw: true, + }); + for (let i = 0; i < userData.length; i += 1) { + const { id, name, age } = userData[i]; + const dat = rawRanks.find((r) => r.id === id); + if (dat) { + dat.name = name; + dat.age = age; + } + } + return rawRanks; +} + export default RegUser; diff --git a/src/routes/api/me.js b/src/routes/api/me.js index ba871b4..429dd4e 100644 --- a/src/routes/api/me.js +++ b/src/routes/api/me.js @@ -4,26 +4,12 @@ import getMe from '../../core/me'; -import { - USE_PROXYCHECK, -} from '../../core/config'; -import checkIPAllowed from '../../core/isAllowed'; - export default async (req, res, next) => { try { const { user, lang } = req; const userdata = await getMe(user, lang); user.updateLogInTimestamp(); - - const { trueIp: ip } = req; - if (USE_PROXYCHECK) { - // pre-fire ip check to give it time to get a real result - // once api_pixel needs it - checkIPAllowed(ip); - } - - // https://stackoverflow.com/questions/49547/how-to-control-web-page-caching-across-all-browsers res.json(userdata); } catch (error) { next(error); diff --git a/src/socket/SocketServer.js b/src/socket/SocketServer.js index 72c4493..7bc765a 100644 --- a/src/socket/SocketServer.js +++ b/src/socket/SocketServer.js @@ -22,7 +22,7 @@ import OnlineCounter from './packets/OnlineCounter'; import socketEvents from './socketEvents'; import chatProvider, { ChatProvider } from '../core/ChatProvider'; import authenticateClient from './authenticateClient'; -import { drawByOffsets } from '../core/draw'; +import drawByOffsets from '../core/draw'; import isIPAllowed from '../core/isAllowed'; @@ -87,7 +87,6 @@ class SocketServer { ws.chunkCnt = 0; const { ip } = user; - isIPAllowed(ip); ws.send(OnlineCounter.dehydrate(socketEvents.onlineCounter)); @@ -174,6 +173,7 @@ class SocketServer { const { headers } = request; // Limiting socket connections per ip const ip = getIPFromRequest(request); + isIPAllowed(ip); const now = Date.now(); const limiter = rateLimit.get(ip); if (limiter && limiter[1]) { @@ -506,7 +506,6 @@ class SocketServer { pxlCnt, rankedPxlCnt, retCode, - needProxycheck, } = await drawByOffsets( ws.user, canvasId, @@ -514,10 +513,6 @@ class SocketServer { pixels, ); - if (needProxycheck) { - isIPAllowed(ip); - } - if (retCode > 9 && retCode !== 13) { const now = Date.now(); if (limiter && limiter[0] > now) { diff --git a/src/utils/ProxyCheck.js b/src/utils/ProxyCheck.js index 16ba8ca..79e7063 100644 --- a/src/utils/ProxyCheck.js +++ b/src/utils/ProxyCheck.js @@ -245,7 +245,7 @@ class ProxyCheck { if (!key) { setTimeout( () => reject(new Error('No pc key available')), - 5000, + 2000, ); return; } @@ -318,7 +318,7 @@ class ProxyCheck { }); }); - req.setTimeout(60000, () => { + req.setTimeout(30000, () => { req.destroy(new Error('Connection TIMEOUT')); }); req.on('error', (err) => { diff --git a/src/utils/whois.js b/src/utils/whois.js index 15e84da..37ef260 100644 --- a/src/utils/whois.js +++ b/src/utils/whois.js @@ -11,124 +11,146 @@ const WHOIS_PORT = 43; const QUERY_SUFFIX = '\r\n'; const WHOIS_TIMEOUT = 30000; -function splitStringBy(string, by) { - return [string.slice(0, by), string.slice(by + 1)]; -} - +/* + * parse whois return into fields + */ function parseSimpleWhois(whois) { - const data = {}; - const text = []; - - const renameLabels = { - NetRange: 'range', - inetnum: 'range', - CIDR: 'route', - origin: 'asn', - OriginAS: 'asn', - }; - const lineToGroup = { - contact: 'contact', - OrgName: 'organisation', - organisation: 'organisation', - OrgAbuseHandle: 'contactAbuse', - irt: 'contactAbuse', - RAbuseHandle: 'contactAbuse', - OrgTechHandle: 'contactTechnical', - RTechHandle: 'contactTechnical', - OrgNOCHandle: 'contactNoc', - RNOCHandle: 'contactNoc', + let data = { + groups: {}, }; - if (whois.includes('returned 0 objects') || whois.includes('No match found')) { - return data; - } - - let resultNum = 0; const groups = [{}]; + const text = []; + const lines = whois.split('\n'); let lastLabel; - whois.split('\n').forEach((line) => { - // catch comment lines + for (let i = 0; i < lines.length; i += 1) { + const line = lines[i].trim(); if (line.startsWith('%') || line.startsWith('#')) { - // detect if an ASN or IP has multiple WHOIS results - if (line.includes('# start')) { - // nothing - } else if (line.includes('# end')) { - resultNum++; - } else { + /* + * detect if an ASN or IP has multiple WHOIS results, + * and only care about first one + */ + if (line.includes('# end')) { + break; + } else if (!lines.includes('# start')) { text.push(line); } - } else if (resultNum === 0) { - // for the moment, parse only first WHOIS result - - if (line) { - if (line.includes(':')) { - const [label, value] = splitStringBy(line, line.indexOf(':')).map((info) => info.trim()); - lastLabel = label; - - // 1) Filter out unnecessary info, 2) then detect if the label is already added to group - if (value.includes('---')) { - // do nothing with useless data - } else if (groups[groups.length - 1][label]) { - groups[groups.length - 1][label] += `\n${value}`; - } else { - groups[groups.length - 1][label] = value; - } + continue; + } + if (line) { + const sep = line.indexOf(':'); + if (~sep) { + const label = line.slice(0, sep).toLowerCase(); + lastLabel = label; + const value = line.slice(sep + 1).trim(); + // 1) Filter out unnecessary info, 2) then detect if the label is already added to group + if (value.includes('---')) { + // do nothing with useless data + } else if (groups[groups.length - 1][label]) { + groups[groups.length - 1][label] += `\n${value}`; } else { - groups[groups.length - 1][lastLabel] += `\n${line.trim()}`; + groups[groups.length - 1][label] = value; } - } else if (Object.keys(groups[groups.length - 1]).length) { - // if empty line, means another info group starts - groups.push({}); + } else { + groups[groups.length - 1][lastLabel] += `\n${line}`; } + } else if (Object.keys(groups[groups.length - 1]).length) { + // if empty line, means another info group starts + groups.push({}); + } + } + + groups.forEach((group) => { + if (group.role) { + const role = group.role.replaceAll(' ', '-').toLowerCase(); + delete group.role; + data.groups[role] = group; + } else { + data = { + ...group, + ...data, + }; } }); - groups - .filter((group) => Object.keys(group).length) - .forEach((group) => { - const groupLabels = Object.keys(group); - let isGroup = false; - - // check if a label is marked as group - groupLabels.forEach((groupLabel) => { - if (!isGroup && Object.keys(lineToGroup).includes(groupLabel)) { - isGroup = lineToGroup[groupLabel]; - } - }); - - // check if a info group is a Contact in APNIC result - // @Link https://www.apnic.net/manage-ip/using-whois/guide/role/ - if (!isGroup && groupLabels.includes('role')) { - isGroup = `Contact ${group.role.split(' ')[1]}`; - } else if (!isGroup && groupLabels.includes('person')) { - isGroup = `Contact ${group['nic-hdl']}`; - } - - if (isGroup === 'contact') { - data.contacts = data.contacts || {}; - data.contacts[group.contact] = group; - } else if (isGroup) { - data[isGroup] = group; - } else { - groupLabels.forEach((key) => { - const label = renameLabels[key] || key; - data[label] = group[key]; - }); - } - }); - - // Append the WHOIS comments - data.text = text; + data.text = text.join('\n'); return data; } -function whoisQuery( - host = null, - query = '', +/* + * parse whois return + * @param ip ip string + * @param whois whois return + * @return object with whois data + */ +function parseWhois(ip, whoisReturn) { + const whoisData = parseSimpleWhois(whoisReturn); + + let cidr; + if (isIPv6(ip)) { + const range = whoisData.inet6num || whoisData.netrange || whoisData.inetnum + || whoisData.route || whoisData.cidr; + cidr = range && !range.includes('-') && range; + } else { + const range = whoisData.inetnum || whoisData.netrange + || whoisData.route || whoisData.cidr; + if (range) { + if (range.includes('/') && !range.includes('-')) { + cidr = range; + } else { + cidr = ip4InRangeToCIDR(ip, range); + } + } + } + + let org = whoisData['org-name'] + || whoisData.organization + || whoisData.orgname + || whoisData.descr + || whoisData['mnt-by']; + if (!org) { + const contactGroup = Object.keys(whoisData.groups).find( + (g) => whoisData.groups[g].address, + ); + if (contactGroup) { + [org] = whoisData.groups[contactGroup].address.split('\n'); + } else { + org = whoisData.owner || whoisData['mnt-by'] || 'N/A'; + } + } + const descr = whoisData.netname || whoisData.descr || 'N/A'; + const asn = whoisData.asn + || whoisData.origin + || whoisData.originas + || whoisData['aut-num'] || 'N/A'; + let country = whoisData.country + || (whoisData.organisation && whoisData.organisation.Country) + || 'xx'; + if (country.length > 2) { + country = country.slice(0, 2); + } + + return { + ip, + cidr: cidr || 'N/A', + org, + country, + asn, + descr, + }; +} + +/* + * send a raw whois query to server + * @param query + * @param host + */ +function singleWhoisQuery( + query, + host, ) { - console.log('whois with query', query, 'for host', host); return new Promise((resolve, reject) => { let data = ''; const socket = net.createConnection({ @@ -144,126 +166,76 @@ function whoisQuery( }); } -async function whoisIp(query) { - query = String(query); - - // find WHOIS server for IP - let whoisResult = await whoisQuery('whois.iana.org', query); - whoisResult = parseSimpleWhois(whoisResult); - const host = whoisResult.whois; - - if (!host) { - throw new Error(`No WHOIS server for "${query}"`); - } - - // hardcoded custom queries.. - console.log('HOST', host); - if (host === 'whois.arin.net') { - query = `+ n ${query}`; - } else if (host === 'whois.ripe.net') { - /* - * flag to not return personal informations, otherwise - * RIPE is gonne rate limit and ban - */ - query = `-r ${query}`; - } - - const rawResult = await whoisQuery(host, query); - console.log(rawResult); - const data = parseSimpleWhois(rawResult); - - return data; -} - /* - * get CIDR of ip from whois return - * @param ip ip string - * @param whois whois return - * @return cidr string + * check if whois result is refering us to + * a different whois server */ -function cIDRofWhois(ip, whoisData) { - if (isIPv6(ip)) { - return whoisData.inet6num - || (whoisData.range && !whoisData.range.includes('-') && whoisData.range) - || whoisData.route - || null; - } - const { range } = whoisData; - if (range && range.includes('/') && !range.includes('-')) { - return range; - } - return ip4InRangeToCIDR(ip, range) || null; -} - -/* - * get organisation from whois return - * @param whois whois return - * @return organisation string - */ -function orgFromWhois(whoisData) { - return (whoisData.organisation && whoisData.organisation['org-name']) - || (whoisData.organisation && whoisData.organisation.OrgName) - || (whoisData['Contact Master'] - && whoisData['Contact Master'].address.split('\n')[0]) - || (whoisData['Contact undefined'] - && whoisData['Contact undefined'].person) - || whoisData.netname - || whoisData.owner - || 'N/A'; -} - -/* - * get counry from whois return - * @param whois whois return - * @return organisation string - */ -function countryFromWhois(whoisData) { - return whoisData.country - || (whoisData.organisation && whoisData.organisation.Country) - || 'xx'; -} - -/* - * parse whois return - * @param ip ip string - * @param whois whois return - * @return object with whois data - */ -function parseWhois(ip, whoisData) { - return { - ip, - country: countryFromWhois(whoisData), - cidr: cIDRofWhois(ip, whoisData) || 'N/A', - org: orgFromWhois(whoisData), - descr: whoisData.descr || 'N/A', - asn: whoisData.asn || whoisData['aut-num'] || 'N/A', - }; -} - -export default async function whoiser(ip) { - const whoisData = await whoisIp(ip); - if (whoisData.ReferralServer) { - let referral = whoisData.ReferralServer; - const prot = referral.indexOf('://'); - if (prot !== -1) { - referral = referral.slice(prot + 3); - } - try { - /* - * if referral whois server produces any error - * fallback to initial one - */ - const refWhoisData = await whoisIp(ip, { - host: referral, - }); - const refParsedData = parseWhois(ip, refWhoisData); - if (refParsedData.cidr !== 'N/A') { - return refParsedData; +const referralKeys = [ + 'whois:', + 'refer:', + 'ReferralServer:', +]; +function checkForReferral( + whoisResult, +) { + for (let u = 0; u < referralKeys.length; u += 1) { + const key = referralKeys[u]; + const pos = whoisResult.indexOf(key); + if (~pos) { + const line = whoisResult.slice( + whoisResult.lastIndexOf('\n', pos) + 1, + whoisResult.indexOf('\n', pos), + ).trim(); + if (!line.startsWith(key)) { + continue; } - } catch { - // nothing + let value = line.slice(line.indexOf(':') + 1).trim(); + const prot = value.indexOf('://'); + if (~prot) { + value = value.slice(prot + 3); + } + return value; } } - console.log(whoisData); - return parseWhois(ip, whoisData); + return null; +} + +/* + * whois ip + */ +export default async function whoisIp( + ip, + host = null, +) { + let useHost = host || 'whois.iana.org'; + let whoisResult = ''; + let refCnt = 0; + while (refCnt < 5) { + let queryPrefix = ''; + if (useHost === 'whois.arin.net') { + queryPrefix = '+ n'; + } else if (useHost === 'whois.ripe.net') { + /* + * flag to not return personal informations, otherwise + * RIPE is gonne rate limit and ban + */ + // queryPrefix = '-r'; + } + + try { + // eslint-disable-next-line no-await-in-loop + whoisResult = await singleWhoisQuery(`${queryPrefix} ${ip}`, useHost); + const ref = checkForReferral(whoisResult); + if (!ref) { + break; + } + useHost = ref; + } catch (err) { + // eslint-disable-next-line no-console + console.error(`Error on WHOIS ${ip} ${useHost}: ${err.message}`); + break; + } + refCnt += 1; + } + return parseWhois(ip, whoisResult); }