qqwry.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. /**
  2. * lib-qqwry
  3. * @module lib-qqwry
  4. */
  5. 'use strict';
  6. var fs = require('fs');
  7. var path = require('path');
  8. var stream = require('stream');
  9. var GBK_decode = require('gbk.js').decode;
  10. var dataCmd = require('./dataCmd');
  11. var getFormatfn = require('./format');
  12. var fileCmd = dataCmd.fileCmd,
  13. bufferCmd = dataCmd.bufferCmd;
  14. var IP_RECORD_LENGTH = 7,
  15. REDIRECT_MODE_1 = 1,
  16. REDIRECT_MODE_2 = 2,
  17. 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])$/;
  18. var pathDefined = path.join(__dirname, '../data/qqwry.dat'); //IP库默认路径
  19. var dbug = false;
  20. var unArea = '',
  21. unCountry = '';
  22. // console.log(Buffer.alloc);
  23. if (!Buffer.alloc) {
  24. Buffer.alloc = function(a, b, c) {
  25. return new Buffer(a, b, c);
  26. };
  27. }
  28. //封装,方便使用
  29. function pack() {
  30. var self = this;
  31. function IP(ip) {
  32. switch (arguments.length) {
  33. case 0:
  34. return self.searchIP('255.255.255.255');
  35. case 1:
  36. return self.searchIP(ip);
  37. case 2:
  38. default:
  39. return self.searchIPScope.apply(self, arguments);
  40. }
  41. }
  42. IP._lib = self;
  43. Object.keys(QqwryDriver.prototype).forEach(function(key) {
  44. if (typeof self[key] == 'function') {
  45. IP[key] = self[key].bind(self);
  46. }
  47. });
  48. return IP;
  49. }
  50. /**
  51. * ip库查询类
  52. * @class QqwryDriver
  53. * @param {boolean} speed 开启极速模式
  54. * @param {string} dataPath IP库路径
  55. */
  56. function QqwryDriver(speed, dataPath) {
  57. if (!(this instanceof QqwryDriver)) {
  58. return new QqwryDriver(speed, dataPath);
  59. }
  60. var isspeed;
  61. if (typeof speed == 'string') {
  62. this.dataPath = speed || pathDefined;
  63. isspeed = !!dataPath;
  64. } else {
  65. isspeed = !!speed;
  66. this.dataPath = dataPath || pathDefined;
  67. }
  68. if (isspeed) {
  69. this.speed();
  70. } else {
  71. this.unSpeed();
  72. }
  73. var cmd = this.cmd();
  74. this.ipBegin = cmd.readUIntLE(0, 4);
  75. this.ipEnd = cmd.readUIntLE(4, 4);
  76. cmd.close();
  77. return pack.call(this);
  78. }
  79. /**
  80. * 极速模式
  81. */
  82. QqwryDriver.prototype.speed = function() {
  83. if (this.cmd && this.cmd.name == 'bufferCmd') return this;
  84. // if (this.cmd) this.cmd.close();
  85. this.cmd = bufferCmd(this.dataPath);
  86. return this;
  87. };
  88. /**
  89. * 关闭极速模式
  90. */
  91. QqwryDriver.prototype.unSpeed = function() {
  92. if (this.cmd && this.cmd.name == 'fileCmd') return this;
  93. // if (this.cmd) this.cmd.close();
  94. this.cmd = fileCmd(this.dataPath);
  95. return this;
  96. };
  97. /**
  98. * 单IP查询
  99. * @param {number|string} IP IP地址
  100. * @return {object}
  101. */
  102. QqwryDriver.prototype.searchIP = function(IP) {
  103. var cmd = this.cmd();
  104. var lib = getLib(this, cmd);
  105. var ip = ipToInt(IP),
  106. g = LocateIP.call(lib, ip),
  107. loc = {};
  108. if (g == -1) {
  109. return { int: ip, ip: intToIP(ip), Country: unArea, Area: unCountry };
  110. }
  111. var add = setIPLocation.call(cmd, g);
  112. loc.int = ip;
  113. loc.ip = intToIP(ip);
  114. loc.Country = add.Country;
  115. loc.Area = add.Area;
  116. // closeData.call(this);
  117. cmd.close();
  118. dbug && log(loc);
  119. return loc;
  120. };
  121. /**
  122. * IP段查询
  123. * @param {number|string} bginIP 起始IP
  124. * @param {number|string} endIP 结束IP
  125. * @param {function} [callback] 回调函数,没有回调函数择执行同步查询
  126. * @return {object[]}
  127. */
  128. QqwryDriver.prototype.searchIPScope = function(bginIP, endIP, callback) {
  129. var self = this;
  130. if (typeof callback === 'function') {
  131. return process.nextTick(function() {
  132. try {
  133. callback(null, self.searchIPScope(bginIP, endIP));
  134. } catch (e) {
  135. callback(e);
  136. }
  137. });
  138. }
  139. var cmd = self.cmd();
  140. var lib = getLib(self, cmd);
  141. // cmd.open();
  142. var _ip1, _ip2, b_g, e_g;
  143. var ips = [];
  144. _ip1 = ipToInt(bginIP);
  145. _ip2 = ipToInt(endIP);
  146. b_g = LocateIP.call(lib, _ip1);
  147. e_g = LocateIP.call(lib, _ip2);
  148. for (var i = b_g; i <= e_g; i += IP_RECORD_LENGTH) {
  149. var loc = {},
  150. add = setIPLocation.call(cmd, i);
  151. loc.begInt = cmd.readUIntLE(i, 4);
  152. loc.endInt = cmd.readUIntLE(cmd.readUIntLE(i + 4, 3), 4);
  153. loc.begIP = intToIP(loc.begInt);
  154. loc.endIP = intToIP(loc.endInt);
  155. loc.Country = add.Country;
  156. loc.Area = add.Area;
  157. ips.push(loc);
  158. }
  159. // closeData.call(this);
  160. cmd.close();
  161. return ips;
  162. };
  163. /**
  164. * IP段查询
  165. * @param {number|string} bginIP 起始IP
  166. * @param {number|string} endIP 结束IP
  167. * @param {object} [options] 输出配制
  168. * @param {string} [options.fromat] 输出数据的格式 支持 'json','csv' 默认 'text'
  169. * @param {Boolean} [options.outHeader] 是否输出字段名 默认 'false'
  170. * @return {stream.Readable}
  171. */
  172. QqwryDriver.prototype.searchIPScopeStream = function(bginIP, endIP, options) {
  173. options = options || {};
  174. var format = options.format;
  175. var objectMode = format === 'object';
  176. var outHeader = options.outHeader == undefined ? false : !!options.outHeader;
  177. var cmd = this.cmd();
  178. var lib = getLib(this, cmd);
  179. var formatFn = getFormatfn(format);
  180. // cmd.open();
  181. var _ip1, _ip2, b_g, e_g;
  182. var ips = [];
  183. _ip1 = ipToInt(bginIP);
  184. _ip2 = ipToInt(endIP);
  185. b_g = LocateIP.call(lib, _ip1);
  186. e_g = LocateIP.call(lib, _ip2);
  187. var i = b_g;
  188. var read = function(size) {
  189. var self = this;
  190. if (i > e_g) return cmd.close(), self.push(null);
  191. var add = setIPLocation.call(cmd, i),
  192. begInt = cmd.readUIntLE(i, 4),
  193. endInt = cmd.readUIntLE(cmd.readUIntLE(i + 4, 3), 4),
  194. begIP = intToIP(begInt),
  195. endIP = intToIP(endInt),
  196. Country = add.Country,
  197. Area = add.Area;
  198. var outstr = '';
  199. switch (format) {
  200. case 'csv':
  201. if (i == b_g && outHeader) {
  202. outstr += formatFn(['begInt', 'endInt', 'begIP', 'endIP', 'Country', 'Area']);
  203. }
  204. outstr += formatFn([begInt, endInt, begIP, endIP, Country, Area]);
  205. self.push(outstr);
  206. break;
  207. case 'json':
  208. outstr += i == b_g ? '[' : '';
  209. outstr += formatFn(
  210. outHeader
  211. ? {
  212. begInt: begInt,
  213. endInt: endInt,
  214. begIP: begIP,
  215. endIP: endIP,
  216. Country: Country,
  217. Area: Area
  218. }
  219. : [begInt, endInt, begIP, endIP, Country, Area]
  220. );
  221. outstr += i == e_g ? ']\n' : ',';
  222. self.push(outstr);
  223. break;
  224. case 'object':
  225. case 'text':
  226. default:
  227. self.push(formatFn([begInt, endInt, begIP, endIP, Country, Area]));
  228. break;
  229. }
  230. i += IP_RECORD_LENGTH;
  231. };
  232. var outss = new stream.Readable({
  233. objectMode: objectMode,
  234. // read: read,
  235. destroy: function(err, callback) {
  236. cmd.close();
  237. callback(err);
  238. }
  239. });
  240. outss._read = read;
  241. return outss;
  242. };
  243. function getLib(consts, cmd) {
  244. return {
  245. ipBegin: consts.ipBegin,
  246. ipEnd: consts.ipEnd,
  247. cmd: cmd
  248. };
  249. }
  250. // 取得begin和end中间的偏移(用于2分法查询);
  251. function GetMiddleOffset(begin, end, recordLength) {
  252. var records = (((end - begin) / recordLength) >> 1) * recordLength + begin;
  253. return records ^ begin ? records : records + recordLength;
  254. }
  255. //2分法查找指定的IP偏移
  256. function LocateIP(ip) {
  257. var g, temp;
  258. for (var b = this.ipBegin, e = this.ipEnd; b < e; ) {
  259. g = GetMiddleOffset(b, e, IP_RECORD_LENGTH); //获取中间位置
  260. temp = this.cmd.readUIntLE(g, 4);
  261. if (ip > temp) {
  262. b = g;
  263. } else if (ip < temp) {
  264. if (g == e) {
  265. g -= IP_RECORD_LENGTH;
  266. break;
  267. }
  268. e = g;
  269. } else {
  270. break;
  271. }
  272. }
  273. if (dbug) {
  274. var begip = this.cmd.readUIntLE(g, 4);
  275. endip = this.cmd.readUIntLE(this.cmd.readUIntLE(g, 3), 4); //获取结束IP的值
  276. log(exports.intToIP(ip) + ' >> ' + ip);
  277. log('>> Indexes as "' + g + '" ( ' + begip + ' --> ' + endip + ' )');
  278. if (ip > endip) {
  279. //与结束IP比较;正常情况不会出现这种情况,除非IP库漏掉了一些IP;
  280. return -1;
  281. }
  282. }
  283. return g;
  284. }
  285. //获取IP地址对应区域
  286. function setIPLocation(g) {
  287. var cmd = this;
  288. var ipwz = cmd.readUIntLE(g + 4, 3) + 4;
  289. var lx = cmd.readUIntLE(ipwz, 1),
  290. loc = {};
  291. if (lx == REDIRECT_MODE_1) {
  292. //Country根据标识再判断
  293. ipwz = cmd.readUIntLE(ipwz + 1, 3); //读取国家偏移`
  294. lx = cmd.readUIntLE(ipwz, 1); //再次获取标识字节
  295. var Gjbut;
  296. if (lx == REDIRECT_MODE_2) {
  297. //再次检查标识字节
  298. Gjbut = cmd.getStringByteArray(cmd.readUIntLE(ipwz + 1, 3));
  299. loc.Country = GBK_decode(Gjbut);
  300. // loc.Country = Gjbut.toString();
  301. ipwz = ipwz + 4;
  302. } else {
  303. Gjbut = cmd.getStringByteArray(ipwz);
  304. loc.Country = GBK_decode(Gjbut);
  305. // loc.Country = Gjbut.toString();
  306. ipwz += Gjbut.length + 1;
  307. }
  308. loc.Area = ReadArea.call(cmd, ipwz);
  309. } else if (lx == REDIRECT_MODE_2) {
  310. //Country直接读取偏移处字符串
  311. var Gjbut = cmd.getStringByteArray(cmd.readUIntLE(ipwz + 1, 3));
  312. loc.Country = GBK_decode(Gjbut);
  313. // loc.Country = Gjbut.toString();
  314. loc.Area = ReadArea.call(cmd, ipwz + 4);
  315. } else {
  316. //Country直接读取 Area根据标志再判断
  317. var Gjbut = cmd.getStringByteArray(ipwz);
  318. ipwz += Gjbut.length + 1;
  319. loc.Country = GBK_decode(Gjbut);
  320. // loc.Country = Gjbut.toString();
  321. loc.Area = ReadArea.call(cmd, ipwz);
  322. }
  323. return loc;
  324. }
  325. //读取Area
  326. function ReadArea(offset) {
  327. var cmd = this;
  328. var one = cmd.readUIntLE(offset, 1);
  329. if (one == REDIRECT_MODE_1 || one == REDIRECT_MODE_2) {
  330. var areaOffset = cmd.readUIntLE(offset + 1, 3);
  331. if (areaOffset == 0) return unArea;
  332. else {
  333. return GBK_decode(cmd.getStringByteArray(areaOffset));
  334. }
  335. } else {
  336. return GBK_decode(cmd.getStringByteArray(offset));
  337. }
  338. }
  339. var ipToInt =
  340. /**
  341. * ip地址转数值
  342. * @param {number|string} IP ip地址
  343. */
  344. (QqwryDriver.ipToInt = function(IP) {
  345. var result = IP_REGEXP.exec(IP),
  346. ip;
  347. if (result) {
  348. var ip_Arr = result.slice(1);
  349. ip =
  350. ((parseInt(ip_Arr[0]) << 24) |
  351. (parseInt(ip_Arr[1]) << 16) |
  352. (parseInt(ip_Arr[2]) << 8) |
  353. parseInt(ip_Arr[3])) >>>
  354. 0;
  355. } else if (/^\d+$/.test(IP) && (ip = parseInt(IP)) >= 0 && ip <= 0xffffffff) {
  356. ip = +IP;
  357. } else {
  358. throw 'The IP address is not normal! >> ' + IP;
  359. }
  360. return ip;
  361. });
  362. var intToIP =
  363. /**
  364. * 数值转IP地址
  365. * @param {number} int ip数值
  366. */
  367. (QqwryDriver.intToIP = function(int) {
  368. if (int < 0 || int > 0xffffffff) {
  369. throw 'The IP number is not normal! >> ' + int;
  370. }
  371. return (
  372. (int >>> 24) +
  373. '.' +
  374. ((int >>> 16) & 0xff) +
  375. '.' +
  376. ((int >>> 8) & 0xff) +
  377. '.' +
  378. ((int >>> 0) & 0xff)
  379. );
  380. });
  381. var ipEndianChange =
  382. /**
  383. * 32位 Big Endian 与 Little Endian 数值互转
  384. * 适用于一些特殊场景 IP数值读取错误时使用
  385. * @param {number} int 32位数值
  386. */
  387. (QqwryDriver.ipEndianChange = function(int) {
  388. int = int & 0xffffffff;
  389. return ((int >>> 24) | ((int >> 8) & 0xff00) | ((int << 8) & 0xff0000) | (int << 24)) >>> 0;
  390. });
  391. QqwryDriver.init = function() {
  392. return QqwryDriver.apply(null, arguments);
  393. };
  394. module.exports = QqwryDriver;