Skip to content

Instantly share code, notes, and snippets.

@leizongmin
Last active March 29, 2017 11:51
Show Gist options
  • Save leizongmin/76513e7f6307b3ce012294409925b210 to your computer and use it in GitHub Desktop.
Save leizongmin/76513e7f6307b3ce012294409925b210 to your computer and use it in GitHub Desktop.
Node.js实现的端口扫描器 scan-port
{
"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"
}
/**
* 端口扫描器
*
* @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);
});
@leizongmin
Copy link
Author

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment