net scan
Some checks are pending
TDevOPsCICD / build-image (push) Waiting to run

This commit is contained in:
cheney 2026-01-27 10:13:12 +08:00
parent 5a5bb80927
commit a226bc65dd
4 changed files with 340 additions and 3 deletions

37
kit/package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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")

View File

@ -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
}