const fs = require('fs'); const http = require('http'); const httpProxy = require('http-proxy'); const HyperDHTServer = require('hyperdht'); const libNet = require('hyper-cmd-lib-net'); const goodbye = require('graceful-goodbye'); const libKeys = require('hyper-cmd-lib-keys'); const b32 = require("hi-base32"); const net = require("net"); const pump = require("pump"); let invalid = false; let mod = 0; const tunnels = {}; const agent = new http.Agent({ maxSockets: Number.MAX_VALUE }); const content = fs.readFileSync('404.txt', 'utf8'); const DEBUG = 0; // Set DEBUG to 1 to enable debug mode // Enabling DEBUG mode will slow down the code, please only enable this if you need // Information about connections coming in because you need to validate connection hashes. const startServer = async () => { console.log("Creating HyperDHT server..."); const dhtServer = new HyperDHTServer(); await dhtServer.ready(); console.log("HyperDHT server created and ready."); const proxy = httpProxy.createProxyServer({ ws: true, agent: agent, timeout: 360000 }); const server = http.createServer(async function (req, res) { try { console.log("Incoming HTTP request..."); const split = req.headers.host.split('.'); if (DEBUG === 1) console.log("Request Headers Host Split: ", split); const publicKey = Buffer.from(b32.decode.asBytes(split[0].toUpperCase())); if (DEBUG === 1) { console.log("Public Key Buffer: ", publicKey); console.log("Length: " + publicKey.length); } if (!(publicKey.length >= 32)) { console.log("Invalid Connection!") res.writeHead(404, { 'Content-Type': 'text/html' }); res.end(content); invalid = true } else { invalid = false } if (invalid == true) return if (!tunnels[publicKey]) { console.log("No tunnel found for public key, creating new tunnel..."); const port = 1337 + ((mod++) % 1000); const server = net.createServer(function (servsock) { console.log('Incoming Connection, Connecting to:', publicKey.toString('hex')); if (DEBUG === 1) console.log("Public Key Length: ", publicKey.toString('hex').length); const socket = dhtServer.connect(publicKey); const local = servsock; let open = { local: true, remote: true }; local.on('data', (d) => { if (DEBUG === 1) console.log("Local Data: ", d); socket.write(d); }); socket.on('data', (d) => { if (DEBUG === 1) console.log("Socket Data: ", d); local.write(d); }); const remoteend = (type) => { if (DEBUG === 1) console.log('Local Connection Ended, Ending Remote Connection'); if (open.remote) socket.end(); open.remote = false; }; const localend = (type) => { if (DEBUG === 1) console.log('Remote Connection Ended, Ending Local Connection'); if (open.local) local.end(); open.local = false; }; local.on('error', remoteend); local.on('finish', remoteend); local.on('end', remoteend); socket.on('finish', localend); socket.on('error', localend); socket.on('end', localend); }); server.listen(port, "127.0.0.1"); tunnels[publicKey] = port; console.log("New tunnel created. Public Key:", publicKey, "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); }); return; } else { console.log('Tunnel exists for public key:', publicKey); proxy.web(req, res, { target: 'http://127.0.0.1:' + tunnels[publicKey] }, function (e) { console.log("Proxy Web Error: ", e); res.writeHead(404, { 'Content-Type': 'text/html' }); res.end(content); }); } } catch (e) { console.error("Error Occurred: ", e); } }); server.on('upgrade', function (req, socket, head) { console.log('Upgrade Request'); const split = req.headers.host.split('.'); const publicKey = Buffer.from(b32.decode.asBytes(split[0].toUpperCase())); proxy.ws(req, socket, { target: 'http://127.0.0.1:' + tunnels[publicKey] }, function (e) { console.error("Proxy WS Error: ", e); socket.end(); }); }); server.listen(8081); console.log("HTTP server listening on port 8081"); process.on('SIGINT', function () { console.log("Shutting down..."); server.close(); dhtServer.destroy(); console.log("Shutdown complete."); process.exit(); }); }; startServer().catch(console.error);