Skip to content

Instantly share code, notes, and snippets.

@FlyInk13
Last active April 28, 2019 22:43
Show Gist options
  • Save FlyInk13/5b952b05be4daf17b95559a3d4ec23c9 to your computer and use it in GitHub Desktop.
Save FlyInk13/5b952b05be4daf17b95559a3d4ec23c9 to your computer and use it in GitHub Desktop.
const { spawn } = require('child_process');
const https = require('https');
const info = {
lastPending: false,
lastDataDate: 0,
lastLineDate: 0,
lastPushDate: 0,
cpErrors: '\n',
buffer: '',
ipInfo: [],
ipError: [],
lastIp: {},
};
const HistoryLog = {
history: [],
counters: {},
limit: 10e3,
minuteLimit: 1,
push: (data) => {
HistoryLog.clean();
data = { id: data.id, time: Date.now(), count: data.count };
HistoryLog.history.push(data);
HistoryLog.countIncr(data.id, data.count);
},
clean: () => {
var array = HistoryLog.history;
var count = array.length - 1;
var death = Date.now() - HistoryLog.minuteLimit + 60e3;
var item;
while (count-- > 0) {
item = array[count];
if (item.time < death) {
HistoryLog.countIncr(array[count], -item.count);
array.splice(count, 1);
}
}
if (array.length > HistoryLog.limit) {
var spliced = array.splice(0, array.length - HistoryLog.limit);
HistoryLog.countIncrList(spliced);
}
},
countGet: (data) => {
var count = HistoryLog.countIncr(data, 0);
return count;
},
countIncrList: (list, value) => {
var count = list.length;
var item;
while (count--) {
item = list[count];
HistoryLog.countIncr(item.id, -item.count);
}
},
countIncr: (key, value) => {
if (!HistoryLog.counters[key]) {
HistoryLog.counters[key] = 0;
}
HistoryLog.counters[key] += value;
if (!HistoryLog.counters[key]) {
delete HistoryLog.counters[key];
}
return HistoryLog.counters[key] || 0;
},
};
// ip info
function matchIp(ip) {
if (/^[\d.]+$/.test(ip)) {
return ip.split('.').splice(0, 4).join('.');
} else if (/^[\w\d:.]+$/.test(ip)) {
return ip.split('.').splice(0, 1).join('.');
}
return ip;
}
function toLong(ip) {
var ipl = 0;
ip.split('.').forEach((octet) => {
ipl <<= 8;
ipl += parseInt(octet);
});
return (ipl >>> 0);
};
function fromLong(ipl) {
return (
(ipl >>> 24 ) + '.' +
(ipl >> 16 & 255) + '.' +
(ipl >> 8 & 255) + '.' +
(ipl & 255)
);
};
function fillRange(first, last, description) {
if (!/^[\d.]+$/.test(first)) return;
if (!/^[\d.]+$/.test(last)) return;
if (first !== fromLong(toLong(first))) return console.log('overflow', first, fromLong(toLong(first)));
if (last !== fromLong(toLong(last))) return console.log('overflow', last, fromLong(toLong(last)));
if (toLong(last) <= toLong(first)) return console.log('bad range');
var min = toLong(first);
var max = toLong(last);
var ip = '';
while (max > min) {
ip = fromLong(max--);
info.ipInfo[ip] = ip + ' (' + description + ')';
}
}
function whoIsApi(ip) {
if (info.lastPending) return;
info.lastPending = true;
return new Promise((resolve, reject) => {
https.get('https://api.iptoasn.com/v1/as/ip/' + ip, (res) => {
var buffer = '';
res.on('data', (c) => buffer += c);
res.on('end', () => resolve(buffer));
}).on('error', reject);
}).then((res) => {
try {
return JSON.parse(res);
} catch (e) {
throw { code: 'ECONNRESET' };
}
}).then((r) => {
if (!r.as_description) {
throw r;
}
info.lastIp = r;
if (r.first_ip && r.last_ip) {
fillRange(r.first_ip, r.last_ip, r.as_description);
}
return ip + ' (' + r.as_description + ')';
}).catch((e) => {
if (e.code == 'ECONNRESET') return whoIsApi(ip);
throw e;
}).then((ipName) => {
info.lastPending = false;
info.ipInfo[ip] = ipName;
}).catch((e) => {
info.lastPending = false;
info.ipInfo[ip] = -1;
info.ipError[ip] = e;
console.log(e);
});
}
function getInfo(ip) {
if (!info.ipInfo[ip]) {
info.ipInfo[ip] = -1;
return ip;
} else if (info.ipInfo[ip] == -1) {
return ip;
} else {
return info.ipInfo[ip];
}
}
// tcpdump
function onTcpData(data) {
info.buffer += data;
let index = info.buffer.indexOf('\n');
let line, match;
info.lastDataDate = new Date();
while (index > -1) {
info.lastLineDate = new Date();
line = info.buffer.substr(0, index);
match = line.match(/.+ IP6? (.+) > .+? length (\d+)/);
if (match) {
var [, id, count] = match;
id = matchIp(id);
HistoryLog.push({ id, count: parseInt(count) });
}
info.buffer = info.buffer.substr(index + 1);
index = info.buffer.indexOf('\n');
info.lastPushDate = new Date();
}
}
function tcpdumpStart() {
const tcpdump = spawn('tcpdump', ['-n', '-nn', 'inbound', 'and', 'greater 10']);
tcpdump.stdout.on('data', onTcpData);
tcpdump.stderr.on('data', (data) => info.cpErrors += data);
tcpdump.on('close', (code) => info.cpErrors += '\nEXIT: ' + code);
}
// tick
function onTick() {
var ip = Object.entries(info.ipInfo)
.sort((a, b) => HistoryLog.counters[b[0]] - HistoryLog.counters[a[0]])
.find((ip, i) => i < 30 && ip[1] === -1);
if (ip) whoIsApi(ip[0]);
var top = Object.entries(HistoryLog.counters)
.sort((a, b) => b[1] - a[1])
.map((i) => i[1] + ' - ' + getInfo(i[0]))
.splice(0, 30)
.join('\n');
process.stdout.write('\u001bc');
console.log(top);
console.log('\n\nInfo:');
Object.entries(info).forEach(([name, data]) => {
if (name == 'ipInfo') return;
if (!/last/.test(name)) console.log();
console.log(name + ':', data);
});
console.log('\nLast log:', HistoryLog.history[HistoryLog.history.length - 1]);
}
// init
tcpdumpStart();
setInterval(onTick, 1000);
@FlyInk13
Copy link
Author

@FlyInk13
Copy link
Author

Example out:

374538 - 95.213.11.164 (VKONTAKTE-SPB-AS http://vk.com)
352434 - 149.154.167.99 (TELEGRAM)
31181 - 87.240.188.134 (VKONTAKTE-SPB-AS http://vk.com)
29331 - 87.240.188.175 (VKONTAKTE-SPB-AS http://vk.com)
28241 - 87.240.188.193 (VKONTAKTE-SPB-AS http://vk.com)
28230 - 87.240.188.216 (VKONTAKTE-SPB-AS http://vk.com)
28133 - 87.240.188.150 (VKONTAKTE-SPB-AS http://vk.com)
27603 - 87.240.188.189 (VKONTAKTE-SPB-AS http://vk.com)
27402 - 87.240.188.131 (VKONTAKTE-SPB-AS http://vk.com)
27363 - 87.240.188.176 (VKONTAKTE-SPB-AS http://vk.com)
27222 - 87.240.188.147 (VKONTAKTE-SPB-AS http://vk.com)
27007 - 149.154.167.220 (TELEGRAM)
26950 - 87.240.188.181 (VKONTAKTE-SPB-AS http://vk.com)
25850 - 87.240.188.213 (VKONTAKTE-SPB-AS http://vk.com)
25813 - 87.240.188.148 (VKONTAKTE-SPB-AS http://vk.com)
25597 - 87.240.188.165 (VKONTAKTE-SPB-AS http://vk.com)
25489 - 87.240.188.170 (VKONTAKTE-SPB-AS http://vk.com)
24998 - 87.240.188.202 (VKONTAKTE-SPB-AS http://vk.com)
23967 - 87.240.188.139 (VKONTAKTE-SPB-AS http://vk.com)
23718 - 87.240.188.159 (VKONTAKTE-SPB-AS http://vk.com)
22486 - 87.240.188.222 (VKONTAKTE-SPB-AS http://vk.com)
22212 - 87.240.188.142 (VKONTAKTE-SPB-AS http://vk.com)
22144 - 87.240.188.205 (VKONTAKTE-SPB-AS http://vk.com)
21834 - 87.240.188.224 (VKONTAKTE-SPB-AS http://vk.com)
21545 - 87.240.188.211 (VKONTAKTE-SPB-AS http://vk.com)
21324 - 87.240.188.188 (VKONTAKTE-SPB-AS http://vk.com)
21090 - 87.240.188.206 (VKONTAKTE-SPB-AS http://vk.com)
20992 - 87.240.188.219 (VKONTAKTE-SPB-AS http://vk.com)
20780 - 87.240.188.197 (VKONTAKTE-SPB-AS http://vk.com)
20435 - 87.240.188.149 (VKONTAKTE-SPB-AS http://vk.com)


Info:
lastPending: false
lastDataDate: 2019-04-28T22:40:38.381Z
lastLineDate: 2019-04-28T22:40:38.396Z
lastPushDate: 2019-04-28T22:40:38.397Z

cpErrors: 
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes


buffer: 01:41:14.668140 IP 149.154.167.

ipError: []
lastIp: { announced: true,
  as_country_code: 'US',
  as_description: 'DIGITALOCEAN-ASN - DigitalOcean, LLC',
  as_number: 14061,
  first_ip: '162.243.128.0',
  ip: '162.243.151.204',
  last_ip: '162.243.159.255' }

Last log: { id: '149.154.167.220', time: 1556491238397, count: 1448 }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment