diff --git a/src/core/ChatProvider.js b/src/core/ChatProvider.js index d3751af..5390f03 100644 --- a/src/core/ChatProvider.js +++ b/src/core/ChatProvider.js @@ -11,7 +11,7 @@ import { import { findIdByNameOrId } from '../data/sql/RegUser'; import ChatMessageBuffer from './ChatMessageBuffer'; import socketEvents from '../socket/SocketEvents'; -import { cheapDetector } from './isProxy'; +import cheapDetector from './isProxy'; import { DailyCron } from '../utils/cron'; import { escapeMd } from './utils'; import ttags from './ttag'; diff --git a/src/core/isProxy.js b/src/core/isProxy.js index efe0303..53bf075 100644 --- a/src/core/isProxy.js +++ b/src/core/isProxy.js @@ -2,7 +2,8 @@ import fetch from '../utils/proxiedFetch'; import redis from '../data/redis/client'; import { getIPv6Subnet } from '../utils/ip'; -import { Blacklist, Whitelist } from '../data/sql'; +import whois from '../utils/whois'; +import { Blacklist, Whitelist, IPInfo } from '../data/sql'; import { proxyLogger as logger } from './logger'; import { USE_PROXYCHECK } from './config'; @@ -37,14 +38,17 @@ async function getIPIntel(ip) { logger.info('PROXYCHECK fetch getipintel is proxy? %s : %s', ip, body); // returns tru iff we found 1 in the response and was ok (http code = 200) const value = parseFloat(body); - return value > 0.995; + return [ + value > 0.995, + `score:${value}`, + ]; } /* * check proxycheck.io if IP is proxy * Use proxiedFetch with random proxies * @param ip IP to check - * @return true if proxy, false if not + * @return [ isProxy, info] true if proxy and extra info */ async function getProxyCheck(ip) { const url = `http://proxycheck.io/v2/${ip}?risk=1&vpn=1&asn=1`; @@ -61,7 +65,17 @@ async function getProxyCheck(ip) { } const data = await response.json(); logger.info('PROXYCHECK proxycheck is proxy?', data); - return data.status === 'ok' && data[ip].proxy === 'yes'; + if (!data.status) { + return [ + false, + 'status not ok', + ]; + } + const ipData = data[ip]; + return [ + ipData.proxy === 'yes', + `${ipData.type},${ipData.city}`, + ]; } /* @@ -98,7 +112,16 @@ async function isWhitelisted(ip) { * dummy function to include if you don't want any proxycheck */ async function dummy() { - return false; + return [false, 'dummy']; +} + +async function saveIPInfo(ip, isProxy, info) { + const whoisData = await whois(ip); + IPInfo.upsert({ + ...whoisData, + isProxy, + pcheck: info, + }); } /* @@ -116,7 +139,8 @@ async function withoutCache(f, ip) { if (await isBlacklisted(ipKey)) { return true; } - const result = f(ip); + const [result, info] = await f(ip); + saveIPInfo(ip, result, info); return result; } @@ -145,33 +169,30 @@ async function withCache(f, ip) { if (checking.indexOf(ipKey) === -1 && lock > 0) { lock -= 1; checking.push(ipKey); - withoutCache(f, ip) - .then((result) => { - const value = result ? 'y' : 'n'; - redis.set(key, value, { - EX: 3 * 24 * 3600, - }); // cache for three days - const pos = checking.indexOf(ipKey); - if (~pos) checking.splice(pos, 1); - lock += 1; - }) - .catch((error) => { - logger.error('PROXYCHECK withCache %s', error.message || error); - const pos = checking.indexOf(ipKey); - if (~pos) checking.splice(pos, 1); - lock += 1; - }); + try { + const result = await withoutCache(f, ip); + const value = result ? 'y' : 'n'; + redis.set(key, value, { + EX: 3 * 24 * 3600, + }); // cache for three days + const pos = checking.indexOf(ipKey); + if (~pos) checking.splice(pos, 1); + } catch (error) { + logger.error('PROXYCHECK withCache %s', error.message || error); + const pos = checking.indexOf(ipKey); + if (~pos) checking.splice(pos, 1); + } finally { + lock += 1; + } } return false; } -export function cheapDetector(ip) { +function cheapDetector(ip) { if (USE_PROXYCHECK) { return withCache(getProxyCheck, ip); } return withCache(dummy, ip); } -export function blacklistDetector(ip) { - return withCache(dummy, ip); -} +export default cheapDetector; diff --git a/src/data/sql/IPInfo.js b/src/data/sql/IPInfo.js new file mode 100644 index 0000000..44d82b8 --- /dev/null +++ b/src/data/sql/IPInfo.js @@ -0,0 +1,81 @@ +import { DataTypes } from 'sequelize'; +import sequelize from './sequelize'; + + +const IPInfo = sequelize.define('IPInfo', { + ip: { + type: DataTypes.CHAR(39), + allowNull: false, + primaryKey: true, + }, + + uuid: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + allowNull: false, + }, + + country: { + type: DataTypes.CHAR(2), + defaultValue: 'xx', + allowNull: false, + }, + + cidr: { + type: DataTypes.CHAR(43), + allowNull: false, + }, + + org: { + type: DataTypes.CHAR(60), + }, + + descr: { + type: DataTypes.CHAR(60), + }, + + asn: { + type: DataTypes.CHAR(12), + allowNull: false, + }, + + proxy: { + type: DataTypes.TINYINT, + defaultValue: 0, + }, + + /* + * extra information from + * proxycheck + */ + pcheck: { + type: DataTypes.CHAR(60), + }, +}, { + getterMethods: { + isProxy() { + return !!this.proxy; + }, + }, + + setterMethods: { + isProxy(proxy) { + const num = (proxy) ? 1 : 0; + this.setDataValue('proxy', num); + }, + + org(value) { + this.setDataValue('org', value.slice(0, 60)); + }, + + descr(value) { + this.setDataValue('descr', value.slice(0, 60)); + }, + + pcheck(value) { + this.setDataValue('pcheck', value.slice(0, 60)); + }, + }, +}); + +export default IPInfo; diff --git a/src/data/sql/index.js b/src/data/sql/index.js index b9cbabe..80c1a2b 100644 --- a/src/data/sql/index.js +++ b/src/data/sql/index.js @@ -5,6 +5,7 @@ import Channel from './Channel'; import UserChannel from './UserChannel'; import Message from './Message'; import UserBlock from './UserBlock'; +import IPInfo from './IPInfo'; /* * User Channel access @@ -43,4 +44,5 @@ export { UserChannel, Message, UserBlock, + IPInfo, }; diff --git a/src/routes/api/me.js b/src/routes/api/me.js index 57eff75..aea83d7 100644 --- a/src/routes/api/me.js +++ b/src/routes/api/me.js @@ -7,7 +7,7 @@ import getMe from '../../core/me'; import { USE_PROXYCHECK, } from '../../core/config'; -import { cheapDetector } from '../../core/isProxy'; +import cheapDetector from '../../core/isProxy'; export default async (req, res, next) => { diff --git a/src/socket/SocketServer.js b/src/socket/SocketServer.js index 9a93ece..737f2f4 100644 --- a/src/socket/SocketServer.js +++ b/src/socket/SocketServer.js @@ -25,7 +25,7 @@ import chatProvider, { ChatProvider } from '../core/ChatProvider'; import authenticateClient from './authenticateClient'; import { drawByOffsets } from '../core/draw'; import { needCaptcha } from '../data/redis/captcha'; -import { cheapDetector } from '../core/isProxy'; +import cheapDetector from '../core/isProxy'; const ipCounter = new Counter(); diff --git a/src/utils/whois.js b/src/utils/whois.js index 9707af0..1cf5fc9 100644 --- a/src/utils/whois.js +++ b/src/utils/whois.js @@ -42,7 +42,7 @@ function orgFromWhois(whoisData) { function parseWhois(ip, whoisData) { return { ip, - country: whoisData.country || 'N/A', + country: whoisData.country || 'xx', cidr: cIDRofWhois(ip, whoisData), org: orgFromWhois(whoisData), descr: whoisData.descr || 'N/A',