UNPKG

200

Version:

Ip location query.

285 lines (237 loc) 6.51 kB
var fs = require('fs'); var join = require('path').join; var iconv = require('iconv-lite'); var debug = require('debug')('ip'); function IpUtil(ipFile, encoding) { this.ipFile = joinDirectory(process.cwd(), ipFile); this.starts = []; if (encoding && encoding.toLowerCase().indexOf('utf') > -1) { this.filter = function(buf) { return buf.toString(); }; } else { this.filter = function(buf) { return iconv.decode(new Buffer(buf), 'gbk'); }; } this.init(); } IpUtil.prototype.init = function() { debug('begin parse ipfile %s', this.ipFile); if (!fs.existsSync(this.ipFile)) { debug('not found ip file!'); return; } var ipMap = this.ipMap = {}; var getLine = readLine(this.ipFile, this.filter); var result = getLine.next(); var line; var lineNum = 0; while(!result.done) { lineNum++; line = result.value; if (!line || !line.trim()) { continue; } var tokens = line.split(',', 6); if (tokens.length !== 6) { debug('第%d行格式不正确: %s', lineNum, line); result = getLine.next(); continue; } var startIp = ip2Long(tokens[0]); var endIp = ip2Long(tokens[1]); if (!startIp || !endIp) { debug('第%d行格式不正确: %s', lineNum, line); result = getLine.next(); continue; } var country = getValue(tokens[2]); var province = getValue(tokens[3]); var city = getValue(tokens[4]); var address = getValue(tokens[5]); // 针对国家、省份、城市解析的统一判空修改 // 首先对特殊值的解析 if ('IANA' === country) { country = 'IANA'; province = 'IANA'; city = 'IANA'; } if ('局域网' === country) { country = '局域网'; province = '局域网'; city = '局域网'; } if('国外' === country) { country = '国外'; province = '国外'; city = '国外'; } if('中国' === country && ('中国' === province || '中国' === city)) { country = '中国'; province = '中国'; city = '中国'; } ipMap[startIp] = { startIp: startIp, endIp: endIp, country: country, province: province, city: city, address: address }; this.starts.push(startIp); result = getLine.next(); } //debug(this.ipMap) debug('完成IP库的载入. 共载入 %d 条IP纪录', this.starts.length); this.starts.sort(function(a, b) { return a - b; }); debug('ip 索引排序完成.'); }; function getValue(val) { if (!val) { return null; } val = val.trim(); if (val === 'null') { return null; } return val; } IpUtil.prototype.getIpInfo = function(ip) { if (!isIp(ip)) { return null; } if (typeof ip === 'string') { ip = ip2Long(ip); } var ipStart = this.locatStartIP(ip); debug('开始获取 ip 信息: %d', ipStart); var ipInfo = this.ipMap[ipStart]; debug('查找IP, %s 成功.', long2IP(ip)); if (ipInfo.endIp < ip) { debug('在IP库中找不到IP[%s]', long2IP(ip)); return null; } return ipInfo; }; IpUtil.prototype.refreshData = function() { }; /** * 查找ip对应的开始IP地址。如果IP库中正好有以该ip开始的IP信息,那么就是返回这个ip。 * 如果没有,则应该是比这个ip小的最大的start * @param ip * @return */ IpUtil.prototype.locatStartIP = function(ip) { debug('开始查找IP: %d', ip); var centerIP = 0; var centerIndex = 0; // 当前指针位置 var startIndex = 0; // 起始位置 var endIndex = this.starts.length - 1; // 结束位置 var count = 0; // 循环次数 while (true) { debug('%d. start = %d, end = %d', count++, startIndex, endIndex); // 中间位置 centerIndex = Math.floor((startIndex + endIndex) / 2); centerIP = this.starts[centerIndex]; if (centerIP < ip) { // 如果中间位置的IP小于要查询的IP,那么下一次查找后半段 startIndex = centerIndex; } else if (centerIP > ip) { // 如果中间位置的IP大于要查询的IP,那么下一次查找前半段 endIndex = centerIndex; } else { // 如果相等,那么已经找到要查询的IP break; } if (startIndex + 1 === endIndex) { // 如果开始指针和结束指针相差只有1,那么说明IP库中没有正好以该ip开始的IP信息 // 只能返回IP信息的start ip比这个ip小的最大的那条IP信息的start ip if (centerIP > ip) { centerIP = this.starts[centerIndex - 1]; } break; } } debug('对应的IP开始地址为: %d', centerIP, centerIndex); return centerIP; }; /** * a,b,c ==> a/b/c * a,b,/tmp ==> /tmp * /a/b, c ==> /a/b/c */ function joinDirectory() { var dirs = [].slice.call(arguments, 1); var dir; for (var i = 0, len = dirs.length; i < len; i++) { dir = dirs[i]; if (/^\//.test(dir)) { // 发现根目录, 直接返回. return dir; } } return join.apply(null, [].slice.call(arguments)); } function ip2Long(ip) { if (!isIp(ip)) { return 0; } var segs = ip.split('.'); var iplong =(parseInt(segs[0]) << 24 | parseInt(segs[1]) << 16 | parseInt(segs[2]) << 8 | parseInt(segs[3])) >>> 0; return iplong; } var 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])$/; function isIp(str) { if (!str) { return false; } str = str.trim(); return IP_REGEXP.test(str); /** var tokens = str.split('.'); if (tokens.length !== 4) { return false; } for (var i = 0, len = tokens.length; i < len; i++) { if (parseInt(tokens[i]) > 255 || parseInt(tokens[i]) < 0) { return false; } } return true; **/ } function long2IP(ipLong) { var ip = [ipLong >> 24]; ip.push((ipLong & 16711680) >> 16); ip.push((ipLong & 65280) >> 8); ip.push(ipLong & 255); return ip.join('.'); } function *readLine(file, filter) { var buffer = fs.readFileSync(file); var i = 0, len = 0 || buffer.length; debug('load file succ', len); // 换行符. var nl = require('os').EOL.charCodeAt(0); var buf = []; while(i < len) { if (buffer[i] !== nl) { buf.push(buffer[i]); } else { yield filter(new Buffer(buf)); buf = []; } i++; } } module.exports = IpUtil; module.exports.isIP = isIp; module.exports.ip2Long = ip2Long; module.exports.long2Ip = long2IP;