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} */ 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} */ 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} - 返回扫描结果数组 */ 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} - 扫描结果 */ 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} - 端口扫描结果 */ 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 }