170 lines
6.5 KiB
TypeScript
Raw Normal View History

2022-07-11 17:52:38 +02:00
import * as net from 'net';
2022-07-23 23:33:13 +02:00
import maxmind, { CityResponse, AsnResponse, IspResponse } from 'maxmind';
2022-07-11 17:52:38 +02:00
import nodesApi from '../../../api/explorer/nodes.api';
import config from '../../../config';
import DB from '../../../database';
import logger from '../../../logger';
import { ResultSetHeader } from 'mysql2';
2022-08-29 23:36:18 +02:00
import * as IPCheck from '../../../utils/ipcheck.js';
2022-11-20 15:35:03 +09:00
import { Reader } from 'mmdb-lib';
2022-07-11 17:52:38 +02:00
export async function $lookupNodeLocation(): Promise<void> {
let loggerTimer = new Date().getTime() / 1000;
let progress = 0;
let nodesUpdated = 0;
let geoNamesInserted = 0;
2022-12-01 15:52:06 +01:00
logger.debug(`Running node location updater using Maxmind`, logger.tags.ln);
2022-07-11 17:52:38 +02:00
try {
const nodes = await nodesApi.$getAllNodes();
const lookupCity = await maxmind.open<CityResponse>(config.MAXMIND.GEOLITE2_CITY);
const lookupAsn = await maxmind.open<AsnResponse>(config.MAXMIND.GEOLITE2_ASN);
2022-11-20 15:35:03 +09:00
let lookupIsp: Reader<IspResponse> | null = null;
try {
lookupIsp = await maxmind.open<IspResponse>(config.MAXMIND.GEOIP2_ISP);
} catch (e) { }
2022-07-11 17:52:38 +02:00
for (const node of nodes) {
const sockets: string[] = node.sockets.split(',');
for (const socket of sockets) {
const ip = socket.substring(0, socket.lastIndexOf(':')).replace('[', '').replace(']', '');
const hasClearnet = [4, 6].includes(net.isIP(ip));
2022-07-11 17:52:38 +02:00
if (hasClearnet && ip !== '127.0.1.1' && ip !== '127.0.0.1') {
const city = lookupCity.get(ip);
const asn = lookupAsn.get(ip);
2022-11-20 15:35:03 +09:00
let isp: IspResponse | null = null;
if (lookupIsp) {
isp = lookupIsp.get(ip);
}
2022-07-23 23:33:13 +02:00
let asOverwrite: any | undefined;
2022-08-29 23:36:18 +02:00
if (asn && (IPCheck.match(ip, '170.75.160.0/20') || IPCheck.match(ip, '172.81.176.0/21'))) {
asOverwrite = {
asn: 394745,
name: 'Lunanode',
};
}
else if (asn && (IPCheck.match(ip, '50.7.0.0/16') || IPCheck.match(ip, '66.90.64.0/18'))) {
asOverwrite = {
asn: 30058,
name: 'FDCservers.net',
};
}
else if (asn && asn.autonomous_system_number === 174) {
asOverwrite = {
asn: 174,
name: 'Cogent Communications',
};
2022-08-29 23:36:18 +02:00
}
2022-07-23 23:33:13 +02:00
if (city && (asn || isp)) {
const query = `
UPDATE nodes SET
as_number = ?,
city_id = ?,
country_id = ?,
subdivision_id = ?,
longitude = ?,
latitude = ?,
accuracy_radius = ?
WHERE public_key = ?
`;
2022-07-23 23:33:13 +02:00
const params = [
asOverwrite?.asn ?? isp?.autonomous_system_number ?? asn?.autonomous_system_number,
2022-07-23 23:33:13 +02:00
city.city?.geoname_id,
city.country?.geoname_id,
city.subdivisions ? city.subdivisions[0].geoname_id : null,
city.location?.longitude,
city.location?.latitude,
city.location?.accuracy_radius,
node.public_key
];
let result = await DB.query<ResultSetHeader>(query, params);
if (result[0].changedRows ?? 0 > 0) {
++nodesUpdated;
}
2022-07-11 17:52:38 +02:00
// Store Continent
if (city.continent?.geoname_id) {
result = await DB.query<ResultSetHeader>(
2022-07-11 17:52:38 +02:00
`INSERT IGNORE INTO geo_names (id, type, names) VALUES (?, 'continent', ?)`,
[city.continent?.geoname_id, JSON.stringify(city.continent?.names)]);
if (result[0].changedRows ?? 0 > 0) {
++geoNamesInserted;
}
}
2022-07-11 17:52:38 +02:00
// Store Country
if (city.country?.geoname_id) {
result = await DB.query<ResultSetHeader>(
2022-07-11 17:52:38 +02:00
`INSERT IGNORE INTO geo_names (id, type, names) VALUES (?, 'country', ?)`,
[city.country?.geoname_id, JSON.stringify(city.country?.names)]);
if (result[0].changedRows ?? 0 > 0) {
++geoNamesInserted;
}
}
2022-07-11 17:52:38 +02:00
// Store Country ISO code
if (city.country?.iso_code) {
result = await DB.query<ResultSetHeader>(
`INSERT IGNORE INTO geo_names (id, type, names) VALUES (?, 'country_iso_code', ?)`,
[city.country?.geoname_id, city.country?.iso_code]);
if (result[0].changedRows ?? 0 > 0) {
++geoNamesInserted;
}
}
2022-07-11 17:52:38 +02:00
// Store Division
if (city.subdivisions && city.subdivisions[0]) {
result = await DB.query<ResultSetHeader>(
2022-07-11 17:52:38 +02:00
`INSERT IGNORE INTO geo_names (id, type, names) VALUES (?, 'division', ?)`,
[city.subdivisions[0].geoname_id, JSON.stringify(city.subdivisions[0]?.names)]);
if (result[0].changedRows ?? 0 > 0) {
++geoNamesInserted;
}
2022-07-11 17:52:38 +02:00
}
// Store City
if (city.city?.geoname_id) {
result = await DB.query<ResultSetHeader>(
2022-07-11 17:52:38 +02:00
`INSERT IGNORE INTO geo_names (id, type, names) VALUES (?, 'city', ?)`,
[city.city?.geoname_id, JSON.stringify(city.city?.names)]);
if (result[0].changedRows ?? 0 > 0) {
++geoNamesInserted;
}
2022-07-11 17:52:38 +02:00
}
2022-07-12 12:12:10 +02:00
// Store AS name
2022-07-23 23:33:13 +02:00
if (isp?.autonomous_system_organization ?? asn?.autonomous_system_organization) {
result = await DB.query<ResultSetHeader>(
2022-07-12 12:12:10 +02:00
`INSERT IGNORE INTO geo_names (id, type, names) VALUES (?, 'as_organization', ?)`,
[
asOverwrite?.asn ?? isp?.autonomous_system_number ?? asn?.autonomous_system_number,
JSON.stringify(asOverwrite?.name ?? isp?.isp ?? asn?.autonomous_system_organization)
]);
if (result[0].changedRows ?? 0 > 0) {
++geoNamesInserted;
}
2022-07-12 12:12:10 +02:00
}
2022-07-11 17:52:38 +02:00
}
++progress;
const elapsedSeconds = Math.round((new Date().getTime() / 1000) - loggerTimer);
2022-12-01 15:52:06 +01:00
if (elapsedSeconds > config.LIGHTNING.LOGGER_UPDATE_INTERVAL) {
logger.debug(`Updating node location data ${progress}/${nodes.length}`);
loggerTimer = new Date().getTime() / 1000;
}
2022-07-11 17:52:38 +02:00
}
}
}
if (nodesUpdated > 0) {
2022-12-01 15:52:06 +01:00
logger.debug(`${nodesUpdated} nodes maxmind data updated, ${geoNamesInserted} geo names inserted`, logger.tags.ln);
}
2022-07-11 17:52:38 +02:00
} catch (e) {
logger.err('$lookupNodeLocation() error: ' + (e instanceof Error ? e.message : e));
}
}