UNPKG

lib-qqwry-yyyj

Version:

Using NodeJS analytical IP Library (qqwry.dat) module.

419 lines (394 loc) 10.9 kB
/** * 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;