lib-qqwry-yyyj
Version:
Using NodeJS analytical IP Library (qqwry.dat) module.
419 lines (394 loc) • 10.9 kB
JavaScript
/**
* lib-qqwry
* @module lib-qqwry
*/
'use strict';
var fs = require('fs');
var path = require('path');
var stream = require('stream');
var GBK_decode = require('gbk.js').decode;
var dataCmd = require('./dataCmd');
var getFormatfn = require('./format');
var fileCmd = dataCmd.fileCmd,
bufferCmd = dataCmd.bufferCmd;
var IP_RECORD_LENGTH = 7,
REDIRECT_MODE_1 = 1,
REDIRECT_MODE_2 = 2,
IP_REGEXP = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/;
var pathDefined = path.join(__dirname, '../data/qqwry.dat'); //IP库默认路径
var dbug = false;
var unArea = '',
unCountry = '';
// console.log(Buffer.alloc);
if (!Buffer.alloc) {
Buffer.alloc = function(a, b, c) {
return new Buffer(a, b, c);
};
}
//封装,方便使用
function pack() {
var self = this;
function IP(ip) {
switch (arguments.length) {
case 0:
return self.searchIP('255.255.255.255');
case 1:
return self.searchIP(ip);
case 2:
default:
return self.searchIPScope.apply(self, arguments);
}
}
IP._lib = self;
Object.keys(QqwryDriver.prototype).forEach(function(key) {
if (typeof self[key] == 'function') {
IP[key] = self[key].bind(self);
}
});
return IP;
}
/**
* ip库查询类
* @class QqwryDriver
* @param {boolean} speed 开启极速模式
* @param {string} dataPath IP库路径
*/
function QqwryDriver(speed, dataPath) {
if (!(this instanceof QqwryDriver)) {
return new QqwryDriver(speed, dataPath);
}
var isspeed;
if (typeof speed == 'string') {
this.dataPath = speed || pathDefined;
isspeed = !!dataPath;
} else {
isspeed = !!speed;
this.dataPath = dataPath || pathDefined;
}
if (isspeed) {
this.speed();
} else {
this.unSpeed();
}
var cmd = this.cmd();
this.ipBegin = cmd.readUIntLE(0, 4);
this.ipEnd = cmd.readUIntLE(4, 4);
cmd.close();
return pack.call(this);
}
/**
* 极速模式
*/
QqwryDriver.prototype.speed = function() {
if (this.cmd && this.cmd.name == 'bufferCmd') return this;
// if (this.cmd) this.cmd.close();
this.cmd = bufferCmd(this.dataPath);
return this;
};
/**
* 关闭极速模式
*/
QqwryDriver.prototype.unSpeed = function() {
if (this.cmd && this.cmd.name == 'fileCmd') return this;
// if (this.cmd) this.cmd.close();
this.cmd = fileCmd(this.dataPath);
return this;
};
/**
* 单IP查询
* @param {number|string} IP IP地址
* @return {object}
*/
QqwryDriver.prototype.searchIP = function(IP) {
var cmd = this.cmd();
var lib = getLib(this, cmd);
var ip = ipToInt(IP),
g = LocateIP.call(lib, ip),
loc = {};
if (g == -1) {
return { int: ip, ip: intToIP(ip), Country: unArea, Area: unCountry };
}
var add = setIPLocation.call(cmd, g);
loc.int = ip;
loc.ip = intToIP(ip);
loc.Country = add.Country;
loc.Area = add.Area;
// closeData.call(this);
cmd.close();
dbug && log(loc);
return loc;
};
/**
* IP段查询
* @param {number|string} bginIP 起始IP
* @param {number|string} endIP 结束IP
* @param {function} [callback] 回调函数,没有回调函数择执行同步查询
* @return {object[]}
*/
QqwryDriver.prototype.searchIPScope = function(bginIP, endIP, callback) {
var self = this;
if (typeof callback === 'function') {
return process.nextTick(function() {
try {
callback(null, self.searchIPScope(bginIP, endIP));
} catch (e) {
callback(e);
}
});
}
var cmd = self.cmd();
var lib = getLib(self, cmd);
// cmd.open();
var _ip1, _ip2, b_g, e_g;
var ips = [];
_ip1 = ipToInt(bginIP);
_ip2 = ipToInt(endIP);
b_g = LocateIP.call(lib, _ip1);
e_g = LocateIP.call(lib, _ip2);
for (var i = b_g; i <= e_g; i += IP_RECORD_LENGTH) {
var loc = {},
add = setIPLocation.call(cmd, i);
loc.begInt = cmd.readUIntLE(i, 4);
loc.endInt = cmd.readUIntLE(cmd.readUIntLE(i + 4, 3), 4);
loc.begIP = intToIP(loc.begInt);
loc.endIP = intToIP(loc.endInt);
loc.Country = add.Country;
loc.Area = add.Area;
ips.push(loc);
}
// closeData.call(this);
cmd.close();
return ips;
};
/**
* IP段查询
* @param {number|string} bginIP 起始IP
* @param {number|string} endIP 结束IP
* @param {object} [options] 输出配制
* @param {string} [options.fromat] 输出数据的格式 支持 'json','csv' 默认 'text'
* @param {Boolean} [options.outHeader] 是否输出字段名 默认 'false'
* @return {stream.Readable}
*/
QqwryDriver.prototype.searchIPScopeStream = function(bginIP, endIP, options) {
options = options || {};
var format = options.format;
var objectMode = format === 'object';
var outHeader = options.outHeader == undefined ? false : !!options.outHeader;
var cmd = this.cmd();
var lib = getLib(this, cmd);
var formatFn = getFormatfn(format);
// cmd.open();
var _ip1, _ip2, b_g, e_g;
var ips = [];
_ip1 = ipToInt(bginIP);
_ip2 = ipToInt(endIP);
b_g = LocateIP.call(lib, _ip1);
e_g = LocateIP.call(lib, _ip2);
var i = b_g;
var read = function(size) {
var self = this;
if (i > e_g) return cmd.close(), self.push(null);
var add = setIPLocation.call(cmd, i),
begInt = cmd.readUIntLE(i, 4),
endInt = cmd.readUIntLE(cmd.readUIntLE(i + 4, 3), 4),
begIP = intToIP(begInt),
endIP = intToIP(endInt),
Country = add.Country,
Area = add.Area;
var outstr = '';
switch (format) {
case 'csv':
if (i == b_g && outHeader) {
outstr += formatFn(['begInt', 'endInt', 'begIP', 'endIP', 'Country', 'Area']);
}
outstr += formatFn([begInt, endInt, begIP, endIP, Country, Area]);
self.push(outstr);
break;
case 'json':
outstr += i == b_g ? '[' : '';
outstr += formatFn(
outHeader
? {
begInt: begInt,
endInt: endInt,
begIP: begIP,
endIP: endIP,
Country: Country,
Area: Area
}
: [begInt, endInt, begIP, endIP, Country, Area]
);
outstr += i == e_g ? ']\n' : ',';
self.push(outstr);
break;
case 'object':
case 'text':
default:
self.push(formatFn([begInt, endInt, begIP, endIP, Country, Area]));
break;
}
i += IP_RECORD_LENGTH;
};
var outss = new stream.Readable({
objectMode: objectMode,
// read: read,
destroy: function(err, callback) {
cmd.close();
callback(err);
}
});
outss._read = read;
return outss;
};
function getLib(consts, cmd) {
return {
ipBegin: consts.ipBegin,
ipEnd: consts.ipEnd,
cmd: cmd
};
}
// 取得begin和end中间的偏移(用于2分法查询);
function GetMiddleOffset(begin, end, recordLength) {
var records = (((end - begin) / recordLength) >> 1) * recordLength + begin;
return records ^ begin ? records : records + recordLength;
}
//2分法查找指定的IP偏移
function LocateIP(ip) {
var g, temp;
for (var b = this.ipBegin, e = this.ipEnd; b < e; ) {
g = GetMiddleOffset(b, e, IP_RECORD_LENGTH); //获取中间位置
temp = this.cmd.readUIntLE(g, 4);
if (ip > temp) {
b = g;
} else if (ip < temp) {
if (g == e) {
g -= IP_RECORD_LENGTH;
break;
}
e = g;
} else {
break;
}
}
if (dbug) {
var begip = this.cmd.readUIntLE(g, 4);
endip = this.cmd.readUIntLE(this.cmd.readUIntLE(g, 3), 4); //获取结束IP的值
log(exports.intToIP(ip) + ' >> ' + ip);
log('>> Indexes as "' + g + '" ( ' + begip + ' --> ' + endip + ' )');
if (ip > endip) {
//与结束IP比较;正常情况不会出现这种情况,除非IP库漏掉了一些IP;
return -1;
}
}
return g;
}
//获取IP地址对应区域
function setIPLocation(g) {
var cmd = this;
var ipwz = cmd.readUIntLE(g + 4, 3) + 4;
var lx = cmd.readUIntLE(ipwz, 1),
loc = {};
if (lx == REDIRECT_MODE_1) {
//Country根据标识再判断
ipwz = cmd.readUIntLE(ipwz + 1, 3); //读取国家偏移`
lx = cmd.readUIntLE(ipwz, 1); //再次获取标识字节
var Gjbut;
if (lx == REDIRECT_MODE_2) {
//再次检查标识字节
Gjbut = cmd.getStringByteArray(cmd.readUIntLE(ipwz + 1, 3));
loc.Country = GBK_decode(Gjbut);
// loc.Country = Gjbut.toString();
ipwz = ipwz + 4;
} else {
Gjbut = cmd.getStringByteArray(ipwz);
loc.Country = GBK_decode(Gjbut);
// loc.Country = Gjbut.toString();
ipwz += Gjbut.length + 1;
}
loc.Area = ReadArea.call(cmd, ipwz);
} else if (lx == REDIRECT_MODE_2) {
//Country直接读取偏移处字符串
var Gjbut = cmd.getStringByteArray(cmd.readUIntLE(ipwz + 1, 3));
loc.Country = GBK_decode(Gjbut);
// loc.Country = Gjbut.toString();
loc.Area = ReadArea.call(cmd, ipwz + 4);
} else {
//Country直接读取 Area根据标志再判断
var Gjbut = cmd.getStringByteArray(ipwz);
ipwz += Gjbut.length + 1;
loc.Country = GBK_decode(Gjbut);
// loc.Country = Gjbut.toString();
loc.Area = ReadArea.call(cmd, ipwz);
}
return loc;
}
//读取Area
function ReadArea(offset) {
var cmd = this;
var one = cmd.readUIntLE(offset, 1);
if (one == REDIRECT_MODE_1 || one == REDIRECT_MODE_2) {
var areaOffset = cmd.readUIntLE(offset + 1, 3);
if (areaOffset == 0) return unArea;
else {
return GBK_decode(cmd.getStringByteArray(areaOffset));
}
} else {
return GBK_decode(cmd.getStringByteArray(offset));
}
}
var ipToInt =
/**
* ip地址转数值
* @param {number|string} IP ip地址
*/
(QqwryDriver.ipToInt = function(IP) {
var result = IP_REGEXP.exec(IP),
ip;
if (result) {
var ip_Arr = result.slice(1);
ip =
((parseInt(ip_Arr[0]) << 24) |
(parseInt(ip_Arr[1]) << 16) |
(parseInt(ip_Arr[2]) << 8) |
parseInt(ip_Arr[3])) >>>
0;
} else if (/^\d+$/.test(IP) && (ip = parseInt(IP)) >= 0 && ip <= 0xffffffff) {
ip = +IP;
} else {
throw 'The IP address is not normal! >> ' + IP;
}
return ip;
});
var intToIP =
/**
* 数值转IP地址
* @param {number} int ip数值
*/
(QqwryDriver.intToIP = function(int) {
if (int < 0 || int > 0xffffffff) {
throw 'The IP number is not normal! >> ' + int;
}
return (
(int >>> 24) +
'.' +
((int >>> 16) & 0xff) +
'.' +
((int >>> 8) & 0xff) +
'.' +
((int >>> 0) & 0xff)
);
});
var ipEndianChange =
/**
* 32位 Big Endian 与 Little Endian 数值互转
* 适用于一些特殊场景 IP数值读取错误时使用
* @param {number} int 32位数值
*/
(QqwryDriver.ipEndianChange = function(int) {
int = int & 0xffffffff;
return ((int >>> 24) | ((int >> 8) & 0xff00) | ((int << 8) & 0xff0000) | (int << 24)) >>> 0;
});
QqwryDriver.init = function() {
return QqwryDriver.apply(null, arguments);
};
module.exports = QqwryDriver;