Skip to content

Instantly share code, notes, and snippets.

@supersational
Created July 3, 2025 14:35
Show Gist options
  • Save supersational/ccf03454fe2f7e3fc3af1f284e711d81 to your computer and use it in GitHub Desktop.
Save supersational/ccf03454fe2f7e3fc3af1f284e711d81 to your computer and use it in GitHub Desktop.
Localhost server scanner
// to run: deno run --allow-net --allow-run severfinder.ts
import { serve } from "https://deno.land/[email protected]/http/server.ts";
// adjust range or concurrency here
const PORT_START = 1;
const PORT_END = 65535;
const CONCURRENCY = 100;
const SERVE_PORT = 3000;
const TIMEOUT_MS = 1000;
interface PortInfo {
port: number;
process?: string;
pid?: string;
command?: string;
}
async function isHttpAlive(port: number): Promise<boolean> {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
try {
const resp = await fetch(`http://127.0.0.1:${port}`, {
method: "HEAD",
signal: controller.signal,
});
return resp.ok;
} catch {
return false;
} finally {
clearTimeout(timeout);
}
}
async function getPortDetails(port: number): Promise<PortInfo> {
try {
const command = new Deno.Command("lsof", {
args: ["-i", `:${port}`, "-t"],
stdout: "piped",
stderr: "piped",
});
const { code, stdout } = await command.output();
if (code === 0) {
const pids = new TextDecoder().decode(stdout).trim().split('\n').filter(p => p);
if (pids.length > 0) {
// Get process details for the first PID
const pid = pids[0];
const psCommand = new Deno.Command("ps", {
args: ["-p", pid, "-o", "comm=,args="],
stdout: "piped",
stderr: "piped",
});
const { code: psCode, stdout: psStdout } = await psCommand.output();
if (psCode === 0) {
const processInfo = new TextDecoder().decode(psStdout).trim();
const [process, ...args] = processInfo.split(/\s+/);
return {
port,
process: process || "unknown",
pid,
command: args.join(' ') || process
};
}
}
}
} catch (error) {
console.log(`Error getting details for port ${port}:`, error);
}
return { port };
}
function makePortItemHtml(info: PortInfo): string {
const processInfo = info.process
? `<br><small style="color: #666;">PID: ${info.pid} | Process: ${info.process}<br>Command: ${info.command}</small>`
: '<br><small style="color: #999;">No process details available</small>';
return `<li id="port-${info.port}" style="margin-bottom: 15px; padding: 10px; background: #f8fafc; border-left: 3px solid #2563eb; border-radius: 4px;">
<a href="http://localhost:${info.port}" target="_blank" style="font-weight: bold; color: #2563eb;">localhost:${info.port}</a>
${processInfo}
</li>`;
}
function getBaseHtml(): string {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Live localhost ports</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 800px;
margin: 40px auto;
padding: 20px;
line-height: 1.6;
background: #ffffff;
}
h1 { color: #1f2937; }
a { text-decoration: none; }
a:hover { text-decoration: underline; }
button {
padding: 10px 20px;
background: #2563eb;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
}
button:hover:not(:disabled) { background: #1d4ed8; }
button:disabled { background: #ccc; cursor: not-allowed; }
#progress {
margin: 20px 0;
padding: 15px;
background: #eff6ff;
border: 1px solid #bfdbfe;
border-radius: 6px;
}
#results {
margin-top: 20px;
}
ul {
list-style: none;
padding: 0;
}
.status {
color: #059669;
font-weight: 500;
}
.error {
color: #dc2626;
}
</style>
</head>
<body>
<h1>Live HTTP Servers on Localhost</h1>
<div style="margin-bottom: 20px;">
<button onclick="rescan()">Rescan Ports</button>
</div>
<div id="progress" style="display: none;">
<div class="status">🔍 Scanning ports...</div>
<div id="scan-status">Starting scan...</div>
</div>
<div id="results">
<ul id="port-list"></ul>
<div id="summary" style="margin-top: 20px; color: #666;"></div>
</div>
<script>
let portCount = 0;
function updateProgress(message) {
document.getElementById('scan-status').textContent = message;
}
function addPort(portHtml) {
const portList = document.getElementById('port-list');
portList.insertAdjacentHTML('beforeend', portHtml);
portCount++;
updateProgress(\`Found \${portCount} HTTP server\${portCount === 1 ? '' : 's'} so far...\`);
}
function finishScan(message) {
document.getElementById('progress').style.display = 'none';
document.getElementById('summary').innerHTML = message;
document.querySelector('button').disabled = false;
document.querySelector('button').textContent = 'Rescan Ports';
}
async function rescan() {
const button = document.querySelector('button');
const progress = document.getElementById('progress');
const portList = document.getElementById('port-list');
const summary = document.getElementById('summary');
button.disabled = true;
button.textContent = 'Scanning...';
progress.style.display = 'block';
portList.innerHTML = '';
summary.innerHTML = '';
portCount = 0;
updateProgress('Starting scan...');
try {
const response = await fetch('/scan');
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\\n');
for (const line of lines) {
if (line.trim()) {
try {
const data = JSON.parse(line);
if (data.type === 'port') {
addPort(data.html);
} else if (data.type === 'progress') {
updateProgress(data.message);
} else if (data.type === 'complete') {
finishScan(data.message);
}
} catch (e) {
console.log('Parsing chunk:', line);
}
}
}
}
} catch (error) {
console.error('Scan failed:', error);
finishScan('<span class="error">❌ Scan failed. Please try again.</span>');
}
}
// Auto-scan on page load
rescan();
</script>
</body>
</html>`;
}
async function streamingScan(controller: ReadableStreamDefaultController<Uint8Array>) {
const encoder = new TextEncoder();
let foundPorts = 0;
let scannedPorts = 0;
const totalPorts = PORT_END - PORT_START + 1;
const sendUpdate = (data: any) => {
controller.enqueue(encoder.encode(JSON.stringify(data) + '\n'));
};
sendUpdate({
type: 'progress',
message: `Scanning ${totalPorts.toLocaleString()} ports...`
});
const openPorts: number[] = [];
const ports = Array.from({ length: totalPorts }, (_, i) => PORT_START + i);
let idx = 0;
async function worker() {
while (idx < ports.length) {
const port = ports[idx++];
scannedPorts++;
if (scannedPorts % 1000 === 0) {
sendUpdate({
type: 'progress',
message: `Scanned ${scannedPorts.toLocaleString()}/${totalPorts.toLocaleString()} ports... Found ${foundPorts} HTTP servers`
});
}
if (await isHttpAlive(port)) {
openPorts.push(port);
foundPorts++;
// Get details for this port immediately
const portInfo = await getPortDetails(port);
const portHtml = makePortItemHtml(portInfo);
sendUpdate({
type: 'port',
html: portHtml
});
console.log(`Found HTTP server on port ${port}`);
}
}
}
const tasks: Promise<void>[] = [];
for (let i = 0; i < CONCURRENCY; i++) {
tasks.push(worker());
}
await Promise.all(tasks);
const message = foundPorts > 0
? `<span class="status">✅ Scan complete! Found ${foundPorts} HTTP server${foundPorts === 1 ? '' : 's'} out of ${totalPorts.toLocaleString()} ports checked.</span>`
: `<span style="color: #666;">ℹ️ Scan complete. No HTTP servers found on localhost ports ${PORT_START}-${PORT_END}.</span>`;
sendUpdate({
type: 'complete',
message: message
});
}
async function handleRequest(req: Request): Promise<Response> {
const url = new URL(req.url);
if (url.pathname === '/scan') {
const stream = new ReadableStream({
async start(controller) {
try {
await streamingScan(controller);
} catch (error) {
console.error("Scan error:", error);
const encoder = new TextEncoder();
controller.enqueue(encoder.encode(JSON.stringify({
type: 'complete',
message: '<span class="error">❌ Scan failed due to an error.</span>'
}) + '\n'));
} finally {
controller.close();
}
}
});
return new Response(stream, {
headers: {
"content-type": "text/plain; charset=utf-8",
"cache-control": "no-cache"
}
});
}
// Default GET request - return the base HTML
return new Response(getBaseHtml(), {
status: 200,
headers: { "content-type": "text/html; charset=utf-8" },
});
}
async function main() {
console.log(`🚀 Severfinder starting on http://localhost:${SERVE_PORT}`);
console.log("Visit the webpage to start scanning for HTTP servers!");
serve(handleRequest, { port: SERVE_PORT });
}
main();
@supersational
Copy link
Author

port-scanner
port-scanner.mov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment