hypermc
f31c52a4f2
Isolated Tunnels per Request: Each HTTP request now generates a unique tunnel server instance. This avoids potential cross-request data confusion by ensuring that no tunnel is reused across multiple requests. Port Availability Check with getAvailablePort: Enhanced the getAvailablePort function to check if a randomly generated port is available before using it. The function now creates a temporary server on each port to ensure it’s free, retrying until an available port is found. This prevents EADDRINUSE errors caused by attempting to bind to a port already in use. Automatic Tunnel Closure: Each tunnel server (tunnelServer) is configured to close automatically after the corresponding request’s response is completed. The res.on('finish') event handler triggers tunnelServer.close() to release the resources immediately, preventing any chance of accidental reuse. Removed tunnels Object: Since each tunnel is now temporary and used for only one request, we removed the need to track tunnels in a tunnels object. This simplifies the code and helps ensure tunnels are created and destroyed within the scope of a single request.
126 lines
4.6 KiB
JavaScript
126 lines
4.6 KiB
JavaScript
const cluster = require('cluster');
|
|
const numCPUs = require('os').cpus().length;
|
|
const net = require('net');
|
|
|
|
if (cluster.isMaster) {
|
|
console.log(`Master ${process.pid} is running`);
|
|
console.log(`Total Workers ${numCPUs * 6}`);
|
|
for (let i = 0; i < numCPUs * 4; i++) {
|
|
cluster.fork();
|
|
}
|
|
|
|
cluster.on('exit', (worker, code, signal) => {
|
|
console.log(`Worker ${worker.process.pid} died`);
|
|
});
|
|
} else {
|
|
const fs = require('fs');
|
|
const http = require('http');
|
|
const httpProxy = require('http-proxy');
|
|
const HyperDHTServer = require('hyperdht');
|
|
const b32 = require("hi-base32");
|
|
const agent = new http.Agent({ maxSockets: Number.MAX_VALUE });
|
|
const content = fs.readFileSync('404.txt', 'utf8');
|
|
const DEBUG = 0;
|
|
const CONINFO = 0;
|
|
const dhtServer = new HyperDHTServer();
|
|
|
|
const startServer = async () => {
|
|
console.log(`Worker ${process.pid} started`);
|
|
await dhtServer.ready();
|
|
|
|
const proxy = httpProxy.createProxyServer({
|
|
ws: true,
|
|
agent: agent,
|
|
timeout: 360000
|
|
});
|
|
|
|
const server = http.createServer(async function (req, res) {
|
|
try {
|
|
const split = req.headers.host.split('.');
|
|
const publicKey = Buffer.from(b32.decode.asBytes(split[0].toUpperCase()));
|
|
|
|
if (publicKey.length < 32) {
|
|
console.log("Invalid Connection!");
|
|
res.writeHead(418, { 'Content-Type': 'text/html' });
|
|
res.end(content);
|
|
return;
|
|
}
|
|
|
|
// Create a unique tunnel for each request
|
|
const port = await getAvailablePort();
|
|
const tunnelServer = net.createServer(function (servsock) {
|
|
const socket = dhtServer.connect(publicKey);
|
|
let open = { local: true, remote: true };
|
|
|
|
servsock.on('data', (d) => socket.write(d));
|
|
socket.on('data', (d) => servsock.write(d));
|
|
|
|
const remoteend = () => {
|
|
if (open.remote) socket.end();
|
|
open.remote = false;
|
|
};
|
|
const localend = () => {
|
|
if (open.local) servsock.end();
|
|
open.local = false;
|
|
};
|
|
|
|
servsock.on('error', remoteend);
|
|
servsock.on('finish', remoteend);
|
|
servsock.on('end', remoteend);
|
|
socket.on('finish', localend);
|
|
socket.on('error', localend);
|
|
socket.on('end', localend);
|
|
});
|
|
|
|
tunnelServer.listen(port, "127.0.0.1", () => {
|
|
if (DEBUG === 1 || CONINFO === 1) console.log(`Tunnel server listening on port ${port}`);
|
|
|
|
proxy.web(req, res, {
|
|
target: 'http://127.0.0.1:' + port
|
|
}, function (e) {
|
|
console.log("Proxy Web Error: ", e);
|
|
res.writeHead(404, { 'Content-Type': 'text/html' });
|
|
res.end(content);
|
|
});
|
|
|
|
// Close tunnel and release resources after response is sent
|
|
res.on('finish', () => {
|
|
tunnelServer.close(() => {
|
|
if (DEBUG === 1 || CONINFO === 1) console.log("Tunnel closed after request completion.");
|
|
});
|
|
});
|
|
});
|
|
} catch (e) {
|
|
console.error("Error Occurred: ", e);
|
|
}
|
|
});
|
|
|
|
server.listen(8081, () => {
|
|
console.log(`Worker ${process.pid} listening on port 8081`);
|
|
});
|
|
};
|
|
|
|
startServer().catch(console.error);
|
|
|
|
async function getAvailablePort() {
|
|
return new Promise((resolve, reject) => {
|
|
const tryPort = () => {
|
|
const port = 1337 + Math.floor(Math.random() * 1000);
|
|
const tester = net.createServer()
|
|
.once('error', (err) => {
|
|
if (err.code === 'EADDRINUSE') {
|
|
tryPort(); // Port in use, try another
|
|
} else {
|
|
reject(err);
|
|
}
|
|
})
|
|
.once('listening', () => {
|
|
tester.close(() => resolve(port)); // Port is available
|
|
})
|
|
.listen(port, '127.0.0.1');
|
|
};
|
|
tryPort();
|
|
});
|
|
}
|
|
}
|