This commit is contained in:
parent
5a5bb80927
commit
a226bc65dd
37
kit/package-lock.json
generated
37
kit/package-lock.json
generated
@ -10,12 +10,14 @@
|
||||
"dependencies": {
|
||||
"auto-launch": "^5.0.6",
|
||||
"cross-port-killer": "^1.4.0",
|
||||
"diskusage-ng": "^1.0.4",
|
||||
"extract-zip": "^2.0.1",
|
||||
"get-port": "^7.0.0",
|
||||
"json5": "^2.2.3",
|
||||
"log4js": "^6.9.1",
|
||||
"physical-cpu-count": "^2.0.0",
|
||||
"sm-crypto": "^0.3.13",
|
||||
"systeminformation": "^5.25.11",
|
||||
"webdav": "^5.3.1"
|
||||
},
|
||||
"bin": {
|
||||
@ -156,6 +158,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/diskusage-ng": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/diskusage-ng/-/diskusage-ng-1.0.4.tgz",
|
||||
"integrity": "sha512-30qT0Bn2dNGii6dAXllGgYRaIt+7gPhwCJks+byhutkCOCgsqfqvXmAV2zTDOvU1ylnGHUjZMMaKhM95FyQQrg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/end-of-stream": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
@ -528,6 +539,32 @@
|
||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
|
||||
"integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
|
||||
},
|
||||
"node_modules/systeminformation": {
|
||||
"version": "5.30.6",
|
||||
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.30.6.tgz",
|
||||
"integrity": "sha512-LEIyK1aEv5P3BhAPW3swdlIyCihxwEq/Gki+kcONieU4PIeRCSLDuGkk0Va/56PSBgjVgEksOM88dmY6YqOyfQ==",
|
||||
"license": "MIT",
|
||||
"os": [
|
||||
"darwin",
|
||||
"linux",
|
||||
"win32",
|
||||
"freebsd",
|
||||
"openbsd",
|
||||
"netbsd",
|
||||
"sunos",
|
||||
"android"
|
||||
],
|
||||
"bin": {
|
||||
"systeminformation": "lib/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "Buy me a coffee",
|
||||
"url": "https://www.buymeacoffee.com/systeminfo"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
"version": "0.1",
|
||||
"bin": "src/index.js",
|
||||
"scripts": {
|
||||
"test": "bun src/index.js",
|
||||
"linux": "bun build ./src/index.js --compile --bytecode --minify --target=bun-linux-x64-baseline --outfile=./dist/linux/kit",
|
||||
"windows": "bun build ./src/index.js --compile --bytecode --minify --target=bun-windows-x64-baseline --outfile=./dist/windows/kit.exe",
|
||||
"pkg-alpine": "pkg --compress GZip . -t node14-alpine-arm64 -o ./dist/alpine/kit",
|
||||
|
||||
@ -183,12 +183,18 @@ parser.addCmdLine("checkip", "获取自己的公网 ip;", async function (cli) {
|
||||
if( "ok" === ret.ret ) {
|
||||
$logger.info( "ip : " + ret.data.ip )
|
||||
$logger.info( "location : " + ret.data.location.join(" ") )
|
||||
$logger.info("\n" + ret.data.ip)
|
||||
} else {
|
||||
$logger.error("获取失败")
|
||||
}
|
||||
});
|
||||
|
||||
parser.addCmdLine("scan <ip-and-mask> [ports]", "扫描局域网;", async function (cli) {
|
||||
let ipAndMask = cli.getParamValue("ip-and-mask")
|
||||
let ports = cli.getParamValue("ports")
|
||||
let ret = await net.scan(ipAndMask, ports)
|
||||
$logger.info( JSON.stringify(ret, null, 4) )
|
||||
});
|
||||
|
||||
|
||||
parser.addCmdLine("telnet [--timeout] <ip-port> [port]", "验证远程 TCP 端口是否可用;", async function (cli) {
|
||||
let ip = cli.getParamValue("ip-port")
|
||||
|
||||
299
kit/src/net.js
299
kit/src/net.js
@ -2,7 +2,7 @@ require("./init")
|
||||
const osutil = require("./util/osutil")
|
||||
const killer = require('cross-port-killer');
|
||||
const net = require('net');
|
||||
|
||||
const dns = require('dns').promises;
|
||||
|
||||
/**
|
||||
* 模拟 telnet ,检测端口是否开放
|
||||
@ -79,6 +79,299 @@ async function checkip(){
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
killport, getport, telnet, checkip
|
||||
|
||||
/**
|
||||
* 内网IP端口扫描函数
|
||||
* @param {string} ipAndMask - IP地址或IP段,如:"192.168.1.1" 或 "192.168.1.0/24"
|
||||
* @param {string|null} ports - 端口,可以是:null(使用默认端口)、单个端口、多个端口逗号分隔
|
||||
* @returns {Promise<Array>} - 返回扫描结果数组
|
||||
*/
|
||||
async function scan(ipAndMask, ports) {
|
||||
const defaultPorts = [22, 80, 443, 3389, 8889];
|
||||
let targetIps = [];
|
||||
let targetPorts = [];
|
||||
|
||||
// 1. 解析端口参数
|
||||
if (!ports) {
|
||||
targetPorts = defaultPorts;
|
||||
} else if (ports.includes(',')) {
|
||||
targetPorts = ports.split(',').map(p => parseInt(p.trim())).filter(p => p > 0 && p <= 65535);
|
||||
} else {
|
||||
targetPorts = [parseInt(ports)];
|
||||
}
|
||||
|
||||
// 2. 解析IP地址/网段
|
||||
if (ipAndMask.includes('/')) {
|
||||
// CIDR表示法,如 192.168.1.0/24
|
||||
targetIps = await parseCIDR(ipAndMask);
|
||||
} else if (net.isIP(ipAndMask)) {
|
||||
// 单个IP地址
|
||||
targetIps = [ipAndMask];
|
||||
} else {
|
||||
// 可能是主机名,尝试解析
|
||||
try {
|
||||
const addresses = await dns.resolve4(ipAndMask);
|
||||
targetIps = addresses;
|
||||
} catch (error) {
|
||||
throw new Error(`无法解析主机名: ${ipAndMask}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 扫描所有IP和端口
|
||||
const scanPromises = targetIps.map(ip =>
|
||||
scanPortsForIP(ip, targetPorts)
|
||||
);
|
||||
|
||||
const results = await Promise.all(scanPromises);
|
||||
|
||||
// 4. 过滤出有开放端口的结果
|
||||
return results.filter(result => result.openPorts.length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析CIDR格式的IP段
|
||||
* @param {string} cidr - CIDR格式,如 "192.168.1.0/24"
|
||||
* @returns {Array} - IP地址数组
|
||||
*/
|
||||
function parseCIDR(cidr) {
|
||||
const [ipStr, maskStr] = cidr.split('/');
|
||||
const mask = parseInt(maskStr);
|
||||
|
||||
if (mask < 0 || mask > 32) {
|
||||
throw new Error('子网掩码必须在0-32之间');
|
||||
}
|
||||
|
||||
// 将IP转换为32位整数
|
||||
const ipToInt = (ip) => {
|
||||
const parts = ip.split('.').map(Number);
|
||||
return (parts[0] << 24) + (parts[1] << 16) + (parts[2] << 8) + parts[3];
|
||||
};
|
||||
|
||||
// 将32位整数转换为IP字符串
|
||||
const intToIp = (int) => {
|
||||
return [
|
||||
(int >>> 24) & 255,
|
||||
(int >>> 16) & 255,
|
||||
(int >>> 8) & 255,
|
||||
int & 255
|
||||
].join('.');
|
||||
};
|
||||
|
||||
const ipInt = ipToInt(ipStr);
|
||||
const hostBits = 32 - mask;
|
||||
const networkMask = (0xFFFFFFFF << hostBits) >>> 0; // 无符号右移
|
||||
const networkAddress = ipInt & networkMask;
|
||||
const broadcastAddress = networkAddress | ((1 << hostBits) - 1);
|
||||
|
||||
// 排除网络地址和广播地址
|
||||
const start = networkAddress + 1;
|
||||
const end = broadcastAddress - 1;
|
||||
|
||||
const ips = [];
|
||||
for (let i = start; i <= end; i++) {
|
||||
ips.push(intToIp(i));
|
||||
}
|
||||
|
||||
return ips;
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描单个IP的所有指定端口
|
||||
* @param {string} ip - IP地址
|
||||
* @param {Array} ports - 端口数组
|
||||
* @param {number} timeout - 连接超时时间(毫秒)
|
||||
* @returns {Promise<Object>} - 扫描结果
|
||||
*/
|
||||
async function scanPortsForIP(ip, ports, timeout = 2000) {
|
||||
const results = {
|
||||
ip: ip,
|
||||
openPorts: [],
|
||||
scannedPorts: ports.length,
|
||||
startTime: Date.now()
|
||||
};
|
||||
|
||||
// 并行扫描所有端口
|
||||
const portPromises = ports.map(port =>
|
||||
scanPort(ip, port, timeout)
|
||||
);
|
||||
|
||||
const portResults = await Promise.allSettled(portPromises);
|
||||
|
||||
// 收集开放的端口
|
||||
portResults.forEach((result, index) => {
|
||||
if (result.status === 'fulfilled' && result.value.open) {
|
||||
results.openPorts.push({
|
||||
port: ports[index],
|
||||
service: result.value.service
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
results.endTime = Date.now();
|
||||
results.scanDuration = results.endTime - results.startTime;
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描单个端口
|
||||
* @param {string} ip - IP地址
|
||||
* @param {number} port - 端口号
|
||||
* @param {number} timeout - 超时时间
|
||||
* @returns {Promise<Object>} - 端口扫描结果
|
||||
*/
|
||||
function scanPort(ip, port, timeout) {
|
||||
return new Promise((resolve) => {
|
||||
const socket = new net.Socket();
|
||||
const result = {
|
||||
ip: ip,
|
||||
port: port,
|
||||
open: false,
|
||||
service: getServiceName(port)
|
||||
};
|
||||
|
||||
// 设置超时
|
||||
socket.setTimeout(timeout);
|
||||
|
||||
socket.on('connect', () => {
|
||||
result.open = true;
|
||||
socket.destroy();
|
||||
resolve(result);
|
||||
});
|
||||
|
||||
socket.on('timeout', () => {
|
||||
socket.destroy();
|
||||
resolve(result);
|
||||
});
|
||||
|
||||
socket.on('error', () => {
|
||||
socket.destroy();
|
||||
resolve(result);
|
||||
});
|
||||
|
||||
socket.on('close', () => {
|
||||
// 连接已关闭
|
||||
});
|
||||
|
||||
// 开始连接
|
||||
socket.connect(port, ip);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据端口号获取常见服务名称
|
||||
* @param {number} port - 端口号
|
||||
* @returns {string} - 服务名称
|
||||
*/
|
||||
function getServiceName(port) {
|
||||
const commonServices = {
|
||||
20: 'FTP Data',
|
||||
21: 'FTP Control',
|
||||
22: 'SSH',
|
||||
23: 'Telnet',
|
||||
25: 'SMTP',
|
||||
53: 'DNS',
|
||||
67: 'DHCP Server',
|
||||
68: 'DHCP Client',
|
||||
69: 'TFTP',
|
||||
80: 'HTTP',
|
||||
88: 'Kerberos',
|
||||
110: 'POP3',
|
||||
123: 'NTP',
|
||||
135: 'MSRPC',
|
||||
137: 'NetBIOS Name',
|
||||
138: 'NetBIOS Datagram',
|
||||
139: 'NetBIOS Session',
|
||||
143: 'IMAP',
|
||||
161: 'SNMP',
|
||||
162: 'SNMP Trap',
|
||||
389: 'LDAP',
|
||||
443: 'HTTPS',
|
||||
445: 'SMB',
|
||||
465: 'SMTPS',
|
||||
514: 'Syslog',
|
||||
515: 'LPD/LPR',
|
||||
587: 'SMTP Submission',
|
||||
636: 'LDAPS',
|
||||
993: 'IMAPS',
|
||||
995: 'POP3S',
|
||||
1433: 'MSSQL',
|
||||
1521: 'Oracle',
|
||||
1723: 'PPTP',
|
||||
1883: 'MQTT',
|
||||
1900: 'UPnP',
|
||||
2082: 'cPanel',
|
||||
2083: 'cPanel SSL',
|
||||
2086: 'WHM',
|
||||
2087: 'WHM SSL',
|
||||
2095: 'Webmail',
|
||||
2096: 'Webmail SSL',
|
||||
2181: 'ZooKeeper',
|
||||
2375: 'Docker',
|
||||
2376: 'Docker TLS',
|
||||
3000: 'Node.js',
|
||||
3306: 'MySQL',
|
||||
3389: 'RDP',
|
||||
4333: 'mSQL',
|
||||
4443: 'HTTPS Alt',
|
||||
4567: 'Sinatra',
|
||||
4848: 'GlassFish',
|
||||
5432: 'PostgreSQL',
|
||||
5500: 'VNC',
|
||||
5601: 'Kibana',
|
||||
5672: 'RabbitMQ',
|
||||
5900: 'VNC',
|
||||
5984: 'CouchDB',
|
||||
6379: 'Redis',
|
||||
6443: 'Kubernetes API',
|
||||
6666: 'IRC',
|
||||
6667: 'IRC',
|
||||
7000: 'Cassandra',
|
||||
7001: 'WebLogic',
|
||||
7077: 'Spark',
|
||||
7200: 'Cassandra Native',
|
||||
7474: 'Neo4j',
|
||||
7687: 'Neo4j Bolt',
|
||||
7777: 'Game Server',
|
||||
8000: 'HTTP Alt',
|
||||
8001: 'Tor',
|
||||
8080: 'HTTP Proxy',
|
||||
8081: 'HTTP Proxy Alt',
|
||||
8088: 'HTTP Alt',
|
||||
8089: 'HTTP Alt',
|
||||
8090: 'HTTP Alt',
|
||||
8091: 'HTTP Alt',
|
||||
8181: 'HTTP Alt',
|
||||
8443: 'HTTPS',
|
||||
8880: 'HTTP Alt',
|
||||
8888: 'HTTP Alt',
|
||||
8889: 'Sunyard HSM',
|
||||
9000: 'PHP-FPM',
|
||||
9001: 'Tor',
|
||||
9090: 'HTTP Alt',
|
||||
9091: 'HTTP Alt',
|
||||
9100: 'Print Server',
|
||||
9200: 'Elasticsearch',
|
||||
9300: 'Elasticsearch',
|
||||
9418: 'Git',
|
||||
9999: 'HTTP Alt',
|
||||
10000: 'Webmin',
|
||||
11211: 'Memcached',
|
||||
15672: 'RabbitMQ Management',
|
||||
27017: 'MongoDB',
|
||||
27018: 'MongoDB',
|
||||
28017: 'MongoDB HTTP',
|
||||
50000: 'SAP',
|
||||
50030: 'Hadoop JobTracker',
|
||||
50070: 'Hadoop NameNode',
|
||||
61616: 'ActiveMQ',
|
||||
// 添加更多端口...
|
||||
};
|
||||
|
||||
return commonServices[port] || `Unknown (${port})`;
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
killport, getport, telnet, checkip, scan
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user