From b758e115139312f7519991c5838ed96f670166e4 Mon Sep 17 00:00:00 2001 From: Dorian Niemiec Date: Sun, 25 Aug 2024 17:04:49 +0200 Subject: [PATCH] Add secondary HTTP server and handlers for primary HTTP server --- src/handlers/noproxyHandler.js | 64 +++++++++++++ src/index.js | 170 ++++++++++++++++++++++++++++++++- 2 files changed, 230 insertions(+), 4 deletions(-) create mode 100644 src/handlers/noproxyHandler.js diff --git a/src/handlers/noproxyHandler.js b/src/handlers/noproxyHandler.js new file mode 100644 index 0000000..76324d3 --- /dev/null +++ b/src/handlers/noproxyHandler.js @@ -0,0 +1,64 @@ +const generateServerString = require("../utils/generateServerString"); +const svrjsInfo = require("../../svrjs.json"); +const { name } = svrjsInfo; + +let serverconsole = {}; + +function noproxyHandler(req, socket, head) { + let reqIdInt = Math.floor(Math.random() * 16777216); + if (reqIdInt == 16777216) reqIdInt = 0; + const reqId = + "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16); + + // SVR.JS log facilities + const logFacilities = { + climessage: (msg) => serverconsole.climessage(msg, reqId), + reqmessage: (msg) => serverconsole.reqmessage(msg, reqId), + resmessage: (msg) => serverconsole.resmessage(msg, reqId), + errmessage: (msg) => serverconsole.errmessage(msg, reqId), + locerrmessage: (msg) => serverconsole.locerrmessage(msg, reqId), + locwarnmessage: (msg) => serverconsole.locwarnmessage(msg, reqId), + locmessage: (msg) => serverconsole.locmessage(msg, reqId), + }; + + socket.on("close", (hasError) => { + if (!hasError) serverconsole.locmessage("Client disconnected."); + else serverconsole.locmessage("Client disconnected due to error."); + }); + socket.on("error", () => {}); + + // SVR.JS configuration object (modified) + const config = Object.assign(process.serverConfig); + + var reqip = socket.remoteAddress; + var reqport = socket.remotePort; + process.reqcounter++; + logFacilities.locmessage( + "Somebody connected to " + + (config.secure + ? (typeof config.sport == "number" ? "port " : "socket ") + config.sport + : (typeof config.port == "number" ? "port " : "socket ") + + config.port) + + "...", + ); + logFacilities.reqmessage( + "Client " + + (!reqip || reqip == "" + ? "[unknown client]" + : reqip + + (reqport && reqport !== 0 && reqport != "" ? ":" + reqport : "")) + + " wants to proxy " + + req.url + + " through this server", + ); + if (req.headers["user-agent"] != undefined) + logFacilities.reqmessage("Client uses " + req.headers["user-agent"]); + + logFacilities.errmessage("This server will never be a proxy."); + if (!socket.destroyed) socket.end("HTTP/1.1 501 Not Implemented\n\n"); +} + +module.exports = (serverconsoleO, middlewareO) => { + serverconsole = serverconsoleO; + return noproxyHandler; +}; diff --git a/src/index.js b/src/index.js index 0682033..3245a17 100644 --- a/src/index.js +++ b/src/index.js @@ -493,15 +493,177 @@ const proxyHandler = require("./handlers/proxyHandler.js")( middleware, ); +const noproxyHandler = require("./handlers/noproxyHandler.js")(serverconsole); + const clientErrorHandler = require("./handlers/clientErrorHandler.js")( serverconsole, ); +let server = {}; +let server2 = {}; + +// Create secondary HTTP server +try { + server2 = http.createServer({ + requireHostHeader: false, + }); +} catch (err) { + server2 = http.createServer(); +} + +// Add handlers to secondary HTTP server +server2.on("request", requestHandler); +server2.on("checkExpectation", requestHandler); +server2.on("clientError", clientErrorHandler); +server2.on("connect", process.serverConfig.disableToHTTPSRedirect ? proxyHandler : noproxyHandler); + // Create HTTP server -const server = http - .createServer(requestHandler) - .on("connect", proxyHandler) - .on("clientError", clientErrorHandler); +if (process.serverConfig.enableHTTP2 == true) { + if (process.serverConfig.secure) { + server = http2.createSecureServer({ + allowHTTP1: true, + requireHostHeader: false, + key: key, + cert: cert, + requestCert: configJSON.useClientCertificate, + rejectUnauthorized: configJSON.rejectUnauthorizedClientCertificates, + ciphers: configJSON.cipherSuite, + ecdhCurve: configJSON.ecdhCurve, + minVersion: configJSON.tlsMinVersion, + maxVersion: configJSON.tlsMaxVersion, + sigalgs: configJSON.signatureAlgorithms, + settings: configJSON.http2Settings, + }); + } else { + server = http2.createServer({ + allowHTTP1: true, + requireHostHeader: false, + settings: configJSON.http2Settings, + }); + } +} else { + if (process.serverConfig.secure) { + server = https.createServer({ + key: key, + cert: cert, + requireHostHeader: false, + requestCert: configJSON.useClientCertificate, + rejectUnauthorized: configJSON.rejectUnauthorizedClientCertificates, + ciphers: configJSON.cipherSuite, + ecdhCurve: configJSON.ecdhCurve, + minVersion: configJSON.tlsMinVersion, + maxVersion: configJSON.tlsMaxVersion, + sigalgs: configJSON.signatureAlgorithms, + }); + } else { + try { + server = http.createServer({ + requireHostHeader: false, + }); + } catch (err) { + server = http.createServer(); + } + } +} + +// TODO: SNI +//if (secure) { +// try { +// sniCredentials.forEach(function (sniCredentialsSingle) { +// server.addContext(sniCredentialsSingle.name, { +// cert: sniCredentialsSingle.cert, +// key: sniCredentialsSingle.key +// }); +// try { +// var snMatches = sniCredentialsSingle.name.match(/^([^:[]*|\[[^]]*\]?)((?::.*)?)$/); +// if (!snMatches[1][0].match(/^\.+$/)) snMatches[1][0] = snMatches[1][0].replace(/\.+$/, ""); +// server._contexts[server._contexts.length - 1][0] = new RegExp("^" + snMatches[1].replace(/([.^$+?\-\\[\]{}])/g, "\\$1").replace(/\*/g, "[^.:]*") + ((snMatches[1][0] == "[" || snMatches[1].match(/^(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$/)) ? "" : "\.?") + snMatches[2].replace(/([.^$+?\-\\[\]{}])/g, "\\$1").replace(/\*/g, "[^.]*") + "$", "i"); +// } catch (ex) { +// // Can't replace regex, ignoring... +// } +// }); +// } catch (err) { +// // SNI error +// } +//} + +// Add handlers to the server +server.on("request", requestHandler); +server.on("checkExpectation", requestHandler); +server.on("connect", proxyHandler); +server.on("clientError", clientErrorHandler); + +if (process.serverConfig.secure) { + server.prependListener("connection", function (sock) { + sock.reallyDestroy = sock.destroy; + sock.destroy = function () { + sock.toDestroy = true; + }; + }); + + server.prependListener("tlsClientError", function (err, sock) { + if ( + err.code == "ERR_SSL_HTTP_REQUEST" || + err.message.indexOf("http request") != -1 + ) { + sock._parent.destroy = sock._parent.reallyDestroy; + sock._readableState = sock._parent._readableState; + sock._writableState = sock._parent._writableState; + sock._parent.toDestroy = false; + sock.pipe = function (a, b, c) { + sock._parent.pipe(a, b, c); + }; + sock.write = function (a, b, c) { + sock._parent.write(a, b, c); + }; + sock.end = function (a, b, c) { + sock._parent.end(a, b, c); + }; + sock.destroyed = sock._parent.destroyed; + sock.readable = sock._parent.readable; + sock.writable = sock._parent.writable; + sock.remoteAddress = sock._parent.remoteAddress; + sock.remotePort = sock._parent.remoteAddress; + sock.destroy = function (a, b, c) { + try { + sock._parent.destroy(a, b, c); + sock.destroyed = sock._parent.destroyed; + } catch (err) { + // Socket is probably already destroyed. + } + }; + } else { + sock._parent.destroy = sock._parent.reallyDestroy; + try { + if (sock._parent.toDestroy) sock._parent.destroy(); + } catch (err) { + // Socket is probably already destroyed. + } + } + }); + + server.prependListener("secureConnection", function (sock) { + sock._parent.destroy = sock._parent.reallyDestroy; + delete sock._parent.reallyDestroy; + }); + + // TODO: OCSP stapling + /*if (process.serverConfig.enableOCSPStapling && !ocsp._errored) { + server.on("OCSPRequest", function (cert, issuer, callback) { + ocsp.getOCSPURI(cert, function (err, uri) { + if (err) return callback(err); + + var req = ocsp.request.generate(cert, issuer); + var options = { + url: uri, + ocsp: req.data + }; + + ocspCache.request(req.id, options, callback); + }); + }); + }*/ +} // TODO: close, open, stop, restart commands // Base commands