From ca18ab3de65a10890d81312e2968a59692959d48 Mon Sep 17 00:00:00 2001 From: zefie Date: Wed, 30 Nov 2022 10:19:47 -0500 Subject: [PATCH] add https pc services support --- zefie_wtvp_minisrv/app.js | 82 +++++++++++++++---- .../ServiceDeps/https/selfsigned_cert.pem | 32 ++++++++ .../ServiceDeps/https/selfsigned_key.pem | 51 ++++++++++++ zefie_wtvp_minisrv/includes/classes/HTTPX.js | 44 ++++++++++ .../includes/classes/WTVShared.js | 8 ++ zefie_wtvp_minisrv/includes/config.json | 10 ++- zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj | 3 + 7 files changed, 212 insertions(+), 18 deletions(-) create mode 100644 zefie_wtvp_minisrv/includes/ServiceDeps/https/selfsigned_cert.pem create mode 100644 zefie_wtvp_minisrv/includes/ServiceDeps/https/selfsigned_key.pem create mode 100644 zefie_wtvp_minisrv/includes/classes/HTTPX.js diff --git a/zefie_wtvp_minisrv/app.js b/zefie_wtvp_minisrv/app.js index b6652083..e1f361c5 100644 --- a/zefie_wtvp_minisrv/app.js +++ b/zefie_wtvp_minisrv/app.js @@ -10,6 +10,7 @@ const tls = require('tls'); const zlib = require('zlib'); const http = require('follow-redirects').http const https = require('follow-redirects').https +const httpx = require(classPath + "/HTTPX.js"); const net = require('net'); const crypto = require('crypto') const CryptoJS = require('crypto-js'); @@ -40,7 +41,7 @@ function shutdown(signal = 'SIGTERM') { }; } -function findServiceByPort(port) { +function getServiceByPort(port) { var service_name = null; Object.keys(minisrv_config.services).forEach(function(k) { if (service_name) return; @@ -52,20 +53,37 @@ function findServiceByPort(port) { return service_name; } - function getPortByService(service) { if (minisrv_config.services[service]) return minisrv_config.services[service].port; else return null; } +function getSocketServer(socket) { + var server = null; + + if (socket._server) { + if (socket._server._connectionKey) server = socket._server; + } else if (socket._parent) { + if (socket._parent._server) { + if (socket._parent._server._connectionKey) server = socket._parent._server; + } + } + return server; +} + function getSocketDestinationPort(socket) { - return parseInt(socket._server._connectionKey.split(':')[2]); + return getServerDestinationPort(getSocketServer(socket)); +} + +function getServerDestinationPort(server) { + return parseInt(server._connectionKey.split(':')[2]); } function verifyServicePort(service_name, socket) { if (!minisrv_config.config.enable_port_isolation) return service_name; - if (socket._server._connectionKey) { - var socketPort = getSocketDestinationPort(socket); + var server = getSocketServer(socket); + if (server) { + var socketPort = getServerDestinationPort(server); if (minisrv_config.services[service_name]) { if (minisrv_config.services[service_name].port == socketPort) { if (minisrv_config.services[service_name].servicevault_dir) @@ -611,6 +629,7 @@ async function processURL(socket, request_headers, pc_services = false) { if (pc_services) { original_service_name = request_headers.service_name; // store PC Services service name service_name = verifyServicePort(request_headers.service_name, socket); // get the actual ServiceVault path + delete request_headers.service_name; } if (request_headers.request_url.indexOf('?') >= 0) { @@ -1068,7 +1087,7 @@ function headerStringToObj(headers, response = false) { return headers_obj; } -async function sendToClient(socket, headers_obj, data) { +async function sendToClient(socket, headers_obj, data = null) { var headers = ""; var content_length = 0; if (typeof (data) === 'undefined' || data === null) data = ''; @@ -1929,7 +1948,8 @@ if (minisrv_config.config.ServiceDeps) { var service_ip = minisrv_config.config.service_ip; Object.keys(minisrv_config.services).forEach(function (k) { if (configureService(k, minisrv_config.services[k], true)) { - console.log(" * Configured Service:", k, "on Port", minisrv_config.services[k].port, "- Service Host:", minisrv_config.services[k].host, "- Bind Port:", !minisrv_config.services[k].nobind, "- PC Services Mode:", (minisrv_config.services[k].pc_services) ? true : false); + var using_tls = (minisrv_config.services[k].pc_services && minisrv_config.services[k].https_cert && minisrv_config.services[k].use_https) ? true : false; + console.log(" * Configured Service:", k, "on Port", minisrv_config.services[k].port, "- Service Host:", minisrv_config.services[k].host + ((using_tls) ? " (TLS)" : ""), "- Bind Port:", !minisrv_config.services[k].nobind, "- PC Services Mode:", (minisrv_config.services[k].pc_services) ? true : false); if (minisrv_config.services[k].local_nntp_port) { if (!wtvnewsserver) { @@ -2022,25 +2042,55 @@ if (!minisrv_config.config.bind_ip) minisrv_config.config.bind_ip = "0.0.0.0"; pc_bind_ports.every(function (v) { try { var server = express(); - server.listen(v, minisrv_config.config.bind_ip); + var service_name = getServiceByPort(v); + var service_handler = http; + var server_opts = {}; + var using_tls = (minisrv_config.services[service_name].https_cert && minisrv_config.services[service_name].use_https) ? true : false; + + if (using_tls) { + service_handler = httpx; + server_opts = + { + key: fs.readFileSync(wtvshared.parseConfigVars(minisrv_config.services[service_name].https_cert.key)), + cert: fs.readFileSync(wtvshared.parseConfigVars(minisrv_config.services[service_name].https_cert.cert)), + }; + } + //service_handler.createServer(server_opts, server).listen(v, minisrv_config.config.bind_ip); + + if (using_tls && !minisrv_config.services[service_name].force_https) { + service_handler = httpx; // HTTP and HTTPS on the same port + } + + service_handler.createServer(server_opts, server).listen(v, minisrv_config.config.bind_ip); initstring_pc += v + ", "; + server.get('*', (req, res) => { - var request_headers = {}; + var ssl = (req.socket.ssl) ? true : false; + var service_name = getServiceByPort(v); + req.socket.minisrv_pc_mode = true; + req.socket.res = res; + req.socket.service_name = service_name; req.socket.id = parseInt(crc16('CCITT-FALSE', Buffer.from(String(req.socket.remoteAddress) + String(req.socket.remotePort), "utf8")).toString(16), 16); - socket_sessions[req.socket.id] = []; - var service_name = findServiceByPort(v); + socket_sessions[req.socket.id] = []; + + var request_headers = {}; request_headers['request'] = "GET " + req.originalUrl + " HTTP/1.1"; request_headers.request_url = req.originalUrl; Object.keys(req.headers).forEach(function (k) { request_headers[k] = req.headers[k]; }); request_headers.query = req.query; - if (minisrv_config.config.debug_flags.show_headers) console.log(" * Incoming PC Headers on", service_name, "socket ID", req.socket.id, wtvshared.filterRequestLog(request_headers)); request_headers.service_name = service_name; - req.socket.minisrv_pc_mode = true; - req.socket.res = res; - req.socket.service_name = service_name; - processURL(req.socket, request_headers, true) + + if (minisrv_config.config.debug_flags.show_headers) console.log(" * Incoming " + ((ssl) ? "HTTPS" : "HTTP") + " PC Headers on", service_name, "socket ID", req.socket.id, wtvshared.filterRequestLog(request_headers)); + if (!ssl && minisrv_config.services[service_name].force_https && minisrv_config.services[service_name].https_cert) { + var headers = `302 Moved +Location: https://${(minisrv_config.services[service_name].https_cert.domain) ? minisrv_config.services[service_name].https_cert.domain : minisrv_config.services[service_name].host}:${minisrv_config.services[service_name].port}/ +Content-type: text/html`; + sendToClient(req.socket, headers); + } else { + processURL(req.socket, request_headers, true) + } }) return true; } catch (e) { diff --git a/zefie_wtvp_minisrv/includes/ServiceDeps/https/selfsigned_cert.pem b/zefie_wtvp_minisrv/includes/ServiceDeps/https/selfsigned_cert.pem new file mode 100644 index 00000000..f4973008 --- /dev/null +++ b/zefie_wtvp_minisrv/includes/ServiceDeps/https/selfsigned_cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFgzCCA2sCFBKo/bZ2E7HHlOfsS4UrBJl1UgpTMA0GCSqGSIb3DQEBCwUAMH0x +CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazEXMBUGA1UECgwOWmVmaWUg +TmV0d29ya3MxJDAiBgNVBAsMG21pbmlzcnYgZGVmYXVsdCBjZXJ0aWZpY2F0ZTEc +MBoGA1UEAwwTbWluaXNydi5wY19zZXJ2aWNlczAgFw0yMjExMzAxNDE3MTVaGA8y +MTIyMTEwNjE0MTcxNVowfTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZb3Jr +MRcwFQYDVQQKDA5aZWZpZSBOZXR3b3JrczEkMCIGA1UECwwbbWluaXNydiBkZWZh +dWx0IGNlcnRpZmljYXRlMRwwGgYDVQQDDBNtaW5pc3J2LnBjX3NlcnZpY2VzMIIC +IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyOZ9iOYqr1cZLZKEWT8I5f7A +l3bd9wslCPKR9Mj3IwEy8KQjxwqJYl6jPvbNMtIgZo7Y9pFBovJEUIFFd4Ffshrm +QZZfDJtw+KvZF3m/GdmgRc147m++Ve6OSOvuSdtAOAiujhmwjHsTif+YTcjsOfAT +O9bhThIespeU4qDoYyDD+3A4wxwxCIFN7clSr7TpPS3uUQ7woCsi1nxdU8B5txT2 +KfXOlyL0uwygxEEUy0XrDdpHFt9sV0LWiy4Ho3wVf3dhv2odSEeh8SK04qWMga4A +fXEXWwg+4cuho8p24MuZZRsCrqM1O7Hm8wz9FiBJJF5US1jRLOf9sz/iaEz2Mz7C ++skG/3gsQ1cI+c/f4jwafYLwJs9XJ141nAxTHxAg2fjg0+Oqe54rs2v/8mBy6r6L +bLBlyNMZ4CFD/OhrE8tnP9WhX/l2DUmsQGq2OaL7i3Tjp2jWEwoY0MVcRYiJgA8L +5cbWnIWuR0WrDmiKwk1gLdGhvqLxYkouST68zr8Oy9TX2G2s9hF6IZbNN8/ZzMnm +y5KjM+DjSCjxym2F03obVGbzCUaqDUHNY84oddoF7zu8lzP8UVYwjvHOvCCQa2Az +30KZjF6YEDYxhY4KtxNDxNJ9EHBxXlyvYRo/XM/YOk0wdoZPGq4tsj4BZrXSfuch +AO0Pr8qDOt+134K1t5ECAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAToJKBCsCcBWB +mNdj320fu5emb7ZYDyK5wpCBFwMLl/kx/wfNUB6E3GZJdvFBr3zAOrUbwrXZ9+2T +MIP1S5uaG5eCmtLZyH0AjsJa70gUwyqk3VSHXErtT7tbLHTCu/Lqn8XeKiRqjKGi ++sI3L4KFzlDELFT8iqkR+MOC8cawKsO1LV/8T0XrRx7zfflQFheo+Zkkn3/EsjP9 +vwKX5+3u5jyLgYdUOVRoUHI2L9hp6ubcqqKTwyKg74PDKsUkg+dSt9QL+UO1FPT8 +hDlvKg2p7w3UawF675AgzyF6tsgCYBs7ThaNYWgjKZ9ALDvTPTypUCsNnSZ0lNaT +iU+dzEocMo2WesE5B1KH6WdAKD54VV4XHPPEzM2WNRTfs+JiTf9aNTJ2tatwvB5w +6glFkgICZ4TgHCADHE0h0aPmVEdzJ6bBA4tUTl6GqWUTVgj53LsdB3NcaEasg/7A +zzgPzsw6bZivrodyr2aXSV8VPIzoy6RjLNh4sujGBN4QU+v3o6X9me32WljSQzzx +5F3pg/okFauWjF8I6VPKVPW5ReUoRI7B/RkrHEqpCzpPltVV5cHmP+2V5Q6UJegC +FebNB1npEfOb/ozpxm4b3UhjrnmIO4NZZs8lvZy/naC+F4QOnMpSGfBjsdZuBlBv +o3nALXySQNoUsUhfkpOG/8O9K3HrSYk= +-----END CERTIFICATE----- diff --git a/zefie_wtvp_minisrv/includes/ServiceDeps/https/selfsigned_key.pem b/zefie_wtvp_minisrv/includes/ServiceDeps/https/selfsigned_key.pem new file mode 100644 index 00000000..256f3e5e --- /dev/null +++ b/zefie_wtvp_minisrv/includes/ServiceDeps/https/selfsigned_key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/zefie_wtvp_minisrv/includes/classes/HTTPX.js b/zefie_wtvp_minisrv/includes/classes/HTTPX.js new file mode 100644 index 00000000..7ef37a1c --- /dev/null +++ b/zefie_wtvp_minisrv/includes/classes/HTTPX.js @@ -0,0 +1,44 @@ +'use strict'; +let net = require('net'); +let http = require('http'); +let https = require('https'); + +exports.createServer = (opts, handler) => { + + let server = net.createServer(socket => { + socket.once('data', buffer => { + // Pause the socket + socket.pause(); + + // Determine if this is an HTTP(s) request + let byte = buffer[0]; + + let protocol; + if (byte === 22) { + protocol = 'https'; + } else if (32 < byte && byte < 127) { + protocol = 'http'; + } + + let proxy = server[protocol]; + if (proxy) { + // Push the buffer back onto the front of the data stream + socket.unshift(buffer); + + // Emit the socket to the HTTP(s) server + proxy.emit('connection', socket); + } + + // As of NodeJS 10.x the socket must be + // resumed asynchronously or the socket + // connection hangs, potentially crashing + // the process. Prior to NodeJS 10.x + // the socket may be resumed synchronously. + process.nextTick(() => socket.resume()); + }); + }); + + server.http = http.createServer(handler); + server.https = https.createServer(opts, handler); + return server; +}; \ No newline at end of file diff --git a/zefie_wtvp_minisrv/includes/classes/WTVShared.js b/zefie_wtvp_minisrv/includes/classes/WTVShared.js index 04328a06..5f0a6083 100644 --- a/zefie_wtvp_minisrv/includes/classes/WTVShared.js +++ b/zefie_wtvp_minisrv/includes/classes/WTVShared.js @@ -62,6 +62,14 @@ class WTVShared { else return out; } + parseConfigVars(s) { + if (s.indexOf("%ServiceDeps%") >= 0) { + return this.getServiceDep(s.replace("%ServiceDeps%", ""), true); + } else { + return s; + } + } + atob(a) { const CryptoJS = require('crypto-js'); const enc = CryptoJS.enc.Base64.parse(a); diff --git a/zefie_wtvp_minisrv/includes/config.json b/zefie_wtvp_minisrv/includes/config.json index 15492334..25a5c387 100644 --- a/zefie_wtvp_minisrv/includes/config.json +++ b/zefie_wtvp_minisrv/includes/config.json @@ -273,8 +273,14 @@ "drop_connection_on_wrong_port": false, // If true, resets connection if the PC browser connects to a port that is not PC Services enabled "show_verbose_errors": false, // extra debugging "privileged": true, - "allow_https": false, // for future use with LetsEncrypt - "force_https": false // for future use with LetsEncrypt + "use_https": false, + "force_https": false, + "https_cert": { + /* self-signed, can be replaced with another cert */ + // "domain": "mycooldomain.com", + "cert": "%ServiceDeps%/https/selfsigned_cert.pem", + "key": "%ServiceDeps%/https/selfsigned_key.pem" + } } }, "favorites": { diff --git a/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj b/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj index 1a008748..7841dc24 100644 --- a/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj +++ b/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj @@ -31,6 +31,9 @@ + + Code +