Skip to content

Commit

Permalink
add cli
Browse files Browse the repository at this point in the history
  • Loading branch information
cnwhy committed Mar 12, 2019
1 parent 91b2a1e commit 4e175de
Show file tree
Hide file tree
Showing 17 changed files with 779 additions and 255 deletions.
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ lib-cov
*.out
*.pid
*.gz

*.tgz
data/*.tmp
pids
logs
results

npm-debug.log
12 changes: 11 additions & 1 deletion History.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
1.3.0 / 2019-03-12
==================

* 修复在普通模式下,某些情况会丢失文件描述符的问题.
* 新增返回流API `qqwry.searchIPScopeStream`
* 新增 cli 命令模式
* `qqwry search <ip> [ips...]` 查询IP/IP段
* `qqwry find <keyword> [keyword...]` 反查IP段
* `qqwry update [dataPath]` 从纯真官网更新IP库文件

1.2.0 / 2019-02-22
==================

* 封装修改构建函数, 返回一个封装好的函数
* 弱化API 支持 新的对像支持 qqwry(ip), qqwry(ipBgin,ipEnd), qqwry(ipBgin,ipEnd,callback)
* 弱化API 支持 新的对像支持 `qqwry(ip)`, `qqwry(ipBgin,ipEnd)`, `qqwry(ipBgin,ipEnd,callback)`
* 隐藏不必要的属性;
* 绑定API函数上下文, 方便ES6解构赋值方式
* 更新IP库2019年2月20日
Expand Down
83 changes: 52 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
# lib-qqwry

`lib-qqwry`是一个高效查询纯真IP库(qqwry.dat)的模块;
`lib-qqwry`是一个高效纯真IP库(qqwry.dat)引擎;

### 实现的功能
1. 通过IP地址或有效的IP数值,搜索IP地址对应的地理位置信息。
2. 搜索一个IP段的地理位置信息。
3. IP地址与数值的互转。

### 安装
## 安装
```
npm install lib-qqwry
npm i lib-qqwry
```

### 使用
## 使用

```js
### cli (v1.3.0+)
从1.3版本开始支持命令模式, 你可以用把`lib-qqwry`安装到全局来使用;
![search](https://cnwhy.github.io/gh-res/images/qqwry-cli-search.svg)

* `qqwry search <ip> [ips...]` 查询IP/IP段
* `qqwry find <keyword> [keyword...]` 反查IP段
* `qqwry update [dataPath]` 从纯真官网更新IP库文件

### node

```js
var libqqwry = require('lib-qqwry');
var qqwry = libqqwry() //初始化IP库解析器
.qqwry.speed(); //启用急速模式 比不开启效率率快非常多 但多占10M左右内存;
qqwry.speed(); //启用急速模式;

var ip1 = qqwry.searchIP("202.103.102.10"); //查询IP信息
var ips = qqwry.searchIPScope("0.0.0.0","1.0.0.0"); //查询IP段信息
//异步查询IP段信息
qqwry.searchIPScope("0.0.0.0","1.0.0.0",function(err,iparr){
console.log(iparr);
});
//查询IP段信息,结果以可读流返回
var ipStream = qqwry.searchIPScopeStream('0.0.0.0','1.0.0.0',{format:'json'});
// s.pipe(fs.readFileSync(outFile))
ipStream.pipe(process.stdout)
```

## API
Expand Down Expand Up @@ -61,8 +71,9 @@ var qqwry = libqqwry(true);

## 解析器对像 Qqwry
### qqwry.searchIP(IP) 单个IP查询
IP : IP地址/IP数值
IP : IP地址/IP数值
反回一个JSON对像;
> 便捷调用: `qqwry(IP)` v1.2.0+
```
> qqwry.searchIP("255.255.255.255");
{ int: 4294967295,
Expand All @@ -71,12 +82,13 @@ IP : IP地址/IP数值
Area: '2017年1月5日IP数据' }
```

### qqwry.searchIPScope(beginIP,endIP,callback) IP段查询
### qqwry.searchIPScope(beginIP,endIP,[callback]) IP段查询
beginIP : 启始IP
endIP : 结束IP
callback: 回调函数,不传则为同步查询 function(err,arrdata){};
callback: function(err,arrdata){} 没有回调则使用同步查询;
> 便捷调用: `qqwry(beginIP,endIP,callback)` v1.2.0+
```
> qqwry.searchIPScope("8.8.8.0","8.8.8.8");
> qqwry("8.8.8.0","8.8.8.8");
[ { begInt: 134744064,
endInt: 134744071,
begIP: '8.8.8.0',
Expand All @@ -91,20 +103,29 @@ callback: 回调函数,不传则为同步查询 function(err,arrdata){};
Area: '加利福尼亚州圣克拉拉县山景市谷歌公司DNS服务器' } ]
```

### qqwry.searchIPScopeStream(beginIP,endIP,options) 流形式反回IP段结果 v1.3.0+
`beginIP` : <string|int> // 启始IP
`endIP` : <string|int> // 结束IP
`options.format` : <string> //输出格式, 支持 'text' , 'csv', 'json', 'object'
`options.outHeader`: <boolean> //为`true`时 'csv' 会输出表头 , 'json' 会以对像数组形式输出(参考`searchIPScope`方法); 默认 `false`

> 流模式适合查询结果数据量较大的情况使用
> format说明: 'csv' , 'json' 格式适合直接输出到文件, 'object' 将返回对像流, 适合程序二次处理数据
```shell
> qqwry.searchIPScopeStream("8.8.8.0","8.8.8.8").pipe(process.stdout);
8.8.8.0 - 8.8.8.7 美国 加利福尼亚州圣克拉拉县山景市谷歌公司
8.8.8.8 - 8.8.8.8 美国 加利福尼亚州圣克拉拉县山景市谷歌公司DNS服务器

> qqwry.searchIPScopeStream("8.8.8.0","8.8.8.8",{format:'csv'}).pipe(process.stdout);
134744064,134744071,8.8.8.0,8.8.8.7,美国,加利福尼亚州圣克拉拉县山景市谷歌公司
134744072,134744072,8.8.8.8,8.8.8.8,美国,加利福尼亚州圣克拉拉县山景市谷歌公司DNS服务器

> qqwry.searchIPScopeStream("8.8.8.0","8.8.8.8",{format:'json'}).pipe(process.stdout);
[[134744064,134744071,"8.8.8.0","8.8.8.7","美国","加利福尼亚州圣克拉拉县山景市谷歌公司"],[134744072,134744072,"8.8.8.8","8.8.8.8","美国","加利福尼亚州圣克拉拉县山景市谷歌公司DNS服务器"]]
```

### qqwry.speed() 启用急速模式
急速模式实质为将IP库文件读入内存中以提升效率, 是普通模式特别是HDD硬盘环境快50倍以上.

### qqwry.unSpeed() 停用急速模式

## 文档说明
1. ./data/qqwry.dat 默认IP库,可用最新IP库替换; 下载地址[www.cz88.net](http://www.cz88.net/)
2. ./lib/qqwry.js 解析IP库的主文件;
3. ./lib/gbk.js GBK编码表文件,从[iconv-lite](https://github.com/ashtuchkin/iconv-lite)中找出来的,并增加了一个转码方法;
4. ./test/demo.js 使用演示;
5. ./test/test_v.js 效率测试示例;

### 效率测试文件 test_v.js
`node test_v.js 255.255.255.255` 正常工作检查
`node test_v.js -1` 单个查询效率测试
`node test_v.js -2` 10次IP段查询效率测试
`node test_v.js -3` 10次IP段异步查询效率测试
急速模式实质为将IP库文件读入内存中以提升效率.

### qqwry.unSpeed() 停用急速模式
90 changes: 90 additions & 0 deletions bin/lib/qqwryUpdate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
require('stream.pipeline-shim/auto');
const zlib = require('zlib');
const util = require('util');
const fs = require('fs');
const stream = require('stream');
const axios = require('axios');
const GBK = require('gbk.js');
const pipeline = util.promisify(stream.pipeline);
const ProgressBar = require('progress');

const urls = {
copywrite: 'http://update.cz88.net/ip/copywrite.rar',
qqwry: 'http://update.cz88.net/ip/qqwry.rar'
};

class QqwryDecode extends stream.Transform {
constructor(key, options) {
super(options);
this._key = key;
this._writeN = 0x200;
}
_transform(chunk, encoding, callback) {
if (this._writeN <= 0) return callback(null, chunk);
let max = this._writeN > chunk.length ? chunk.length : this._writeN;
let key = this._key;
this._writeN -= max;
for (let i = 0; i < max; i++) {
key *= 0x805;
key++;
key &= 0xff;
chunk[i] = chunk[i] ^ key;
}
this._key = key;
return callback(null, chunk);
}
_flush(cb) {
cb();
}
}

// async function getURLFile(url, resType = 'arraybuffer') {
async function getURLFile(url, showProgressBar = false) {
return axios.get(url, { responseType: 'stream' }).then(res => {
if(showProgressBar){
let bar = new ProgressBar('downloading [:bar]:percent :rate/bps :etas', {
complete: '=',
incomplete: ' ',
width: 20,
total: +res.headers['content-length']
});
res.data.on('data',function(buffer){
bar.tick(buffer.length);
})
}
return res.data
});
}

let get_copywrite = showBar => getURLFile(urls.copywrite, showBar);
let get_qqwry = showBar => getURLFile(urls.qqwry, showBar);

async function getLastInfo() {
let copywrite = await get_copywrite();
copywrite.read(20);
let key = copywrite.read(4).readUIntLE(0, 4);
let version = GBK.decode(copywrite.read(128)).replace(/\0/g, '');
return {
version,
key
};
}

async function update(dataPath, key) {
if (!key) {
key = (await getLastInfo()).key;
}
let _temPath = dataPath + ".tmp"
return pipeline(
await get_qqwry(true),
new QqwryDecode(key),
zlib.createInflate(),
fs.createWriteStream(_temPath)
).then(()=>{
fs.renameSync(_temPath,dataPath);
});
}

exports.getLastInfo = getLastInfo;
exports.update = update;
exports.QqwryDecode = QqwryDecode;
43 changes: 43 additions & 0 deletions bin/qqwry
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env node

const program = require('commander');
const chalk = require('chalk');
const qqwry = require('../');
const child_process = require('child_process')
const path = require('path')

program
.usage('<command> [options]')
.option('-v, --version', '当前版本信息')
.option('--remote', '获取最新IP库信息')
.command('search', '默认指令 搜索IP/IP段', { isDefault: true })
.command('find', '查找区域信息')
.command('update', '更新IP库(qqwry.dat)')
// .command('remote', '获取最新IP库信息').alias('last');

//版本
program.on('option:version', function() {
try {
let v_lib = require('../package').version;
let v_data = qqwry().searchIP('255.255.255.255').Area;
console.log(`v${v_lib}`, `(Local data: ${chalk.bold(v_data)})`);
} catch (err) {
console.error(err.message || err);
}
process.exit(0);
});
program.on('option:remote',function(){
try{
var log = child_process.execFileSync(process.argv[0], [path.join(__dirname , 'qqwry-remote')]);
process.stdout.write(log);
}catch(err){
console.error(err.message || err);
}
process.exit(0);
})

if (process.argv.length < 3) {
// program.help();
}

program.parse(process.argv);
82 changes: 82 additions & 0 deletions bin/qqwry-find
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env node

const program = require('commander');
const toRegex = require('to-regex');
const qqwry = require('..');
const getFomatFn = require('../lib/format')

program
.name('find')
.usage('<keyword> [keywords...]')
.description('搜索区域信息')
.option('-c, --count', '只是统计记录数')
// .option('-f, --format <value>', '让IP段模式输出的特定格式 支持 "json" or "csv"', /^(json|csv)$/)
.option('-i, --ignore-case', '不区分大小写模式')
.option('-E, --extended-regexp', '启用正则表达式查询')
.parse(process.argv);

if (program.args.length < 1) {
program.help();
} else if (program.args.length) {
try {
let lib = qqwry();
let { ignoreCase, count, format, extendedRegexp } = program.opts();
let keyword = program.args;
let stdout = process.stdout;
lib.speed();
let sum = 0;
let formatFn = getFomatFn('text');
let isMatch = (function(){
let reg;
if(extendedRegexp){
reg = toRegex(keyword,{contains: true, nocase: ignoreCase});
}else{
// reg = mm.makeRe(keyword[0],{matchBase: false,nocase: ignoreCase})
// reg = new RegExp(keyword[0], ignoreCase ? 'i' : '');
let _isMatch;
if(ignoreCase){
keyword = keyword.map(v=>v.toLocaleLowerCase());
_isMatch = function(str,key){
return ~str.toLocaleLowerCase().indexOf(key);
}
}else{
_isMatch = function(str,key){
return str.indexOf(key) != -1;
}
}
return (str)=>{
for(let key of keyword){
if(_isMatch(str,key)) return true;
}
}
}
return (str)=>{
return reg.test(str);
}
}());
// mm.isMatch

lib.searchIPScopeStream(0, '255.255.255.255', { format: 'object' })
.on('data', function(obj) {
// let reg = new RegExp(extendedRegexp?keyword:keyword.repeat(/[\.\{\*]/g,), ignoreCase ? 'i' : '');
// for(let key of keys){
// if(~obj[4].indexOf(key) || ~obj[5].indexOf(key)){
// if (isMatch(obj.slice(4).join(' '))) {
if (isMatch(obj[4]) || isMatch(obj[5])) {
sum++;
// count || stdout.write(obj.slice(2, 4).join(' - ') + ' ' + obj.slice(4).join(' ') + '\n');
if(!count){

}stdout.write(formatFn(obj));
return;
}
// }
// stdout.write('')
})
.on('end', function() {
count && console.log(sum);
});
} catch (err) {
console.error(err.message || err);
}
}
19 changes: 19 additions & 0 deletions bin/qqwry-remote
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env node

const program = require('commander');
const qqwryUpdate = require('./lib/qqwryUpdate');

async function main(){
let {version} = await qqwryUpdate.getLastInfo();
let info = /\d+年\d+月\d+日/.exec(version);
if(info){
console.log('Remote data:',info[0]+'数据');
}
};

program
.name('remote')
.description('获取服务端 IP库信息')
.parse(process.argv);

main().catch(console.error);
Loading

0 comments on commit 4e175de

Please sign in to comment.