Last active
March 29, 2017 11:51
-
-
Save leizongmin/76513e7f6307b3ce012294409925b210 to your computer and use it in GitHub Desktop.
Node.js实现的端口扫描器 scan-port
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "scan-port", | |
"version": "1.0.0", | |
"description": "端口扫描器", | |
"main": "scan.js", | |
"dependencies": { | |
"cli-color": "^1.2.0", | |
"progress": "^1.1.8", | |
"yargs": "^7.0.2" | |
}, | |
"devDependencies": {}, | |
"scripts": { | |
"test": "echo \"Error: no test specified\" && exit 1" | |
}, | |
"keywords": [ | |
"scan", | |
"port", | |
"hack", | |
"tool" | |
], | |
"author": "Zongmin Lei <leizongmin@gmail.com>", | |
"license": "MIT" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 端口扫描器 | |
* | |
* @author Zongmin Lei <leizongmin@gmail.com> | |
*/ | |
const net = require('net'); | |
const dns = require('dns'); | |
const assert = require('assert'); | |
const argv = require('yargs').argv; | |
const ProgressBar = require('progress'); | |
const clc = require('cli-color'); | |
// IP地址正则表达式 | |
const REGEXP_IP = /((?:(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d))))/; | |
// 端口范围正则表达式 | |
const REGEXP_PORT_RANGE = /^(\d+)\-(\d+)$/; | |
/** | |
* 判断是否为IP地址 | |
* | |
* @param {String} host | |
* @return {Boolean} | |
*/ | |
function isIPAddress(host) { | |
return REGEXP_IP.test(host); | |
} | |
/** | |
* 解析域名 | |
* | |
* @param {String} domain | |
* @return {String} | |
*/ | |
function resolveDNS(domain) { | |
return new Promise((resolve, reject) => { | |
dns.resolve4(domain, (err, addresses) => { | |
if (err) { | |
return reject(err); | |
} | |
resolve(addresses[0]); | |
}); | |
}); | |
} | |
/** | |
* 检查端口状态 | |
* | |
* @param {String} host | |
* @param {Number} port | |
* @return {Boolean} | |
*/ | |
function checkPortStatus(host, port) { | |
return new Promise((resolve, reject) => { | |
port = Number(port); | |
assert(isIPAddress(host), `invalid IP address: ${ host }`); | |
assert(port >= 0 && port < 65536, `invalid port: ${ port }`); | |
// 尝试连接指定端口,如果连接成功则表示端口开放,并立即销毁连接 | |
const c = net.connect(port, host); | |
let status = false; | |
c.on('connect', () => { | |
status = true; | |
c.destroy() | |
}); | |
c.on('error', err => { | |
status = false; | |
c.destroy(); | |
}); | |
c.on('close', () => { | |
c.destroy(); | |
resolve(status); | |
}); | |
}); | |
} | |
/** | |
* 检查端口范围 | |
* | |
* @param {String} host | |
* @param {Number} portStart | |
* @param {Number} portEnd | |
*/ | |
async function checkPortRange(host, portStart, portEnd) { | |
console.log('IP: %s', host); | |
console.log('Port: %s to %s', portStart, portEnd); | |
// 创建进度条 | |
const progressBar = new ProgressBar(':percent | elapsed :elapseds | estimated :etas [:bar]', { | |
total: portEnd - portStart, | |
clear: true, | |
}); | |
const foundPorts = []; | |
for (let port = portStart; port <= portEnd; port++) { | |
progressBar.tick(); | |
const status = await checkPortStatus(host, port); | |
if (status) { | |
// 为了让界面显示更完美,需要清除终端最后两行的内容 | |
process.stdout.write(clc.erase.line); | |
process.stdout.write(clc.move.up(1)); | |
console.log(' - %s', clc.green(port)); | |
foundPorts.push(port); | |
} | |
} | |
console.log('Total find %s port(s), Done.', foundPorts.length); | |
return foundPorts; | |
} | |
/** | |
* 解析端口范围 | |
* | |
* @param {String} str | |
* @return {Array} | |
*/ | |
function parseRange(str) { | |
const r = str.match(REGEXP_PORT_RANGE); | |
if (!r) { | |
throw new TypeError(`parse port range failed: ${ str }`); | |
} | |
return [ Number(r[1]), Number(r[2]) ]; | |
} | |
/** | |
* 打印帮助信息 | |
*/ | |
function printHelp() { | |
console.log(` | |
Usage: | |
scan <host> <portStart-portEnd> | |
Example: | |
scan 127.0.0.1 1-65525 | |
scan example.com 1000-10000 | |
`); | |
} | |
/** | |
* 主函数 | |
*/ | |
async function main() { | |
// 获取命令参数 | |
let host = argv._[0]; | |
const range = parseRange(argv._[1] || '1-65535'); | |
// 打印帮助信息 | |
if (argv.help || !host) { | |
printHelp(); | |
return; | |
} | |
// 如果是域名则先将其解析成IP地址 | |
if (!isIPAddress(host)) { | |
host = await resolveDNS(host); | |
} | |
// 开始扫描 | |
await checkPortRange(host, range[0], range[1]); | |
} | |
main() | |
.then(() => process.exit()) | |
.catch(err => { | |
console.error(err); | |
process.exit(1); | |
}); |
Author
leizongmin
commented
Mar 20, 2017
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment