377 lines
9.9 KiB
JavaScript
377 lines
9.9 KiB
JavaScript
require("./init")
|
|
const osutil = require("./util/osutil")
|
|
const killer = require('cross-port-killer');
|
|
const net = require('net');
|
|
const dns = require('dns').promises;
|
|
|
|
/**
|
|
* 模拟 telnet ,检测端口是否开放
|
|
* @param port
|
|
* @param host
|
|
* @param timeout
|
|
* @returns {Promise<unknown>}
|
|
*/
|
|
async function telnet(port, host, timeout=3000){
|
|
$logger.debug(`port=${port} host=${host} timeout=${timeout}`)
|
|
return new Promise(function (resolve, reject){
|
|
const socket = new net.Socket();
|
|
|
|
socket.setTimeout(timeout, function() {
|
|
socket.end();
|
|
reject("链接超时")
|
|
});
|
|
|
|
socket.connect(port, host);
|
|
|
|
socket.on('connect', function() {
|
|
resolve('连接成功');
|
|
socket.end();
|
|
});
|
|
|
|
socket.on('error', function(err) {
|
|
reject('连接失败');
|
|
});
|
|
})
|
|
|
|
}
|
|
|
|
async function killport(port) {
|
|
return killer.kill( port );
|
|
// let func = osutil.choise([
|
|
// require("./windows/net").killport,
|
|
// require("./linux/net").killport,
|
|
// ])
|
|
// return func && func(port);
|
|
}
|
|
|
|
/**
|
|
* 获取端口信息
|
|
* 因为命令输出缓存问题,有缺陷。
|
|
* @param port
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function getport(port) {
|
|
$logger.info( "暂未实现" );
|
|
}
|
|
|
|
|
|
async function checkip(){
|
|
const url = "https://myip.ipip.net/json"
|
|
const http = url.startsWith("https:") ? require("node:https") : require('node:http');
|
|
return new Promise((resolve, reject) => {
|
|
http.get(url, (res) => {
|
|
let data = '';
|
|
|
|
// A chunk of data has been recieved.
|
|
res.on('data', (chunk) => {
|
|
data += chunk;
|
|
});
|
|
|
|
// The whole response has been received. Resolve the promise with result
|
|
res.on('end', () => {
|
|
resolve(JSON.parse(data));
|
|
});
|
|
|
|
}).on("error", (err) => {
|
|
// In case of any error reject the promise
|
|
reject(err);
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* 内网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
|
|
} |