node_js_ipqs_db_reader
Version:
A Node JS implementation of the IPQualityScore flat file IP reputation database reader.
334 lines (333 loc) • 13.9 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const FileReader_1 = require("./FileReader");
const Binary = require("./BinaryOption");
const AbuseVelocity_1 = require("./AbuseVelocity");
const ConnectionType_1 = require("./ConnectionType");
const FraudScore_1 = require("./FraudScore");
const Column_1 = require("./Column");
class IPQSRecord {
constructor(file) {
this.isProxy = false;
this.isVPN = false;
this.isTOR = false;
this.isCrawler = false;
this.isBot = false;
this.recentAbuse = false;
this.isBlacklisted = false;
this.isPrivate = false;
this.isMobile = false;
this.hasOpenPorts = false;
this.isHostingProvider = false;
this.activeVPN = false;
this.activeTOR = false;
this.publicAccessPoint = false;
this.connectionType = null;
this.abuseVelocity = null;
this.country = null;
this.city = null;
this.region = null;
this.ISP = null;
this.organization = null;
this.ASN = null;
this.timezone = null;
this.latitude = null;
this.longitude = null;
this.fraudScore = null;
this.columns = [];
this.valid = false;
this.position = 0;
this.literal = "";
this.file_position = 0;
this.previous = {};
this.file = file;
}
fetch(ip) {
return __awaiter(this, void 0, void 0, function* () {
this.position = 0;
this.previous = {};
this.file_position = this.file.treeStart + FileReader_1.default.TREE_PREFIX_BYTES;
this.literal = this.convertIPToLitteral(this.file.IPv6, ip);
return yield this.populateRecord(0);
});
}
populateRecord(iterations) {
return __awaiter(this, void 0, void 0, function* () {
if (iterations > 256) {
throw new Error("Invalid or nonexistent IP address specified for lookup. (EID: 15)");
}
if (this.file.fileHandler === undefined) {
throw new Error("Invalid or nonexistant file pointer. EID 7");
}
this.previous[this.position] = this.file_position;
if (this.literal.length <= this.position) {
throw new Error("Invalid or nonexistent IP address specified for lookup. (EID: 8)");
}
try {
let read = Buffer.alloc(IPQSRecord.TREE_BYTES);
yield this.file.fileHandler.read(read, 0, // offset in the buffer to start reading
IPQSRecord.TREE_BYTES, // number of bytes to read
this.file_position // position in the file to start reading from
);
if (this.literal[this.position] === "0") {
this.file_position = read.readUInt32LE(0);
}
else {
this.file_position = read.readUInt32LE(4);
}
if (this.file.blacklistFile === false) {
if (this.file_position === 0) {
for (let i = 0; i <= this.position; i++) {
if (this.literal[this.position - i] === "1") {
let bf = this.literal.split("");
bf[this.position - i] = "0";
for (let n = this.position - i + 1; n < bf.length; n++) {
bf[n] = "1";
}
this.literal = bf.join("");
this.position = this.position - i;
this.file_position = this.previous[this.position];
break;
}
}
return this.populateRecord(iterations + 1);
}
}
if (this.file_position < this.file.treeEnd) {
if (this.file_position === 0) {
throw new Error("Invalid or nonexistent IP address specified for lookup. (EID: 12)");
}
this.position++;
return this.populateRecord(iterations + 1);
}
if (this.file.fileHandler === undefined) {
throw new Error("Invalid or nonexistant file pointer. EID 7");
}
let raw = Buffer.alloc(this.file.recordBytes);
const { bytesRead } = yield this.file.fileHandler.read(raw, 0, // offset in the buffer to start reading
this.file.recordBytes, // number of bytes to read
this.file_position // position in the file to start reading from
);
if (bytesRead === 0) {
throw new Error("Invalid or nonexistant file pointer. EID 7");
}
return yield this.parseRecord(raw);
}
catch (err) {
throw err;
}
});
}
parseRecord(raw) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
let current_byte = 0;
if (this.file.binaryData === undefined) {
throw new Error("Invalid or nonexistant file. EID 11");
}
if (this.file.binaryData.has(Binary.BinaryData)) {
this.processFirstByte(new Binary.Bitmask(raw[0]));
this.processSecondByte(new Binary.Bitmask(raw[1]));
let third = new Binary.Bitmask(raw[2]);
this.connectionType = new ConnectionType_1.default(third);
this.abuseVelocity = new AbuseVelocity_1.default(third);
current_byte = 3;
}
else {
let first = new Binary.Bitmask(raw[0]);
this.connectionType = new ConnectionType_1.default(first);
this.abuseVelocity = new AbuseVelocity_1.default(first);
current_byte = 1;
}
this.fraudScore = new FraudScore_1.default();
for (let i = 0; i < this.file.columns.length; i++) {
let c = this.file.columns[i];
if (c === undefined) {
throw new Error("Invalid or nonexistent IP address specified for lookup. (EID: 12)");
}
let value = "";
switch (c.name) {
case "ASN":
this.ASN = raw.readUInt32LE(current_byte);
value = (_a = this.ASN) === null || _a === void 0 ? void 0 : _a.toString();
current_byte += 4;
break;
case "Latitude":
this.latitude = raw.readFloatLE(current_byte);
value = this.latitude.toString();
current_byte += 4;
break;
case "Longitude":
this.longitude = raw.readFloatLE(current_byte);
value = this.longitude.toString();
current_byte += 4;
break;
case "ZeroFraudScore":
this.fraudScore.setFraudScore(0, raw.readUInt8(current_byte));
value = this.fraudScore.getFraudScore(0).toString();
current_byte++;
break;
case "OneFraudScore":
this.fraudScore.setFraudScore(1, raw.readUInt8(current_byte));
value = this.fraudScore.getFraudScore(1).toString();
current_byte++;
break;
default:
if (c.type.has(Binary.StringData)) {
const pos = raw.readUInt32LE(current_byte);
if (!this.file.fileHandler) {
throw new Error("Invalid or nonexistent file pointer. EID 13");
}
try {
const sb = Buffer.alloc(1);
const { bytesRead: bytesRead1 } = yield this.file.fileHandler.read(sb, 0, 1, pos);
if (bytesRead1 !== 1) {
throw new Error("Failed to read string size byte. EID 13a");
}
const size = sb.readUInt8(0);
if (size === 0) {
throw new Error("Invalid or nonexistent file pointer. EID 14");
}
const vb = Buffer.alloc(size);
const { bytesRead: bytesRead2 } = yield this.file.fileHandler.read(vb, 0, size, pos + 1);
if (bytesRead2 !== size) {
throw new Error("Failed to read string value. EID 13b");
}
value = vb.toString();
current_byte += 4;
}
catch (err) {
throw new Error(`File read error: ${err.message}`);
}
}
break;
}
this.columns.push(new Column_1.default(c.name, c.type, value));
switch (c.name) {
case "Country":
this.country = value;
break;
case "City":
this.city = value;
break;
case "Region":
this.region = value;
break;
case "ISP":
this.ISP = value;
break;
case "Organization":
this.organization = value;
break;
case "Timezone":
this.timezone = value;
break;
}
}
this.valid = true;
this.position = 0;
this.literal = "";
this.previous = {};
this.file_position = 0;
return;
});
}
processFirstByte(b) {
if (b.has(Binary.IsProxy)) {
this.isProxy = true;
}
if (b.has(Binary.IsVPN)) {
this.isVPN = true;
}
if (b.has(Binary.IsTOR)) {
this.isTOR = true;
}
if (b.has(Binary.IsCrawler)) {
this.isCrawler = true;
}
if (b.has(Binary.IsBot)) {
this.isBot = true;
}
if (b.has(Binary.RecentAbuse)) {
this.recentAbuse = true;
}
if (b.has(Binary.IsBlacklisted)) {
this.isBlacklisted = true;
}
if (b.has(Binary.IsPrivate)) {
this.isPrivate = true;
}
}
processSecondByte(b) {
if (b.has(Binary.IsMobile)) {
this.isMobile = true;
}
if (b.has(Binary.HasOpenPorts)) {
this.hasOpenPorts = true;
}
if (b.has(Binary.IsHostingProvider)) {
this.isHostingProvider = true;
}
if (b.has(Binary.ActiveVPN)) {
this.activeVPN = true;
}
if (b.has(Binary.ActiveTOR)) {
this.activeTOR = true;
}
if (b.has(Binary.PublicAccessPoint)) {
this.publicAccessPoint = true;
}
}
convertIPToLitteral(ipv6, ip) {
let result = "";
if (ipv6) {
let parts = this.expandIPv6(ip).split(":");
for (let i = 0; i < parts.length; i++) {
result = result + parseInt(parts[i], 16).toString(2).padStart(16, "0");
}
}
else {
let parts = ip.split(".");
for (let i = 0; i < parts.length; i++) {
result = result + parseInt(parts[i]).toString(2).padStart(8, "0");
}
}
return result;
}
expandIPv6(ip) {
if (ip.indexOf("::") === -1) {
return ip;
}
let length = 8;
let result = "";
let sides = ip.split("::");
var groupsPresent = 0;
for (var i = 0; i < sides.length; i++) {
groupsPresent += sides[i].split(":").length;
}
let fullAddress = sides[0] + ":";
for (var i = 0; i < length - groupsPresent; i++) {
fullAddress += "0000:";
}
fullAddress += sides[1];
let groups = fullAddress.split(":");
for (var i = 0; i < length; i++) {
while (groups[i].length < 4) {
groups[i] = "0" + groups[i];
}
result += i != length - 1 ? groups[i] + ":" : groups[i];
}
return result;
}
}
exports.default = IPQSRecord;
IPQSRecord.TREE_BYTES = 8;