add Port-Scanner

This commit is contained in:
2025-09-23 19:47:51 +02:00
parent eabd59e945
commit f21da6b888
5 changed files with 249 additions and 3 deletions

View File

@@ -0,0 +1,80 @@
// backend/routes/portScan.js
const express = require('express');
const Sentry = require("@sentry/node");
const pino = require('pino');
const { isValidIp, isPrivateIp, checkPort } = require('../utils');
const logger = pino({ level: process.env.LOG_LEVEL || 'info' });
const router = express.Router();
const COMMON_PORTS_TO_SCAN = [
21, 22, 25, 53, 80, 110, 143, 443, 3306, 3389, 5432, 8080, 8443
];
router.get('/', (req, res) => {
const targetIpRaw = req.query.targetIp;
const targetIp = typeof targetIpRaw === 'string' ? targetIpRaw.trim() : targetIpRaw;
const requestIp = req.ip || req.socket.remoteAddress;
logger.info({ requestIp, targetIp }, 'Port scan stream request received');
if (!isValidIp(targetIp)) {
logger.warn({ requestIp, targetIp }, 'Invalid target IP for port scan');
return res.status(400).json({ success: false, error: 'Invalid target IP address provided.' });
}
if (isPrivateIp(targetIp)) {
logger.warn({ requestIp, targetIp }, 'Attempt to scan private IP blocked');
return res.status(403).json({ success: false, error: 'Operations on private or local IP addresses are not allowed.' });
}
Sentry.configureScope(scope => {
scope.setContext("port_scan_details", { targetIp, requestIp });
});
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.setHeader('X-Accel-Buffering', 'no');
res.flushHeaders();
const sendEvent = (event, data) => {
if (!res.writableEnded) {
res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
}
};
let isConnectionClosed = false;
req.on('close', () => {
logger.info({ requestIp, targetIp }, 'Client disconnected from port scan stream.');
isConnectionClosed = true;
});
(async () => {
try {
sendEvent('info', { message: `Starting scan of ${COMMON_PORTS_TO_SCAN.length} common ports on ${targetIp}...` });
for (const port of COMMON_PORTS_TO_SCAN) {
if (isConnectionClosed) break;
const result = await checkPort(port, targetIp, 2000);
sendEvent('port_status', result);
}
if (!isConnectionClosed) {
logger.info({ requestIp, targetIp }, 'Port scan stream completed successfully.');
sendEvent('end', { message: 'Scan complete.' });
}
} catch (error) {
logger.error({ requestIp, targetIp, error: error.message }, 'Error during port scan stream');
Sentry.captureException(error);
if (!isConnectionClosed) {
sendEvent('error', { error: 'An unexpected error occurred during the scan.' });
}
} finally {
if (!res.writableEnded) {
res.end();
}
}
})();
});
module.exports = router;

View File

@@ -1,3 +1,4 @@
// backend/server.js
// server.js
// Load .env variables FIRST!
require('dotenv').config();
@@ -33,6 +34,7 @@ const lookupRoutes = require('./routes/lookup');
const dnsLookupRoutes = require('./routes/dnsLookup');
const whoisLookupRoutes = require('./routes/whoisLookup');
const versionRoutes = require('./routes/version');
const portScanRoutes = require('./routes/portScan');
// --- Logger Initialisierung ---
const logger = pino({
@@ -94,6 +96,7 @@ app.use('/api/traceroute', generalLimiter);
app.use('/api/lookup', generalLimiter);
app.use('/api/dns-lookup', generalLimiter);
app.use('/api/whois-lookup', generalLimiter);
app.use('/api/port-scan', generalLimiter);
// --- API Routes ---
@@ -105,6 +108,7 @@ app.use('/api/lookup', lookupRoutes);
app.use('/api/dns-lookup', dnsLookupRoutes);
app.use('/api/whois-lookup', whoisLookupRoutes);
app.use('/api/version', versionRoutes);
app.use('/api/port-scan', portScanRoutes);
// --- Sentry Error Handler ---

View File

@@ -1,4 +1,3 @@
// backend/utils.js
const net = require('net'); // Node.js built-in module for IP validation
const { spawn } = require('child_process');
const pino = require('pino'); // Import pino for logging within utils if needed
@@ -256,6 +255,48 @@ function parseTracerouteLine(line) {
return null;
}
/**
* Checks if a specific TCP port is open on a given host.
* @param {number} port - The port to check.
* @param {string} host - The target host IP address.
* @param {number} timeout - Connection timeout in milliseconds.
* @returns {Promise<{port: number, status: 'open'|'closed'|'timeout', service: string}>} A promise that resolves with the port status.
*/
function checkPort(port, host, timeout = 2000) {
// A small map of common ports to their services
const commonPorts = {
21: 'FTP', 22: 'SSH', 23: 'Telnet', 25: 'SMTP', 53: 'DNS', 80: 'HTTP',
110: 'POP3', 143: 'IMAP', 443: 'HTTPS', 445: 'SMB', 993: 'IMAPS',
995: 'POP3S', 1433: 'MSSQL', 1521: 'Oracle', 3306: 'MySQL', 3389: 'RDP',
5432: 'PostgreSQL', 5900: 'VNC', 8080: 'HTTP-Alt', 8443: 'HTTPS-Alt'
};
const service = commonPorts[port] || 'Unknown';
return new Promise((resolve) => {
const socket = new net.Socket();
socket.setTimeout(timeout);
socket.on('connect', () => {
socket.destroy();
resolve({ port, status: 'open', service });
});
socket.on('timeout', () => {
socket.destroy();
resolve({ port, status: 'timeout', service });
});
socket.on('error', (err) => {
socket.destroy();
// 'ECONNREFUSED' is the key for a closed port. Other errors might be network issues.
const status = err.code === 'ECONNREFUSED' ? 'closed' : 'error';
resolve({ port, status, service, error: err.code });
});
socket.connect(port, host);
});
}
module.exports = {
isValidIp,
@@ -265,5 +306,5 @@ module.exports = {
executeCommand,
parsePingOutput,
parseTracerouteLine,
// Note: logger is not exported, assuming it's managed globally or passed where needed
checkPort,
};